diff --git a/components/pages/products/filter/filter.tsx b/components/pages/products/filter/filter.tsx index ad79a5c..72037b7 100644 --- a/components/pages/products/filter/filter.tsx +++ b/components/pages/products/filter/filter.tsx @@ -262,7 +262,7 @@ export default function Filter() { )} {/* O'lcham filtri */} - {visibleSectionNumber && visibleSectionNumber.length > 0 && ( + {/* {visibleSectionNumber && visibleSectionNumber.length > 0 && (

O'lcham @@ -297,7 +297,7 @@ export default function Filter() { {numberExpanded ? "Yashirish" : "Ko'proq ko'rish"}

- )} + )} */} ); } diff --git a/components/pages/products/product/mianProduct.tsx b/components/pages/products/product/mianProduct.tsx index d5c9c64..4de98e3 100644 --- a/components/pages/products/product/mianProduct.tsx +++ b/components/pages/products/product/mianProduct.tsx @@ -5,50 +5,71 @@ 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"; +import { useMemo, useState } from "react"; import { useProductPageInfo } from "@/store/useProduct"; import { useSubCategory } from "@/store/useSubCategory"; import { useTranslations } from "next-intl"; +import PaginationLite from "@/components/paginationUI"; export default function MainProduct() { const t = useTranslations(); - const category = useCategory((state) => state.category); - const subCategory = useSubCategory((state) => state.subCategory); - const filter = useFilter((state) => state.filter); - const getFiltersByType = useFilter((state) => state.getFiltersByType); - const setProduct = useProductPageInfo((state) => state.setProducts); - // Query params yaratish + const category = useCategory((s) => s.category); + const subCategory = useSubCategory((s) => s.subCategory); + const filter = useFilter((s) => s.filter); + const getFiltersByType = useFilter((s) => s.getFiltersByType); + const setProduct = useProductPageInfo((s) => s.setProducts); + + const [currentPage, setCurrentPage] = useState(1); + 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 for gitea + const catalogParams = catalog.map((i) => `catalog=${i.id}`).join("&"); + const sizeParams = size.map((i) => `size=${i.id}`).join("&"); const allParams = [catalogParams, sizeParams].filter(Boolean).join("&"); - + setCurrentPage(1); return allParams ? `&${allParams}` : ""; - }, [filter, getFiltersByType]); + }, [filter]); - // Request link yaratish const requestLink = useMemo(() => { const baseLink = category.have_sub_category - ? endPoints.product.bySubCategory(subCategory.id) - : endPoints.product.byCategory(category.id || 0); - - // Query params qo'shish + ? endPoints.product.bySubCategory({ id: subCategory.id, currentPage }) + : endPoints.product.byCategory({ id: category.id, currentPage }); return `${baseLink}${queryParams}`; - }, [category.id, category.have_sub_category, queryParams, subCategory.id]); + }, [ + category.id, + category.have_sub_category, + queryParams, + subCategory.id, + currentPage, + ]); const { data, isLoading, error } = useQuery({ - queryKey: ["products", subCategory.id, queryParams], + queryKey: [ + "products", + subCategory.id, + category.id, + queryParams, + currentPage, + ], queryFn: () => httpClient(requestLink), - select: (data) => data?.data?.data?.results, + placeholderData: (prev) => prev, // ✅ pagination da flicker yo'q + select: (res) => ({ + results: res?.data?.data?.results ?? [], + totalPages: res?.data?.data?.total_pages ?? 1, + }), }); - if (isLoading) { + // ✅ To'g'ridan select dan olamiz — useEffect + let emas + const results = useMemo(() => { + return data?.results ?? []; + }, [data]); + const totalPages = useMemo(() => { + return data?.totalPages ?? 1; + }, [data]); + + if (isLoading && !data) { + // ✅ placeholderData bor — faqat birinchi yuklanishda return (
{[1, 2, 3].map((i) => ( @@ -59,10 +80,12 @@ export default function MainProduct() { } if (error) { - return
{t("loadingError")}
; + return ( +
{t("loadingError")}
+ ); } - if (!data || data.length === 0) { + if (!results.length) { return (
{t("productsNotFound")} @@ -71,16 +94,29 @@ export default function MainProduct() { } return ( -
- {data.map((item: any) => ( - setProduct(item)} - title={item.name} - image={item?.images[0]?.image || ""} - slug="special_product" +
+ {/* ✅ isLoading da overlay — list o'rnini saqlab, ustidan opacity */} +
+ {results.map((item: any) => ( + setProduct(item)} + title={item.name} + image={item?.images?.[0]?.image || ""} + slug="special_product" + /> + ))} +
+ + {totalPages > 1 && ( + setCurrentPage(p)} /> - ))} + )}
); } diff --git a/components/pages/products/product/productCard.tsx b/components/pages/products/product/productCard.tsx index 4a3a021..3edec54 100644 --- a/components/pages/products/product/productCard.tsx +++ b/components/pages/products/product/productCard.tsx @@ -1,3 +1,5 @@ +"use client"; + import { useLocale } from "next-intl"; import Image from "next/image"; import Link from "next/link"; @@ -18,26 +20,141 @@ export default function ProductCard({ const locale = useLocale(); return ( - -
+ +
+ + {/* Top accent line */} +
+ {/* Image Container */} -
+
+ {/* Background pattern */} +
+ {title} + + {/* Hover overlay */} +
- {/* Content Container */} -
-

+ {/* Content */} +
+ {/* Decorative line */} +
+ +

{title}

+ + {/* Bottom row */} +
+ + Batafsil + + + {/* Arrow */} +
+ + + +
+

); -} +} \ No newline at end of file diff --git a/components/paginationUI.tsx b/components/paginationUI.tsx new file mode 100644 index 0000000..29d1bd3 --- /dev/null +++ b/components/paginationUI.tsx @@ -0,0 +1,99 @@ +"use client"; + +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from "@/components/ui/pagination"; + +type Props = { + currentPage: number; + totalPages: number; + onChange: (page: number) => void; +}; + +export default function PaginationLite({ + currentPage, + totalPages, + onChange, +}: Props) { + const getPages = () => { + const visibleCount = 7; // maximum number of items to show (including ellipses) + const pages: (number | string)[] = []; + + // If total pages are within visible limit, show all + if (totalPages <= visibleCount) { + for (let i = 1; i <= totalPages; i++) pages.push(i); + return pages; + } + + // If current page is near the beginning: show 1..5, ellipsis, last + if (currentPage <= 4) { + for (let i = 1; i <= 5; i++) pages.push(i); + pages.push("…", totalPages); + return pages; + } + + // If current page is near the end: show first, ellipsis, last-4..last + if (currentPage >= totalPages - 3) { + pages.push(1, "…"); + for (let i = totalPages - 4; i <= totalPages; i++) pages.push(i); + return pages; + } + + // Middle case: first, ellipsis, current-1, current, current+1, ellipsis, last + pages.push( + 1, + "…", + currentPage - 1, + currentPage, + currentPage + 1, + "…", + totalPages, + ); + return pages; + }; + + const pages = getPages(); + + return ( + + + {totalPages !== 1 && ( + onChange(Math.max(1, currentPage - 1))} + /> + )} + + {/* Pages */} + {pages.map((p, idx) => + p === "…" ? ( + + … + + ) : ( + + onChange(Number(p))} + > + {p} + + + ), + )} + {totalPages !== 1 && ( + onChange(Math.min(totalPages, currentPage + 1))} + /> + )} + + + ); +} diff --git a/components/ui/pagination.tsx b/components/ui/pagination.tsx index ce8f25d..d6ee8d6 100644 --- a/components/ui/pagination.tsx +++ b/components/ui/pagination.tsx @@ -1,20 +1,20 @@ -import * as React from 'react' +import * as React from "react" import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon, -} from 'lucide-react' +} from "lucide-react" -import { cn } from '@/lib/utils' -import { Button, buttonVariants } from '@/components/ui/button' +import { cn } from "@/lib/utils" +import { Button, buttonVariants } from "@/components/ui/button" -function Pagination({ className, ...props }: React.ComponentProps<'nav'>) { +function Pagination({ className, ...props }: React.ComponentProps<"nav">) { return (