scroll infinito de productos
This commit is contained in:
@@ -1,14 +1,26 @@
|
|||||||
|
'use client';
|
||||||
// import PageContainer from '@/components/layout/page-container';
|
// 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 { 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 (
|
return (
|
||||||
// <PageContainer>
|
// <PageContainer>
|
||||||
<main className='p-4 md:px-6'>
|
<main className='p-4 md:px-6'>
|
||||||
@@ -18,7 +30,7 @@ export default function SurveysPage() {
|
|||||||
<Button>Mi inventario</Button>
|
<Button>Mi inventario</Button>
|
||||||
</a>
|
</a>
|
||||||
</header>
|
</header>
|
||||||
<ProductList />
|
<ProductList/>
|
||||||
</main>
|
</main>
|
||||||
// </PageContainer>
|
// </PageContainer>
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,21 +1,12 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardFooter,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from '@repo/shadcn/card';
|
|
||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useAllProductQuery } from '@/feactures/inventory/hooks/use-query-products';
|
import { useAllProductQuery } from '@/feactures/inventory/hooks/use-query-products';
|
||||||
import { allProducts } from '../../schemas/inventory';
|
import { allProducts } from '../../schemas/inventory';
|
||||||
// import { ImageIcon } from 'lucide-react';
|
import { ProductCard } from '@/feactures/inventory/components/products/productCard'
|
||||||
|
|
||||||
export function ProductList() {
|
export function ProductList() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { data: produts } = useAllProductQuery();
|
const { data: produts } = useAllProductQuery();
|
||||||
// console.log(produts);
|
|
||||||
|
|
||||||
const handle = (id: number) => {
|
const handle = (id: number) => {
|
||||||
router.push(`/dashboard/productos/${id}`);
|
router.push(`/dashboard/productos/${id}`);
|
||||||
@@ -31,30 +22,7 @@ export function ProductList() {
|
|||||||
</section>
|
</section>
|
||||||
) : (
|
) : (
|
||||||
produts?.data.map((data: allProducts) => (
|
produts?.data.map((data: allProducts) => (
|
||||||
<Card
|
<ProductCard product={data} onClick={() => handle(Number(data.id))} key={data.id} />
|
||||||
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>
|
|
||||||
))
|
))
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,12 +1,36 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import { useSafeQuery } from "@/hooks/use-safe-query";
|
import { useSafeQuery, useSafeInfiniteQuery } from "@/hooks/use-safe-query";
|
||||||
import { getInventoryAction, getAllProducts } from "../actions/actions";
|
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
|
// Hook for users
|
||||||
export function useProductQuery(params = {}) {
|
export function useProductQuery(params: Params = {}) {
|
||||||
return useSafeQuery(['product',params], () => getInventoryAction(params))
|
return useSafeQuery(['product',params], () => getInventoryAction(params))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useAllProductQuery(params = {}) {
|
export function useAllProductQuery(params: Params = {}) {
|
||||||
return useSafeQuery(['product',params], () => getAllProducts(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;
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@@ -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>(
|
export function useSafeQuery<T, K = unknown>(
|
||||||
queryKey: [string, K?],
|
queryKey: [string, K?],
|
||||||
@@ -10,4 +11,34 @@ export function useSafeQuery<T, K = unknown>(
|
|||||||
queryFn,
|
queryFn,
|
||||||
...options,
|
...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,
|
||||||
|
// })
|
||||||
|
|
||||||
|
// }
|
||||||
Reference in New Issue
Block a user