favourite list update page

This commit is contained in:
Samandar Turgunboyev
2026-03-16 16:10:25 +05:00
parent 26c933d196
commit 3d1d9d4860
3 changed files with 71 additions and 45 deletions

View File

@@ -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<HTMLDivElement>(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 (
<div className="min-h-screen py-12">
<div className="container mx-auto px-4">
@@ -54,8 +83,6 @@ export default function Favourite() {
<Skeleton className="h-8 w-64 mb-2" />
<Skeleton className="h-4 w-32" />
</div>
{/* Grid */}
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
{Array.from({ length: 10 }).map((_, i) => (
<div key={i} className="rounded-xl border p-3 space-y-3">
@@ -71,37 +98,32 @@ export default function Favourite() {
return (
<div className="custom-container">
<>
<div className="mb-8">
<div>
<h1 className="text-3xl font-bold text-slate-800 mb-2">
{t('Sevimli mahsulotlar')}
</h1>
<p className="text-slate-500">
{favourite && favourite.total} {t('ta mahsulot')}
</p>
</div>
</div>
<div className="mb-8">
<h1 className="text-3xl font-bold text-slate-800 mb-2">
{t('Sevimli mahsulotlar')}
</h1>
<p className="text-slate-500">
{favourite.length} {t('ta mahsulot')}
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4 mb-30">
{isLoading &&
Array.from({ length: 6 }).map((_, index) => (
<Card className="p-3 space-y-3 rounded-xl" key={index}>
<Skeleton className="h-40 sm:h-48 md:h-56 w-full rounded-lg" />
<Skeleton className="h-4 w-3/4" />
<Skeleton className="h-4 w-1/2" />
<Skeleton className="h-10 w-full rounded-lg" />
</Card>
))}
{favourite &&
!isLoading &&
favourite?.results
.filter((product) => product.state === 'A')
.map((product) => (
<ProductCard key={product.id} product={product} />
))}
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 xl:grid-cols-6 gap-4 mb-8">
{favourite
.filter((product) => product.state === 'A')
.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
{/* Sentinel element — screen pastiga yetganda trigger bo'ladi */}
<div ref={loadMoreRef} className="h-10" />
{/* Yuklash indikatori */}
{isFetchingNextPage && (
<div className="flex justify-center py-6">
<Loader2 className="w-6 h-6 animate-spin text-blue-600" />
</div>
</>
)}
</div>
);
}

View File

@@ -55,8 +55,11 @@ export const product_api = {
return res;
},
async favouuriteProduct(): Promise<AxiosResponse<FavouriteProduct>> {
const res = await httpClient.get(API_URLS.FavouriteProduct);
async favouuriteProduct(params: {
page: number;
page_size: number;
}): Promise<AxiosResponse<FavouriteProduct>> {
const res = await httpClient.get(API_URLS.FavouriteProduct, { params });
return res;
},
};

View File

@@ -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'] });
},