Merge branch 'inventory'

This commit is contained in:
2025-09-23 10:44:54 -04:00
84 changed files with 12150 additions and 317 deletions

View File

@@ -0,0 +1,55 @@
import { Button } from '@repo/shadcn/button';
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@repo/shadcn/card';
import { Badge } from '@repo/shadcn/badge';
import { BadgeCheck } from 'lucide-react';
import { SurveyAnswerForUser } from '../schemas/survey';
interface cardProps {
data: SurveyAnswerForUser;
onClick: (id: number) => void;
}
export function SurveyCard ({ data, onClick }: cardProps) {
return (
<Card key={data.surveys.id} className="flex flex-col">
<CardHeader>
<CardTitle>{data.surveys.title}</CardTitle>
<CardDescription>{data.surveys.description}</CardDescription>
</CardHeader>
<CardContent className="flex-grow">
<section className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Fecha de creación:</span>
<span>{new Date(data.surveys.created_at).toLocaleDateString()}</span>
</div>
{data.surveys.closingDate && (
<div className="flex justify-between">
<span className="text-muted-foreground">Fecha de cierre:</span>
<span>{new Date(data.surveys.closingDate).toLocaleDateString()}</span>
</div>
)}
</section>
</CardContent>
<CardFooter className="flex justify-center">
{data.answers_surveys === null ? (
<Button className="w-full" onClick={() => onClick(Number(data.surveys.id))}>
Responder
</Button>
) : (
<Badge className="px-4 py-2 w-full bg-green-600 text-black">
<BadgeCheck size={28} />
Realizada
</Badge>
)}
</CardFooter>
</Card>
)
}

View File

@@ -4,83 +4,99 @@
// - Permite editar encuestas existentes
// - Permite eliminar encuestas con confirmación
// - Muestra el estado (publicada/borrador), fechas y conteo de respuestas
'use client';
import { Button } from '@repo/shadcn/button';
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from '@repo/shadcn/card';
import { useRouter } from 'next/navigation';
import { useSurveysForUserQuery } from '@/feactures/surveys/hooks/use-query-surveys';
import { SurveyAnswerForUser } from '../schemas/survey';
import { Badge } from '@repo/shadcn/badge';
import { BadgeCheck } from 'lucide-react';
import { useAllSurveysInfiniteQuery } from '@/feactures/surveys/hooks/use-query-surveys';
import { SurveyCard } from '@/feactures/surveys/components/survey-card';
import { SurveyAnswerForUser } from '../schemas/survey';
import { useEffect, useRef, useState } from 'react';
import { Input } from '@repo/shadcn/components/ui/input';
export function SurveyList() {
const router = useRouter();
const {data: surveys} = useSurveysForUserQuery()
const lastProductRef = useRef(null);
const [search, setSearch] = useState("")
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading
} = useAllSurveysInfiniteQuery(search)
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]);
const surveys = data?.pages.flatMap(page => page.data) || [];
// funcion para el buscador
const formSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
const formdata = new FormData(e.currentTarget)
setSearch(formdata.get('search') as string)
// console.log('submit')
}
const handleRespond = (surveyId: number) => {
router.push(`/dashboard/encuestas/${surveyId}/responder`);
};
// console.log(surveys?.data)
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{surveys?.meta.totalPages === 0 ? (
<div className="col-span-full text-center py-10">
<form onSubmit={formSubmit} action={''} className='col-span-full text-center py-3 flex gap-3'>
<Input name='search' type='text' placeholder='Buscar...' className='' defaultValue={search}/>
<Button variant={'outline'} className=''>Buscar</Button>
</form>
{isLoading ? (
<section className="col-span-full text-center py-10">
<p className="text-muted-foreground">Cargando productos...</p>
</section>
) : surveys.length === 0 ? (
<section className="col-span-full text-center py-10">
<p className="text-muted-foreground">No hay encuestas disponibles en este momento.</p>
</div>
</section>
) : (
surveys?.data.map((data: SurveyAnswerForUser) => (
<Card key={data.surveys.id} className="flex flex-col">
<CardHeader>
<CardTitle>{data.surveys.title}</CardTitle>
<CardDescription>{data.surveys.description}</CardDescription>
</CardHeader>
<CardContent className="flex-grow">
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-muted-foreground">Fecha de creación:</span>
{/* <span>{data.surveys.created_at.toLocaleDateString()}</span> */}
<span>{new Date(data.surveys.created_at).toLocaleDateString()}</span>
</div>
{data.surveys.closingDate && (
<div className="flex justify-between">
<span className="text-muted-foreground">Fecha de cierre:</span>
{/* <span>{data.surveys.closingDate.toLocaleDateString()}</span> */}
<span>{new Date(data.surveys.closingDate).toLocaleDateString()}</span>
</div>
)}
<>
{surveys.map((data: SurveyAnswerForUser, index) => {
const isLastElement = index === surveys.length - 1;
return (
<div ref={isLastElement ? lastProductRef : null} key={data.surveys.id}>
<SurveyCard data={data} onClick={handleRespond}/>
</div>
</CardContent>
<CardFooter className="flex justify-center">
{data.answers_surveys === null ? (
<Button
className="w-full"
onClick={() => handleRespond(Number(data.surveys.id))}
>
Responder
</Button>
) : (
<Badge className="px-4 py-2 w-full bg-green-600 text-black">
<BadgeCheck size={28} />
Realizada
</Badge>
)}
</CardFooter>
</Card>
))
)
})}
{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,5 +1,5 @@
'use client'
import { useSafeQuery } from "@/hooks/use-safe-query";
import { useSafeInfiniteQuery, useSafeQuery } from "@/hooks/use-safe-query";
import { getSurveyByIdAction, getSurveysAction, getSurveysForUserAction } from "../actions/surveys-actions";
@@ -8,13 +8,25 @@ export function useSurveysQuery(params = {}) {
return useSafeQuery(['surveys',params], () => getSurveysAction(params))
}
export function useAllSurveysInfiniteQuery(search: string = '') {
return useSafeInfiniteQuery(
['surveys', search],
// pageParam + 1 para evitar duplicación de datos
({ pageParam = 0 }) => getSurveysForUserAction({ page: pageParam + 1, limit: 10, search }),
(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;
}
)
}
export function useSurveysForUserQuery(params = {}) {
return useSafeQuery(['surveys',params], () => getSurveysForUserAction(params))
}
export function useSurveysByIdQuery(id: number) {
return useSafeQuery(['surveys',id], () => getSurveyByIdAction(id))
}