diff --git a/src/features/favourite/ui/Favourite.tsx b/src/features/favourite/ui/Favourite.tsx index 60522db..0e9c2f6 100644 --- a/src/features/favourite/ui/Favourite.tsx +++ b/src/features/favourite/ui/Favourite.tsx @@ -3,25 +3,54 @@ import { product_api } from '@/shared/config/api/product/api'; import { useRouter } from '@/shared/config/i18n/navigation'; import { Button } from '@/shared/ui/button'; -import { Card } from '@/shared/ui/card'; import { Skeleton } from '@/shared/ui/skeleton'; import { ProductCard } from '@/widgets/categories/ui/product-card'; -import { useQuery } from '@tanstack/react-query'; -import { Heart } from 'lucide-react'; +import { useInfiniteQuery } from '@tanstack/react-query'; +import { Heart, Loader2 } from 'lucide-react'; import { useTranslations } from 'next-intl'; +import { useEffect, useRef } from 'react'; export default function Favourite() { const router = useRouter(); const t = useTranslations(); - const { data: favourite, isLoading } = useQuery({ - queryKey: ['favourite_product'], - queryFn: () => product_api.favouuriteProduct(), - select(data) { - return data.data; - }, - }); + const loadMoreRef = useRef(null); - if (favourite && favourite.results.length === 0) { + const { data, isLoading, fetchNextPage, hasNextPage, isFetchingNextPage } = + useInfiniteQuery({ + queryKey: ['favourite_product'], + initialPageParam: 1, + queryFn: ({ pageParam }) => + product_api.favouuriteProduct({ page: pageParam, page_size: 30 }), + getNextPageParam: (lastPage) => { + if (lastPage.data.has_next) return lastPage.data.page + 1; + return undefined; + }, + select(data) { + return data.pages.flatMap((page) => page.data.results); + }, + }); + + // Intersection Observer — loadMoreRef ko'ringanda keyingi sahifani yuklaydi + useEffect(() => { + const el = loadMoreRef.current; + if (!el) return; + + const observer = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }, + { threshold: 0.1 }, + ); + + observer.observe(el); + return () => observer.disconnect(); + }, [hasNextPage, isFetchingNextPage, fetchNextPage]); + + const favourite = data ?? []; + + if (!isLoading && favourite.length === 0) { return (
@@ -54,8 +83,6 @@ export default function Favourite() {
- - {/* Grid */}
{Array.from({ length: 10 }).map((_, i) => (
@@ -71,37 +98,32 @@ export default function Favourite() { return (
- <> -
-
-

- {t('Sevimli mahsulotlar')} -

-

- {favourite && favourite.total} {t('ta mahsulot')} -

-
-
+
+

+ {t('Sevimli mahsulotlar')} +

+

+ {favourite.length} {t('ta mahsulot')} +

+
-
- {isLoading && - Array.from({ length: 6 }).map((_, index) => ( - - - - - - - ))} - {favourite && - !isLoading && - favourite?.results - .filter((product) => product.state === 'A') - .map((product) => ( - - ))} +
+ {favourite + .filter((product) => product.state === 'A') + .map((product) => ( + + ))} +
+ + {/* Sentinel element — screen pastiga yetganda trigger bo'ladi */} +
+ + {/* Yuklash indikatori */} + {isFetchingNextPage && ( +
+
- + )}
); } diff --git a/src/shared/config/api/product/api.ts b/src/shared/config/api/product/api.ts index 46157a0..aa00acd 100644 --- a/src/shared/config/api/product/api.ts +++ b/src/shared/config/api/product/api.ts @@ -55,8 +55,11 @@ export const product_api = { return res; }, - async favouuriteProduct(): Promise> { - const res = await httpClient.get(API_URLS.FavouriteProduct); + async favouuriteProduct(params: { + page: number; + page_size: number; + }): Promise> { + const res = await httpClient.get(API_URLS.FavouriteProduct, { params }); return res; }, }; diff --git a/src/widgets/categories/ui/product-card.tsx b/src/widgets/categories/ui/product-card.tsx index 41b5ee4..7a1d71f 100644 --- a/src/widgets/categories/ui/product-card.tsx +++ b/src/widgets/categories/ui/product-card.tsx @@ -173,7 +173,8 @@ export function ProductCard({ mutationFn: (productId: string) => product_api.favourite(productId), onSuccess: () => { - queryClient.refetchQueries({ queryKey: ['product_list'] }); + queryClient.refetchQueries({ queryKey: ['list'] }); + queryClient.refetchQueries({ queryKey: ['all_products'] }); queryClient.refetchQueries({ queryKey: ['favourite_product'] }); },