scroll infinito de productos

This commit is contained in:
2025-09-02 14:02:19 -04:00
parent 2d596b93ad
commit 997314b3a5
6 changed files with 205 additions and 47 deletions

View File

@@ -1,14 +1,26 @@
'use client';
// import PageContainer from '@/components/layout/page-container';
import { ProductList } from '@/feactures/inventory/components/products/product-list';
// import { ProductList } from '@/feactures/inventory/components/products/product-list';
import { ProductList } from '@/feactures/inventory/components/products/product-list-scroll';
import { Button } from '@repo/shadcn/components/ui/button';
import { Metadata } from 'next';
// import { Metadata } from 'next';
import { useAllProductQuery } from '@/feactures/inventory/hooks/use-query-products';
import { allProducts } from '@/feactures/inventory/schemas/inventory';
export const metadata: Metadata = {
title: 'Productos - Fondemi',
description: 'Listado de productos disponibles',
};
export default function SurveysPage() {
// export const metadata: Metadata = {
// title: 'Productos - Fondemi',
// description: 'Listado de productos disponibles',
// };
export default function SurveysPage() {
// const { data } = useAllProductQuery();
// let products: allProducts[] = []
// if (data?.data) {
// products = data.data
// }
return (
// <PageContainer>
<main className='p-4 md:px-6'>
@@ -18,7 +30,7 @@ export default function SurveysPage() {
<Button>Mi inventario</Button>
</a>
</header>
<ProductList />
<ProductList/>
</main>
// </PageContainer>

View File

@@ -0,0 +1,80 @@
'use client';
import { useRouter } from 'next/navigation';
import { useAllProductInfiniteQuery } from '@/feactures/inventory/hooks/use-query-products';
import { ProductCard } from '@/feactures/inventory/components/products/productCard';
import { allProducts } from '@/feactures/inventory/schemas/inventory';
import { useRef, useEffect } from 'react';
export function ProductList() {
const router = useRouter();
const lastProductRef = useRef(null);
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading
} = useAllProductInfiniteQuery();
const handle = (id: number) => {
router.push(`/dashboard/productos/${id}`);
};
const products = data?.pages.flatMap(page => page.data) || [];
useEffect(() => {
if (!lastProductRef.current || !hasNextPage || isFetchingNextPage) return;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0]?.isIntersecting) {
fetchNextPage();
}
},
{
root: null,
rootMargin: '200px',
threshold: 1.0,
}
);
observer.observe(lastProductRef.current);
return () => {
observer.disconnect();
};
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
return (
<div className="w-full grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4">
{isLoading ? (
<section className="col-span-full text-center py-10">
<p className="text-muted-foreground">Cargando productos...</p>
</section>
) : products.length === 0 ? (
<section className="col-span-full text-center py-10">
<p className="text-muted-foreground">
No hay productos disponibles en este momento.
</p>
</section>
) : (
<>
{products.map((item, index) => {
const isLastElement = index === products.length - 1;
return (
<div ref={isLastElement ? lastProductRef : null} key={item.id}>
<ProductCard product={item} onClick={() => handle(Number(item.id))} />
</div>
);
})}
{isFetchingNextPage && (
<section className="col-span-full text-center py-10">
<p className="text-muted-foreground">Cargando más productos...</p>
</section>
)}
</>
)}
</div>
);
}

View File

@@ -1,21 +1,12 @@
'use client';
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from '@repo/shadcn/card';
import { useRouter } from 'next/navigation';
import { useAllProductQuery } from '@/feactures/inventory/hooks/use-query-products';
import { allProducts } from '../../schemas/inventory';
// import { ImageIcon } from 'lucide-react';
import { ProductCard } from '@/feactures/inventory/components/products/productCard'
export function ProductList() {
const router = useRouter();
const { data: produts } = useAllProductQuery();
// console.log(produts);
const handle = (id: number) => {
router.push(`/dashboard/productos/${id}`);
@@ -31,30 +22,7 @@ export function ProductList() {
</section>
) : (
produts?.data.map((data: allProducts) => (
<Card
key={data.id}
className="cursor-pointer flex flex-col"
onClick={() => handle(Number(data.id))}
>
{/* <CardHeader> */}
<CardTitle className="text-base font-bold truncate p-3 text-primary">
{data.title.charAt(0).toUpperCase() + data.title.slice(1)}
</CardTitle>
{/* </CardHeader> */}
<CardContent className="p-0 flex-grow">
<img
className="object-cover w-full h-full aspect-square border"
src={`/uploads/inventory/${data.userId}/${data.id}/${data.urlImg}`}
alt=""
/>
</CardContent>
<CardFooter className="flex-col items-start p-4">
{data.status === 'AGOTADO' ? (
<p className="font-semibold text-lg text-red-900">AGOTADO</p>
): ('')}
<p className="font-semibold text-lg">$ {data.price}</p>
</CardFooter>
</Card>
<ProductCard product={data} onClick={() => handle(Number(data.id))} key={data.id} />
))
)}
</div>

View File

@@ -0,0 +1,43 @@
'use client';
import {
Card,
CardContent,
CardFooter,
CardHeader,
CardTitle,
} from '@repo/shadcn/card';
import { allProducts } from '../../schemas/inventory';
interface cardProps {
product: allProducts;
onClick?: () => void;
}
export function ProductCard({ product, onClick }: cardProps) {
return (
<Card className="cursor-pointer flex flex-col" onClick={onClick}
>
{/* <CardHeader> */}
<CardTitle className="text-base font-bold truncate p-3 text-primary">
{product.title.charAt(0).toUpperCase() + product.title.slice(1)}
</CardTitle>
{/* </CardHeader> */}
<CardContent className="p-0 flex-grow">
<img
className="object-cover w-full h-full aspect-square border"
src={`/uploads/inventory/${product.userId}/${product.id}/${product.urlImg}`}
alt=""
/>
</CardContent>
<CardFooter className="flex-col items-start p-4">
{product.status === 'AGOTADO' ? (
<p className="font-semibold text-lg text-red-900">AGOTADO</p>
): ('')}
<p className="font-semibold text-lg">$ {product.price}</p>
</CardFooter>
</Card>
)
}

View File

@@ -1,12 +1,36 @@
'use client'
import { useSafeQuery } from "@/hooks/use-safe-query";
import { useSafeQuery, useSafeInfiniteQuery } from "@/hooks/use-safe-query";
import { getInventoryAction, getAllProducts } from "../actions/actions";
// import { useInfiniteQuery } from "@tanstack/react-query";
interface Params {
page?: number;
limit?: number;
search?: string;
sortBy?: string;
sortOrder?: 'asc' | 'desc';
}
// Hook for users
export function useProductQuery(params = {}) {
export function useProductQuery(params: Params = {}) {
return useSafeQuery(['product',params], () => getInventoryAction(params))
}
export function useAllProductQuery(params = {}) {
export function useAllProductQuery(params: Params = {}) {
return useSafeQuery(['product',params], () => getAllProducts(params))
}
export function useAllProductInfiniteQuery() {
return useSafeInfiniteQuery(
['product'],
// pageParam + 1 para evitar duplicación de datos
({ pageParam = 0 }) => getAllProducts({ page: pageParam + 1, limit: 10 }),
(lastPage, allPages) => {
// Esta lógica determina el 'pageParam' para la siguiente página
const nextPage = allPages.length;
// Puedes añadir una condición para saber si hay más páginas
if (lastPage.data.length < 10) return undefined;
return nextPage;
}
)
}

View File

@@ -1,4 +1,5 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query'
import { UseQueryOptions, useInfiniteQuery, useQuery } from '@tanstack/react-query'
export function useSafeQuery<T, K = unknown>(
queryKey: [string, K?],
@@ -10,4 +11,34 @@ export function useSafeQuery<T, K = unknown>(
queryFn,
...options,
})
}
}
export function useSafeInfiniteQuery<T, K = unknown>(
queryKey: [string, K?],
queryFn: ({ pageParam }: { pageParam: number }) => Promise<T>,
getNextPageParam: (lastPage: T, allPages: T[]) => number | undefined,
// options?: Omit<UseQueryOptions<T>, 'queryKey' | 'queryFn'>
) {
return useInfiniteQuery({
queryKey,
queryFn,
getNextPageParam,
initialPageParam: 0,
})
}
// export function useAllProductInfiniteQuery(){
// return useInfiniteQuery({
// queryKey:['product'],
// queryFn: ({ pageParam = 0 }) => getAllProducts({ page: pageParam + 1, limit: 10 }),
// getNextPageParam: (lastPage, allPages) => {
// // Esta lógica determina el 'pageParam' para la siguiente página
// const nextPage = allPages.length;
// // Puedes añadir una condición para saber si hay más páginas
// if (lastPage.data.length < 10) return undefined;
// return nextPage;
// },
// initialPageParam: 0,
// })
// }