+
diff --git a/pages/products/filter/catalog.tsx b/pages/products/filter/catalog.tsx
new file mode 100644
index 0000000..43745bc
--- /dev/null
+++ b/pages/products/filter/catalog.tsx
@@ -0,0 +1,150 @@
+"use client";
+import { useTranslations } from "next-intl";
+import { ChevronDown, ChevronUp } from "lucide-react";
+import { useCatalogHook } from "./useCataloghook";
+import { useCatalog } from "@/zustand/useCatalog";
+import { AnimatePresence, motion } from "framer-motion";
+
+export function CatalogSection() {
+ const t = useTranslations();
+ const setParentID = useCatalog((state) => state.setParentID);
+ const parentID = useCatalog((state) => state.parentID);
+ const {
+ catalogSection,
+ catalogsectionChild,
+ setParent,
+ childLoading,
+ openDropdowns,
+ setOpenDropdowns,
+ } = useCatalogHook();
+
+ return (
+
+ {/* ── Top-level categories ─────────────────────────────────────── */}
+
+ {catalogSection?.map((item: any) => (
+
+
{
+ setParent(item.id);
+ setOpenDropdowns((prev) => (prev === item.id ? null : item.id));
+ }}
+ className="flex items-center gap-2"
+ >
+
+ {item.name}
+
+
+ {item.children.length > 0 && (
+
+ {/*
+ * Single icon that rotates — replaces the ChevronUp/Down swap.
+ * Logic unchanged: openDropdowns === item.id still drives it.
+ */}
+
+
+ )}
+
+
+ ))}
+
+
+ {/* ── Sub-category dropdown — animated open/close ───────────────── */}
+ {/*
+ * AnimatePresence watches its children mount/unmount.
+ * The `key` on the motion.div is the open dropdown's id so that
+ * when you switch categories, the old panel exits and new one enters.
+ * All original conditional logic (catalogsectionChild, childLoading,
+ * .length > 0, t("subcategory_not_found")) is untouched.
+ */}
+
+ {catalogsectionChild && openDropdowns !== null && (
+
+ {childLoading ? (
+ // Loading skeleton — staggered pulse instead of plain text
+
+ {[1, 2, 3, 4].map((i) => (
+
+ ))}
+
+ ) : catalogsectionChild.length > 0 ? (
+
+ {catalogsectionChild.map((subItem: any) => (
+ setParentID(subItem.id)}
+ className="border-l px-3 my-2 hover:cursor-pointer hover:text-red-500 text-white flex items-center justify-center"
+ >
+
+ {subItem.name}
+
+
+ ))}
+
+ ) : (
+
+ {t("subcategory_not_found")}
+
+ )}
+
+ )}
+
+
+ );
+}
diff --git a/pages/products/filter/category.tsx b/pages/products/filter/category.tsx
new file mode 100644
index 0000000..0fcd263
--- /dev/null
+++ b/pages/products/filter/category.tsx
@@ -0,0 +1,87 @@
+"use client";
+import { useTranslations } from "next-intl";
+import { useCategoryHook } from "./useCategory";
+import { Check, ChevronDown, ChevronUp } from "lucide-react";
+
+export function Category() {
+ const t = useTranslations();
+ const {
+ categoryBack,
+ handleCategoryClick,
+ openDropdowns,
+ category,
+ subCategory,
+ handleSubCategoryClick,
+ subCategoryBack,
+ subCategoryLoading,
+ } = useCategoryHook();
+
+ return (
+
+ {categoryBack?.map((item: any) => (
+
+ {/* Main Category */}
+
handleCategoryClick(item)} className="">
+
+ {item.name}
+
+ {item.have_sub_category && (
+
+ {openDropdowns[item.id] ? (
+
+ ) : (
+
+ )}
+
+ )}
+
+
+ {/* ⭐ YANGI: SubCategory Dropdown */}
+ {item.have_sub_category && openDropdowns[item.id] && (
+
+ {subCategoryLoading ? (
+
Yuklanmoqda...
+ ) : subCategoryBack && subCategoryBack.length > 0 ? (
+ subCategoryBack.map((subItem: any) => (
+
handleSubCategoryClick(subItem)}
+ className="hover:cursor-pointer flex items-center gap-2 hover:bg-gray-600 p-1.5 rounded transition-colors"
+ >
+
+ {subCategory.id === subItem.id && (
+
+ )}
+
+
{subItem.name}
+
+ ))
+ ) : (
+
+ {t("subcategory_not_found")}
+
+ )}
+
+ )}
+
+ ))}
+
+ );
+}
diff --git a/pages/products/filter/filter.tsx b/pages/products/filter/filter.tsx
index e4d1af8..a3a827e 100644
--- a/pages/products/filter/filter.tsx
+++ b/pages/products/filter/filter.tsx
@@ -1,34 +1,11 @@
-import { CatalogItem } from "@/lib/types";
-import httpClient from "@/request/api";
-import { endPoints } from "@/request/links";
-import { useQuery } from "@tanstack/react-query";
-import { ChevronDown } from "lucide-react";
+import { Category } from "./category";
+import { CatalogSection } from "./catalog";
export default function Filter() {
- const { data } = useQuery({
- queryKey: ["catalogsection"],
- queryFn: () => httpClient(endPoints.filter.catalog),
- select: (res) => res?.data?.data?.results,
- });
-
- console.log("filter catalog: ", data);
return (
-
-
- {data?.map((item: CatalogItem) => (
-
-
{item.name}
- {item.children.length > 0 && (
-
-
-
- )}
-
- ))}
-
+
+
+
);
}
diff --git a/pages/products/filter/useCataloghook.ts b/pages/products/filter/useCataloghook.ts
new file mode 100644
index 0000000..c030a8b
--- /dev/null
+++ b/pages/products/filter/useCataloghook.ts
@@ -0,0 +1,34 @@
+"use client";
+import httpClient from "@/request/api";
+import { endPoints } from "@/request/links";
+import { useQuery } from "@tanstack/react-query";
+import { useState } from "react";
+
+export const useCatalogHook = () => {
+ const [openDropdowns, setOpenDropdowns] = useState
(0);
+ const [parent, setParent] = useState(0);
+ const { data: catalogsectionChild, isLoading: childLoading } = useQuery({
+ queryKey: ["catalogsection/", parent],
+ queryFn: () => httpClient(endPoints.filter.child({ id: parent || 0 })),
+ select: (data) => data?.data?.data?.results,
+ enabled: !!openDropdowns,
+ });
+
+ const { data: catalogSection } = useQuery({
+ queryKey: ["catalogsection"],
+ queryFn: () => httpClient(endPoints.filter.catalogSection),
+ select: (data) => data?.data?.data?.results,
+ });
+
+ console.log({ catalogSection });
+ console.log({ catalogsectionChild });
+
+ return {
+ catalogSection,
+ catalogsectionChild,
+ setParent,
+ openDropdowns,
+ setOpenDropdowns,
+ childLoading,
+ };
+};
diff --git a/pages/products/filter/useCategory.ts b/pages/products/filter/useCategory.ts
new file mode 100644
index 0000000..e5a673b
--- /dev/null
+++ b/pages/products/filter/useCategory.ts
@@ -0,0 +1,78 @@
+import httpClient from "@/request/api";
+import { endPoints } from "@/request/links";
+import { useCategory } from "@/zustand/useCategory";
+import { useSubCategory } from "@/zustand/useSubCategory";
+import { useQuery } from "@tanstack/react-query";
+import { useState } from "react";
+
+export const useCategoryHook = () => {
+ const [openDropdowns, setOpenDropdowns] = useState>(
+ {},
+ );
+
+ const category = useCategory((state) => state.category);
+ const setCategory = useCategory((state) => state.setCategory);
+ const subCategory = useSubCategory((state) => state.subCategory);
+ const setSubCategory = useSubCategory((state) => state.setSubCategory);
+ const clearSubCategory = useSubCategory((state) => state.clearSubCategory);
+
+ // Category data
+ const { data: categoryBack } = useQuery({
+ queryKey: ["category"],
+ queryFn: () => httpClient(endPoints.category.all),
+ select: (data) => data?.data?.results,
+ });
+
+ const { data: subCategoryBack, isLoading: subCategoryLoading } = useQuery({
+ queryKey: ["subCategory", category.id],
+ queryFn: () => httpClient(endPoints.subCategory.byId(category.id)),
+ enabled:
+ !!category.id &&
+ category.have_sub_category === true &&
+ openDropdowns[category.id] === true,
+ select: (data) => data?.data?.results,
+ });
+
+ const handleCategoryClick = (item: any) => {
+ if (item.have_sub_category) {
+ // Agar subCategory bo'lsa, dropdown ochish/yopish
+ setOpenDropdowns((prev) => ({
+ ...prev,
+ [item.id]: !prev[item.id],
+ }));
+
+ // Category'ni set qilish (filterlar yangilanishi uchun)
+ setCategory(item);
+
+ // SubCategory'ni tozalash (yangisini tanlash uchun)
+ if (!openDropdowns[item.id]) {
+ clearSubCategory();
+ }
+ } else {
+ // Agar subCategory bo'lmasa, to'g'ridan-to'g'ri category ni set qilish
+ setCategory(item);
+ clearSubCategory();
+
+ // Barcha dropdown'larni yopish
+ setOpenDropdowns({});
+ }
+ };
+
+ const handleSubCategoryClick = (item: any) => {
+ setSubCategory(item);
+ };
+
+ return {
+ category,
+ setCategory,
+ subCategory,
+ setSubCategory,
+ clearSubCategory,
+ categoryBack,
+ subCategoryBack,
+ subCategoryLoading,
+ handleCategoryClick,
+ handleSubCategoryClick,
+ openDropdowns,
+ };
+};
diff --git a/pages/products/product/mianProduct.tsx b/pages/products/product/mianProduct.tsx
index 24f4c20..f5cdbbf 100644
--- a/pages/products/product/mianProduct.tsx
+++ b/pages/products/product/mianProduct.tsx
@@ -10,6 +10,7 @@ import { useProductPageInfo } from "@/zustand/useProduct";
import { useSubCategory } from "@/zustand/useSubCategory";
import { useTranslations } from "next-intl";
import PaginationLite from "@/components/paginationUI";
+import { useCatalog } from "@/zustand/useCatalog";
export default function MainProduct() {
const t = useTranslations();
@@ -18,6 +19,7 @@ export default function MainProduct() {
const filter = useFilter((s) => s.filter);
const getFiltersByType = useFilter((s) => s.getFiltersByType);
const setProduct = useProductPageInfo((s) => s.setProducts);
+ const parentID = useCatalog((state) => state.parentID);
const [currentPage, setCurrentPage] = useState(1);
@@ -34,7 +36,9 @@ export default function MainProduct() {
const requestLink = useMemo(() => {
const baseLink = category.have_sub_category
? endPoints.product.bySubCategory({ id: subCategory.id, currentPage })
- : endPoints.product.byCategory({ id: category.id, currentPage });
+ : parentID
+ ? endPoints.product.byCatalogSection({ id: parentID, currentPage })
+ : endPoints.product.byCategory({ id: category.id, currentPage });
return `${baseLink}${queryParams}`;
}, [
category.id,
diff --git a/pages/products/product/products.tsx b/pages/products/product/products.tsx
index 84ce45f..86702ad 100644
--- a/pages/products/product/products.tsx
+++ b/pages/products/product/products.tsx
@@ -5,7 +5,7 @@ export function Products() {
return (
-
+
{/* filter part */}
diff --git a/request/links.ts b/request/links.ts
index 80ab424..7d195c1 100644
--- a/request/links.ts
+++ b/request/links.ts
@@ -29,6 +29,13 @@ export const endPoints = {
return link;
},
+ byCatalogSection: ({ id, currentPage }: ProductTypes) => {
+ let link = "product";
+ if (id) link += `?catalog_section=${id}`;
+ if (currentPage) link += `&page=${currentPage}`;
+
+ return link;
+ },
detail: (id: number) => `product/${id}/`,
},
faq: "faq/",
@@ -41,9 +48,18 @@ export const endPoints = {
normative: "document/?type=normative",
guides: "guide/",
filter: {
- catalog: "catalogsection/?page_size=500",
- child: (parenId: number) =>
- `catalogsection/?page_size=500&parent=${parenId}`,
+ size: "size/",
+ sizePageItems: "size/?page_size=500",
+ sizeCategoryId: (id: number) => `size/?category=${id}&page_size=500`,
+ catalog: "catalog/",
+ catalogPageItems: "catalog/?page_size=500",
+ catalogCategoryId: (id: number) => `catalog/?category=${id}&page_size=500`,
+ child: ({ id }: { id?: number }) => {
+ const link = "catalogsection/?page_size=500";
+ if (id) return `${link}&parent=${id}`;
+ return link;
+ },
+ catalogSection: "catalogsection/?page_size=500",
},
post: {
sendNumber: "callBack/",
diff --git a/zustand/useCatalog.ts b/zustand/useCatalog.ts
new file mode 100644
index 0000000..cc84c6b
--- /dev/null
+++ b/zustand/useCatalog.ts
@@ -0,0 +1,13 @@
+import { create } from "zustand";
+
+type CatalogType = {
+ parentID: number;
+ setParentID: (id: number) => void;
+};
+
+export const useCatalog = create((set) => {
+ return {
+ parentID: 0,
+ setParentID: (id: number) => set({ parentID: id }),
+ };
+});