agprotocolopainelweb/src/views/protocols/ProtocolsDialog.tsx

261 lines
7.4 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable react-hooks/set-state-in-effect */
'use client';
import { getCounts, getProtocols } from '@/services/protocolService';
import { Button } from '@/views/components/ui/button';
import {
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/views/components/ui/dialog';
import {
Pagination,
PaginationContent,
PaginationEllipsis,
PaginationItem,
PaginationLink,
PaginationNext,
PaginationPrevious,
} from '@/views/components/ui/pagination';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/views/components/ui/table';
import { useEffect, useState } from 'react';
import type { Protocol } from '@/services/protocolService';
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from '../components/ui/card';
type Props = {
status: string;
};
export function ProtocolsDialog({ status }: Props) {
const [open, setOpen] = useState(false);
const [search, setSearch] = useState('');
const [page, setPage] = useState(1);
const [pageSize] = useState(10);
const [allItems, setAllItems] = useState<Protocol[]>([]);
const [paginatedItems, setPaginatedItems] = useState<Protocol[]>([]);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(false);
const [counts, setCounts] = useState<Record<string, number>>({ Todos: 0 });
const label = `${status} (${counts[status] ?? 0})`;
useEffect(() => {
setCounts(getCounts());
}, []);
useEffect(() => {
if (!open) return;
setLoading(true);
getProtocols({
status: status as any,
page: 1,
pageSize: 1000,
search,
}).then((r) => {
setAllItems(r.items);
setTotal(r.total);
setLoading(false);
setPage(1); // Reset para primeira página quando buscar
});
}, [open, search, status]);
// Efeito para aplicar a paginação local
useEffect(() => {
if (allItems.length === 0) {
setPaginatedItems([]);
return;
}
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + pageSize;
const itemsForCurrentPage = allItems.slice(startIndex, endIndex);
setPaginatedItems(itemsForCurrentPage);
}, [allItems, page, pageSize]);
const totalPages = Math.max(1, Math.ceil(total / pageSize));
function getPageItems(totalPages: number, current: number) {
const pages: Array<number | 'ellipsis'> = [];
if (totalPages <= 7) {
for (let i = 1; i <= totalPages; i++) pages.push(i);
return pages;
}
pages.push(1);
const left = Math.max(2, current - 1);
const right = Math.min(totalPages - 1, current + 1);
if (left > 2) pages.push('ellipsis');
for (let i = left; i <= right; i++) pages.push(i);
if (right < totalPages - 1) pages.push('ellipsis');
pages.push(totalPages);
return pages;
}
const handlePreviousPage = () => {
if (page > 1) {
setPage(page - 1);
}
};
const handleNextPage = () => {
if (page < totalPages) {
setPage(page + 1);
}
};
const handlePageClick = (pageNumber: number) => {
setPage(pageNumber);
};
const pageItems = getPageItems(totalPages, page);
return (
<Dialog
open={open}
onOpenChange={(v) => {
setOpen(v);
if (!v) {
// Resetar estado quando o dialog fechar
setSearch('');
setPage(1);
}
}}>
<DialogTrigger asChild>
<Card className="hover:shadow-md transition-shadow duration-200 cursor-pointer">
<CardHeader>
<CardTitle className="text-sm">{`Protocolos ${status.toLowerCase()}`}</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold mb-3">{counts[status] ?? 0}</div>
</CardContent>
</Card>
</DialogTrigger>
<DialogContent className="min-w-[80vw] max-w-[80vw] h-full max-h-[80vh] overflow-auto">
<DialogHeader>
<DialogTitle>{label}</DialogTitle>
</DialogHeader>
<div className="flex gap-2 items-center mb-4">
<input
className="flex-1 rounded-sm border px-3 py-2"
placeholder={`Pesquisar em ${label.toLowerCase()}...`}
value={search}
onChange={(e) => {
setSearch(e.target.value);
setPage(1);
}}
/>
<div className="text-sm text-muted-foreground">
{loading ? 'Carregando...' : `${total} encontrados`}
</div>
</div>
<div className="overflow-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[100px]">Código</TableHead>
<TableHead>Assunto</TableHead>
<TableHead className="w-[120px]">Data</TableHead>
<TableHead>Solicitante</TableHead>
<TableHead>Local</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{paginatedItems.map((protocol) => (
<TableRow key={protocol.id}>
<TableCell className="font-medium">{protocol.id}</TableCell>
<TableCell>{protocol.title}</TableCell>
<TableCell>{protocol.date}</TableCell>
<TableCell>{protocol.sender}</TableCell>
<TableCell>{protocol.location}</TableCell>
</TableRow>
))}
{paginatedItems.length === 0 && (
<TableRow>
<TableCell colSpan={5} className="h-24 text-center">
{loading ? 'Carregando...' : 'Nenhum protocolo encontrado.'}
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
<div className="flex items-center justify-between gap-2 mt-4">
<div className="text-sm text-muted-foreground">
Página {page} de {totalPages}
</div>
<Pagination className='w-fit m-0'>
<PaginationContent>
<PaginationItem>
<PaginationPrevious
href="#"
onClick={handlePreviousPage}
className={page <= 1 ? 'pointer-events-none opacity-50' : ''}
/>
</PaginationItem>
{pageItems.map((pageItem, index) => (
<PaginationItem key={index}>
{pageItem === 'ellipsis' ? (
<PaginationEllipsis />
) : (
<PaginationLink
href="#"
onClick={() => handlePageClick(pageItem)}
isActive={pageItem === page}>
{pageItem}
</PaginationLink>
)}
</PaginationItem>
))}
<PaginationItem>
<PaginationNext
href="#"
onClick={handleNextPage}
className={
page >= totalPages ? 'pointer-events-none opacity-50' : ''
}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
</div>
<DialogFooter>
<Button onClick={() => setOpen(false)}>Fechar</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
export default ProtocolsDialog;