From 873bbb82a91e0d70048d82379c76cafebd8c59da Mon Sep 17 00:00:00 2001 From: "nabijonovdavronbek619@gmail.com" Date: Fri, 6 Feb 2026 21:47:56 +0500 Subject: [PATCH] get products by filter connected --- components/pages/products/filter/filter.tsx | 144 ++++-------------- .../pages/products/filter/filterInfo.tsx | 4 +- .../pages/products/product/mianProduct.tsx | 33 +++- lib/demoData.ts | 114 ++++++++++++++ lib/filter-zustand.ts | 89 +++++++---- package.json | 1 + pnpm-lock.yaml | 13 +- request/links.ts | 2 + 8 files changed, 253 insertions(+), 147 deletions(-) diff --git a/components/pages/products/filter/filter.tsx b/components/pages/products/filter/filter.tsx index 1aad318..1988f2d 100644 --- a/components/pages/products/filter/filter.tsx +++ b/components/pages/products/filter/filter.tsx @@ -1,4 +1,5 @@ "use client"; +import { result } from "@/lib/demoData"; import { useFilter } from "@/lib/filter-zustand"; import httpClient from "@/request/api"; import { endPoints } from "@/request/links"; @@ -18,125 +19,43 @@ export default function Filter() { const [dataExpanded, setDataExpanded] = useState(false); const [numberExpanded, setNumberExpanded] = useState(false); - const sectionData = [ - "SLT-Aqua", - "Вварное седло", - "Кран шаровый", - "Муфты", - "Муфты комбинированные", - "Муфты переходные", - "Тройник комбинированный", - "Тройники", - "Трубы SDR 6", - "Трубы SDR 7,4", - "Угол 45", - "Угол 90", - "Угольник комбинированный", - "Фланцы+бурты", - ]; - - const sectionNumber = [ - "25", - '25х1/2"', - '25х3/4"', - "32", - "32x25x25", - "32x25x32", - '32х1"', - '32х1/2"', - "32х25", - '32х3/4"', - "40", - "40x25x40", - "40x32x40", - '40х1 1/4"', - '40х1 3/4"', - '40х1/2"', - "40х25", - "40х32", - "50", - "50x25x50", - "50x32x50", - "50x40x50", - '50х1 1/2"', - '50х1/2"', - "50х25", - "50х32", - "50х40", - "63", - "63x25x63", - "63x32x63", - "63x40x63", - "63x50x63", - '63х1/2"', - '63х2"', - "63х25", - "63х32", - "63х40", - "63х50", - "75", - "75x25x75", - "75x32x75", - "75x40x75", - "75x50x75", - "75x63x75", - '75х1/2"', - "75х32", - "75х40", - "75х50", - "75х63", - "90", - "90x40x90", - "90x50x90", - "90x63x90", - "90x75x90", - '90х1/2"', - "90х32", - "90х40", - "90х50", - "90х63", - "90х75", - "110", - "110x50x110", - "110x63x110", - "110x75x110", - "110x90x110", - '110х1/2"', - "110х25", - "110х32", - "110х40", - "110х50", - "110х63", - "110х75", - "110х90", - "125", - "160", - ]; - - const [catalogData, setCatalogData] = useState(sectionData); - const [sizeData, setSizeData] = useState(sectionNumber); + const [catalogData, setCatalogData] = useState< + { id: number; name: string; type: string }[] + >(result[0].items); + const [sizeData, setSizeData] = useState< + { id: number; name: string; type: string }[] + >(result[1].items); const { data: catalog } = useQuery({ queryKey: ["catalog"], - queryFn: () => httpClient(endPoints.filter.catalogPageItems), + queryFn: () => httpClient(endPoints.filter.catalogCategoryId(category.id)), select: (data) => { - const catalog = data?.data?.results; - return catalog.map((item: any) => item.name) || []; + const catalogData = data?.data?.results; + return catalogData.map((item: any) => ({ + id: item.id, + name: item.name, + type: "catalog", + })); }, }); const { data: size } = useQuery({ queryKey: ["size"], - queryFn: () => httpClient(endPoints.filter.sizePageItems), + queryFn: () => httpClient(endPoints.filter.sizeCategoryId(category.id)), select: (data) => { - const size = data?.data?.results; - return size.map((item: any) => item.name) || []; + const sizedata = data?.data?.results; + return sizedata.map((item: any) => ({ + id: item.id, + name: item.name, + type: "size", + })); }, }); useEffect(() => { catalog && setCatalogData(catalog); size && setSizeData(size); + console.log("catalog: ", catalog, "size: ", size); }, [size, catalog]); // Bo'lim uchun ko'rsatiladigan itemlar @@ -150,9 +69,10 @@ export default function Filter() { : sizeData.slice(0, 10); console.log("have suncategory: ", category.have_sub_category); - if (category.have_sub_category || subCategory.id!==0) { + if (category.have_sub_category || subCategory.id !== 0) { return null; } + console.log("filter: ",filter); return (
@@ -164,23 +84,23 @@ export default function Filter() {
{visibleSectionData.map((item) => (
toggleFilter(item)} className="hover:cursor-pointer flex items-center gap-2" > - {hasData(item) && ( + {hasData(item.name) && ( )} -

{item}

+

{item.name}

))}
@@ -200,23 +120,23 @@ export default function Filter() {
{visibleSectionNumber.map((item) => (
toggleFilter(item)} className="hover:cursor-pointer flex items-center gap-2" > - {hasData(item) && ( + {hasData(item.name) && ( )} -

{item}

+

{item.name}

))}
diff --git a/components/pages/products/filter/filterInfo.tsx b/components/pages/products/filter/filterInfo.tsx index 8dd3202..4215df9 100644 --- a/components/pages/products/filter/filterInfo.tsx +++ b/components/pages/products/filter/filterInfo.tsx @@ -17,13 +17,13 @@ export default function FilterInfo() { {filtered && filtered.map((item) => (
- {item} + {item.name}
))}
diff --git a/components/pages/products/product/mianProduct.tsx b/components/pages/products/product/mianProduct.tsx index 78d17bd..c3b2f9e 100644 --- a/components/pages/products/product/mianProduct.tsx +++ b/components/pages/products/product/mianProduct.tsx @@ -4,16 +4,41 @@ import { endPoints } from "@/request/links"; import { useQuery } from "@tanstack/react-query"; import ProductCard from "./productCard"; import { useCategory } from "@/store/useCategory"; +import { useFilter } from "@/lib/filter-zustand"; +import { useMemo } from "react"; export default function MainProduct() { const category = useCategory((state) => state.category); + const filter = useFilter((state) => state.filter); + const getFiltersByType = useFilter((state)=>state.getFiltersByType) - const requestLink = category.have_sub_category - ? endPoints.subCategory.byId(category.id) - : endPoints.product.byCategory(category.id || 0); + // Query params yaratish + const queryParams = useMemo(() => { + const catalog = getFiltersByType("catalog"); + const size = getFiltersByType("size"); + + // Har bir filter uchun query string yaratish + const catalogParams = catalog.map((item) => `catalog=${item.id}`).join("&"); + const sizeParams = size.map((item) => `size=${item.id}`).join("&"); + + // Barcha paramslarni birlashtirish + const allParams = [catalogParams, sizeParams].filter(Boolean).join("&"); + + return allParams ? `&${allParams}` : ""; + }, [filter, getFiltersByType]); + + // Request link yaratish + const requestLink = useMemo(() => { + const baseLink = category.have_sub_category + ? endPoints.subCategory.byId(category.id) + : endPoints.product.byCategory(category.id || 0); + + // Query params qo'shish + return `${baseLink}${queryParams}`; + }, [category.id, category.have_sub_category, queryParams]); const { data, isLoading, error } = useQuery({ - queryKey: ["products", category.id], + queryKey: ["products", category.id , queryParams], queryFn: () => httpClient(requestLink), select: (data) => data?.data?.data?.results, }); diff --git a/lib/demoData.ts b/lib/demoData.ts index 97c2da4..d7a662f 100644 --- a/lib/demoData.ts +++ b/lib/demoData.ts @@ -124,3 +124,117 @@ export const lede = [ image: "/images/products/lede/troynik_perexadnoy-removebg-preview.png", }, ]; + +const sectionData = [ + "SLT-Aqua", + "Вварное седло", + "Кран шаровый", + "Муфты", + "Муфты комбинированные", + "Муфты переходные", + "Тройник комбинированный", + "Тройники", + "Трубы SDR 6", + "Трубы SDR 7,4", + "Угол 45", + "Угол 90", + "Угольник комбинированный", + "Фланцы+бурты", +]; + +const sectionNumber = [ + "25", + '25х1/2"', + '25х3/4"', + "32", + "32x25x25", + "32x25x32", + '32х1"', + '32х1/2"', + "32х25", + '32х3/4"', + "40", + "40x25x40", + "40x32x40", + '40х1 1/4"', + '40х1 3/4"', + '40х1/2"', + "40х25", + "40х32", + "50", + "50x25x50", + "50x32x50", + "50x40x50", + '50х1 1/2"', + '50х1/2"', + "50х25", + "50х32", + "50х40", + "63", + "63x25x63", + "63x32x63", + "63x40x63", + "63x50x63", + '63х1/2"', + '63х2"', + "63х25", + "63х32", + "63х40", + "63х50", + "75", + "75x25x75", + "75x32x75", + "75x40x75", + "75x50x75", + "75x63x75", + '75х1/2"', + "75х32", + "75х40", + "75х50", + "75х63", + "90", + "90x40x90", + "90x50x90", + "90x63x90", + "90x75x90", + '90х1/2"', + "90х32", + "90х40", + "90х50", + "90х63", + "90х75", + "110", + "110x50x110", + "110x63x110", + "110x75x110", + "110x90x110", + '110х1/2"', + "110х25", + "110х32", + "110х40", + "110х50", + "110х63", + "110х75", + "110х90", + "125", + "160", +]; + +export const result = [ + { + type: "section", + items: sectionData.map((name, index) => ({ + id: index + 1, + name, + type: "catalog", + })), + }, + { + type: "size", + items: sectionNumber.map((name, index) => ({ + id: index + 1, + name, + type: "size", + })), + }, +]; diff --git a/lib/filter-zustand.ts b/lib/filter-zustand.ts index 8bc38b5..0617f39 100644 --- a/lib/filter-zustand.ts +++ b/lib/filter-zustand.ts @@ -1,33 +1,68 @@ import { create } from "zustand"; +import { persist } from "zustand/middleware"; +import { immer } from "zustand/middleware/immer"; -interface FilterZustandTypes { - filter: string[]; - removeFilter: (data: string) => void; - toggleFilter: (data: string) => void; - resetFilter: () => void; - hasFilter: (data: string) => boolean; +export interface FilterItem { + id: number; + name: string; + type: string; } -export const useFilter = create((set, get) => ({ - filter: [], +interface FilterZustandTypes { + filter: FilterItem[]; + removeFilter: (id: number) => void; + toggleFilter: (item: FilterItem) => void; + resetFilter: () => void; + hasFilter: (name: string) => boolean; + getFiltersByType: (type: string) => FilterItem[]; // ✅ Qo'shimcha + removeFiltersByType: (type: string) => void; // ✅ Qo'shimcha +} - removeFilter: (data) => - set((state) => ({ - filter: state.filter.filter((item) => item !== data), +export const useFilter = create()( + persist( + immer((set, get) => ({ + filter: [], + + removeFilter: (id) => + set((state) => { + state.filter = state.filter.filter((item:FilterItem) => item.id !== id); + }), + + toggleFilter: (item) => + set((state) => { + const index = state.filter.findIndex((f:FilterItem) => f.name === item.name); + if (index !== -1) { + // Agar bor bo'lsa o'chirish + state.filter.splice(index, 1); + } else { + // Agar yo'q bo'lsa qo'shish + state.filter.push(item); + } + }), + + resetFilter: () => + set((state) => { + state.filter = []; + }), + + hasFilter: (name) => { + return get().filter.some((item) => item.name === name); + }, + + // ✅ Type bo'yicha filterlarni olish + getFiltersByType: (type) => { + return get().filter.filter((item) => item.type === type); + }, + + // ✅ Type bo'yicha filterlarni o'chirish + removeFiltersByType: (type) => + set((state) => { + state.filter = state.filter.filter((item:FilterItem) => item.type !== type); + }), })), - - // Toggle: mavjud bo'lsa o'chirish, yo'q bo'lsa qo'shish - toggleFilter: (data) => - set((state) => { - if (state.filter.includes(data)) { - return { filter: state.filter.filter((item) => item !== data) }; - } - return { filter: [...state.filter, data] }; - }), - - resetFilter: () => set({ filter: [] }), - - hasFilter: (data) => { - return get().filter.includes(data); - }, -})); \ No newline at end of file + { + name: "filter-storage", // localStorage key nomi + partialize: (state) => ({ filter: state.filter }), // Faqat filter'ni saqlash + } + ) +); \ No newline at end of file diff --git a/package.json b/package.json index 54c9aa2..49cf250 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,7 @@ "date-fns": "4.1.0", "embla-carousel-react": "8.5.1", "framer-motion": "^12.29.2", + "immer": "^11.1.3", "input-otp": "1.4.1", "lucide-react": "^0.454.0", "negotiator": "^1.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 34efeec..65b9b39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -125,6 +125,9 @@ importers: framer-motion: specifier: ^12.29.2 version: 12.29.2(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + immer: + specifier: ^11.1.3 + version: 11.1.3 input-otp: specifier: 1.4.1 version: 1.4.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0) @@ -187,7 +190,7 @@ importers: version: 3.25.76 zustand: specifier: ^5.0.10 - version: 5.0.10(@types/react@19.2.9)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)) + version: 5.0.10(@types/react@19.2.9)(immer@11.1.3)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)) devDependencies: '@tailwindcss/postcss': specifier: ^4.1.9 @@ -1689,6 +1692,9 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + immer@11.1.3: + resolution: {integrity: sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==} + input-otp@1.4.1: resolution: {integrity: sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw==} peerDependencies: @@ -3515,6 +3521,8 @@ snapshots: dependencies: function-bind: 1.1.2 + immer@11.1.3: {} + input-otp@1.4.1(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: react: 19.2.0 @@ -3932,8 +3940,9 @@ snapshots: zod@3.25.76: {} - zustand@5.0.10(@types/react@19.2.9)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)): + zustand@5.0.10(@types/react@19.2.9)(immer@11.1.3)(react@19.2.0)(use-sync-external-store@1.6.0(react@19.2.0)): optionalDependencies: '@types/react': 19.2.9 + immer: 11.1.3 react: 19.2.0 use-sync-external-store: 1.6.0(react@19.2.0) diff --git a/request/links.ts b/request/links.ts index d6abfc6..578d171 100644 --- a/request/links.ts +++ b/request/links.ts @@ -18,8 +18,10 @@ export const endPoints = { filter: { 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`, }, post: { sendNumber: "callBack/",