diff --git a/src/i18n/locales/en.js b/src/i18n/locales/en.js
index 5818676..a167e25 100644
--- a/src/i18n/locales/en.js
+++ b/src/i18n/locales/en.js
@@ -5,6 +5,7 @@ export default {
login: "Login",
signUp: "Sign Up",
brands: "Brands",
+ stores: "Stores",
search: "Search by product name...",
cart: "Cart",
home: "Home",
diff --git a/src/i18n/locales/ru.js b/src/i18n/locales/ru.js
index 4c45e24..eeec789 100644
--- a/src/i18n/locales/ru.js
+++ b/src/i18n/locales/ru.js
@@ -5,6 +5,7 @@ export default {
login: "Войти",
signUp: "Регистрация",
brands: "Бренды",
+ stores: "Магазины",
search: "Поиск по названию товара...",
cart: "Корзина",
home: "Главная",
diff --git a/src/i18n/locales/tm.js b/src/i18n/locales/tm.js
index ef26fae..b04d246 100644
--- a/src/i18n/locales/tm.js
+++ b/src/i18n/locales/tm.js
@@ -5,6 +5,7 @@ export default {
login: "Giriş",
signUp: "Agza bolmak",
brands: "Brendler",
+ stores: "Dükanlar",
search: "Haryt ady boýunça gözleg...",
cart: "Sebet",
home: "Baş sahypa",
diff --git a/src/pages/Category/index.jsx b/src/pages/Category/index.jsx
index ad1d3ce..55e2b69 100644
--- a/src/pages/Category/index.jsx
+++ b/src/pages/Category/index.jsx
@@ -138,7 +138,7 @@ const CategoryPage = () => {
return;
}
- if (prevRouteRef.current === routeKey) return;
+ if (prevRouteRef.current === routeKey && !location.state?.clearFilters) return;
prevRouteRef.current = routeKey;
@@ -159,8 +159,10 @@ const CategoryPage = () => {
setTimeout(() => window.scrollTo(0, savedScroll), 100);
}
} else {
- setAllProducts([]);
- setHasMore(true);
+ if (prevRouteRef.current !== routeKey) {
+ setAllProducts([]);
+ setHasMore(true);
+ }
setPageState({
currentPage: 1,
minPrice: "",
diff --git a/src/pages/ProductDetail/index.jsx b/src/pages/ProductDetail/index.jsx
index 1ff62cc..1593692 100644
--- a/src/pages/ProductDetail/index.jsx
+++ b/src/pages/ProductDetail/index.jsx
@@ -367,7 +367,7 @@ const ProductPage = ({
{product.channel?.[0]?.name && (
-
+
{t("order.channel")}
{product.channel[0].name}
diff --git a/src/pages/Stores/Stores.module.scss b/src/pages/Stores/Stores.module.scss
new file mode 100644
index 0000000..d7567f7
--- /dev/null
+++ b/src/pages/Stores/Stores.module.scss
@@ -0,0 +1,143 @@
+.storesContainer {
+ padding: 1rem 4.4rem;
+ max-width: 1336px;
+ margin: 0 auto;
+ @media screen and (max-width: 1023px) {
+ padding: 1rem 1.25rem;
+ }
+}
+
+.searchWrapper {
+ margin-bottom: 24px;
+ display: flex;
+ align-items: center;
+ height: 40px;
+ svg {
+ position: absolute;
+ width: 24px;
+ height: 24px;
+ transform: translateX(35%);
+ color: #9ca3af;
+ }
+
+ input {
+ width: 46%;
+ height: 38px;
+ border: 1px solid #e5e7eb;
+ border-radius: 8px;
+ font-size: 14px;
+ padding-left: 40px;
+ outline: none;
+ @media screen and (max-width: 1023px) {
+ width: 100%;
+ }
+
+ &::placeholder {
+ color: #9ca3af;
+ }
+ }
+}
+
+.categorySection {
+ margin-bottom: 40px;
+
+ h2 {
+ font-size: 24px;
+ font-weight: 600;
+ margin-bottom: 20px;
+ color: #111827;
+ cursor: pointer;
+ &:hover {
+ color: #aaaaaa;
+ }
+ }
+}
+
+.storesGrid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(180px, 180px));
+ gap: 16px;
+ @media screen and (max-width: 1023px) {
+ grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+ gap: 10px;
+ }
+ @media screen and (max-width: 900px) {
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
+ gap: 5px;
+ }
+ @media screen and (max-width: 798px) {
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
+ gap: 5px;
+ }
+}
+
+.storeCard {
+ background: white;
+ border-radius: 8px;
+ padding: 16px;
+ text-align: center;
+ transition: all 0.2s ease;
+ border: 1px solid #e5e7eb;
+ cursor: pointer;
+ @media screen and (max-width: 900px) {
+ padding: 5px;
+ }
+
+ &:hover {
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
+ transform: translateY(-2px);
+ }
+
+ .imageWrapper {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 12px;
+ position: relative;
+ aspect-ratio: 4/3;
+
+ img {
+ height: 100%;
+ object-fit:contain;
+ }
+ }
+
+ .placeholder {
+ width: 80px;
+ height: 80px;
+ background: #f3f4f6;
+ border-radius: 8px;
+ }
+
+ .storeName {
+ font-size: 16px;
+ color: #374151;
+ margin: 0;
+ }
+}
+
+.skeleton {
+ background: linear-gradient(90deg, #f3f4f6 25%, #e5e7eb 50%, #f3f4f6 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+}
+
+@keyframes shimmer {
+ 0% {
+ background-position: 200% 0;
+ }
+ 100% {
+ background-position: -200% 0;
+ }
+}
+
+
+.logoFallback {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 120px;
+ height: 100%;
+ border-radius: 4px;
+ object-fit: contain;
+}
diff --git a/src/pages/Stores/index.jsx b/src/pages/Stores/index.jsx
new file mode 100644
index 0000000..31f79ab
--- /dev/null
+++ b/src/pages/Stores/index.jsx
@@ -0,0 +1,202 @@
+import React, { useState, useEffect } from "react";
+import { useTranslation } from "react-i18next";
+import InfiniteScroll from "react-infinite-scroll-component";
+import { useLazyGetChannelsQuery } from "../../app/api/channelsApi";
+import styles from "./Stores.module.scss";
+import { CiSearch } from "react-icons/ci";
+import { useNavigate } from "react-router-dom";
+import { Logo } from "../../components/Icons";
+import Loader from "../../components/Loader/index";
+import { Result, Button } from "antd";
+
+const StoresPage = () => {
+ const { t } = useTranslation();
+ const [searchTerm, setSearchTerm] = useState("");
+ const navigate = useNavigate();
+
+ // Stores data state
+ const [allStores, setAllStores] = useState([]);
+ const [visibleStores, setVisibleStores] = useState([]);
+ const [page, setPage] = useState(1);
+ const [hasMore, setHasMore] = useState(true);
+ const itemsPerPage = 24;
+
+ // Use lazy query to have more control over when to fetch
+ const [getChannels, { data: channelsData, isLoading, isFetching, error }] =
+ useLazyGetChannelsQuery();
+
+ // Initial fetch on component mount
+ useEffect(() => {
+ getChannels({ page: 1, limit: itemsPerPage });
+ }, [getChannels]);
+
+ // Process stores data when it arrives
+ useEffect(() => {
+ if (channelsData) {
+ const stores = channelsData.data || [];
+ const pagination = channelsData.pagination || {};
+
+ console.log("Stores Data Received:", {
+ count: stores.length,
+ page,
+ pagination
+ });
+
+ setAllStores((prev) => {
+ const existingIds = new Set(prev.map((store) => store.id));
+ const newStores = stores.filter(
+ (store) => !existingIds.has(store.id)
+ );
+ return [...prev, ...newStores];
+ });
+
+ // More robust hasMore logic
+ const hasNext = pagination.next_page_url ||
+ (pagination.current_page && pagination.last_page && pagination.current_page < pagination.last_page) ||
+ (stores.length === itemsPerPage);
+
+ setHasMore(!!hasNext);
+ }
+ }, [channelsData, page, itemsPerPage]);
+
+ // Process stores for display whenever all stores or search term changes
+ useEffect(() => {
+ if (allStores.length > 0) {
+ const filteredStores = searchTerm
+ ? allStores.filter((store) =>
+ store.name.toLowerCase().includes(searchTerm.toLowerCase())
+ )
+ : allStores;
+
+ // Grouping logic (similar to Brands, but defaults to "Stores")
+ const groupedStores = filteredStores.reduce((acc, store) => {
+ const type = store.type || "Stores";
+ if (!acc[type]) {
+ acc[type] = [];
+ }
+ acc[type].push(store);
+ return acc;
+ }, {});
+
+ const displayGroups = Object.entries(groupedStores)
+ .map(([type, stores]) => ({
+ title: type === "Stores" ? t("navbar.stores") : type.charAt(0).toUpperCase() + type.slice(1),
+ stores,
+ }))
+ .filter((group) => group.stores.length > 0);
+
+ setVisibleStores(displayGroups);
+ if (searchTerm) {
+ setHasMore(false);
+ }
+ }
+ }, [allStores, searchTerm, t]);
+
+ const loadMoreStores = () => {
+ if (!searchTerm && !isFetching && hasMore) {
+ const nextPage = page + 1;
+ getChannels({ page: nextPage, limit: itemsPerPage });
+ setPage(nextPage);
+ }
+ };
+
+ const handleSearch = (e) => {
+ const value = e.target.value;
+ setSearchTerm(value);
+ setPage(1);
+ setAllStores([]);
+ setHasMore(true);
+ getChannels({ page: 1, limit: itemsPerPage, search: value });
+ };
+
+ const handleStoreClick = (storeId) => {
+ navigate(`/channel/${storeId}`);
+ };
+
+ if (isLoading && page === 1) return ;
+ if (error)
+ return (
+ navigate("/")}>
+ {t("common.back_to_home") || "Baş sahypa gidiň"}
+
+ }
+ />
+ );
+
+ return (
+ }
+ >
+ {visibleStores.map((group, index) => (
+
+ {group.title}
+
+
+ {group.stores.map((store) => (
+
handleStoreClick(store.id)}
+ >
+
+ {store.media?.[0]?.thumbnail ||
+ store.media?.[0]?.images_800x800 ||
+ store.logo ? (
+

{
+ e.target.style.display = "none";
+ e.target.nextSibling.style.display = "flex";
+ }}
+ />
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+
{store.name}
+
+ ))}
+
+
+ ))}
+
+
+ );
+};
+
+export default StoresPage;
diff --git a/src/routes.jsx b/src/routes.jsx
index 3fa0307..bf09bf6 100644
--- a/src/routes.jsx
+++ b/src/routes.jsx
@@ -21,6 +21,7 @@ const DeliveryTerms = lazy(() => import("./pages/DeliveryTerms/index.jsx"));
const AboutUs = lazy(() => import("./pages/AboutUs/index.jsx"));
const PrivacyPolicy = lazy(() => import("./pages/PrivacyPolicy/index.jsx"));
const AdminPage = lazy(() => import("./pages/CarconfiguratorAdmin/index.jsx"));
+const StoresPage = lazy(() => import("./pages/Stores/index.jsx"));
export default function Router() {
const routes = useRoutes([
@@ -34,6 +35,7 @@ export default function Router() {
children: [
{ path: "/", element: