re-alteracoes esteticas

This commit is contained in:
pedrosiimoes 2025-11-06 00:18:34 -03:00
parent 1aec6b56d0
commit 1daa664ff4
3 changed files with 442 additions and 449 deletions

View File

@ -7,7 +7,7 @@ import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/button" import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu" import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Plus, Edit, Trash2, Eye, Calendar, Filter, MoreVertical, Loader2 } from "lucide-react" import { Plus, Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react"
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
@ -69,24 +69,20 @@ export default function DoctorsPage() {
const [specialtyFilter, setSpecialtyFilter] = useState("all"); const [specialtyFilter, setSpecialtyFilter] = useState("all");
const [statusFilter, setStatusFilter] = useState("all"); const [statusFilter, setStatusFilter] = useState("all");
// --- Estados para Paginação (ADICIONADOS) --- // --- Estados para Paginação ---
const [itemsPerPage, setItemsPerPage] = useState(10); const [itemsPerPage, setItemsPerPage] = useState(10);
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
// ---------------------------------------------
const fetchDoctors = useCallback(async () => { const fetchDoctors = useCallback(async () => {
setLoading(true); setLoading(true);
setError(null); setError(null);
try { try {
const data: Doctor[] = await doctorsService.list(); const data: Doctor[] = await doctorsService.list();
// Exemplo: Adicionando um status fake
const dataWithStatus = data.map((doc, index) => ({ const dataWithStatus = data.map((doc, index) => ({
...doc, ...doc,
status: index % 3 === 0 ? "Inativo" : index % 2 === 0 ? "Férias" : "Ativo" status: index % 3 === 0 ? "Inativo" : index % 2 === 0 ? "Férias" : "Ativo"
})); }));
setDoctors(dataWithStatus || []); setDoctors(dataWithStatus || []);
// IMPORTANTE: Resetar a página ao carregar novos dados
setCurrentPage(1); setCurrentPage(1);
} catch (e: any) { } catch (e: any) {
console.error("Erro ao carregar lista de médicos:", e); console.error("Erro ao carregar lista de médicos:", e);
@ -141,37 +137,23 @@ export default function DoctorsPage() {
setDeleteDialogOpen(true); setDeleteDialogOpen(true);
}; };
const handleEdit = (doctorId: number) => {
router.push(`/manager/home/${doctorId}/editar`);
};
// Gera a lista de especialidades dinamicamente
const uniqueSpecialties = useMemo(() => { const uniqueSpecialties = useMemo(() => {
const specialties = doctors.map(doctor => doctor.specialty).filter(Boolean); const specialties = doctors.map(doctor => doctor.specialty).filter(Boolean);
return [...new Set(specialties)]; return [...new Set(specialties)];
}, [doctors]); }, [doctors]);
// Lógica de filtragem
const filteredDoctors = doctors.filter(doctor => { const filteredDoctors = doctors.filter(doctor => {
const specialtyMatch = specialtyFilter === "all" || doctor.specialty === specialtyFilter; const specialtyMatch = specialtyFilter === "all" || doctor.specialty === specialtyFilter;
const statusMatch = statusFilter === "all" || doctor.status === statusFilter; const statusMatch = statusFilter === "all" || doctor.status === statusFilter;
return specialtyMatch && statusMatch; return specialtyMatch && statusMatch;
}); });
// --- Lógica de Paginação (ADICIONADA) ---
// 1. Definição do total de páginas com base nos itens FILTRADOS
const totalPages = Math.ceil(filteredDoctors.length / itemsPerPage); const totalPages = Math.ceil(filteredDoctors.length / itemsPerPage);
// 2. Cálculo dos itens a serem exibidos na página atual (dos itens FILTRADOS)
const indexOfLastItem = currentPage * itemsPerPage; const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage; const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = filteredDoctors.slice(indexOfFirstItem, indexOfLastItem); const currentItems = filteredDoctors.slice(indexOfFirstItem, indexOfLastItem);
// 3. Função para mudar de página
const paginate = (pageNumber: number) => setCurrentPage(pageNumber); const paginate = (pageNumber: number) => setCurrentPage(pageNumber);
// 4. Funções para Navegação
const goToPrevPage = () => { const goToPrevPage = () => {
setCurrentPage((prev) => Math.max(1, prev - 1)); setCurrentPage((prev) => Math.max(1, prev - 1));
}; };
@ -180,7 +162,6 @@ export default function DoctorsPage() {
setCurrentPage((prev) => Math.min(totalPages, prev + 1)); setCurrentPage((prev) => Math.min(totalPages, prev + 1));
}; };
// 5. Lógica para gerar os números das páginas visíveis
const getVisiblePageNumbers = (totalPages: number, currentPage: number) => { const getVisiblePageNumbers = (totalPages: number, currentPage: number) => {
const pages: number[] = []; const pages: number[] = [];
const maxVisiblePages = 5; const maxVisiblePages = 5;
@ -205,245 +186,256 @@ export default function DoctorsPage() {
const visiblePageNumbers = getVisiblePageNumbers(totalPages, currentPage); const visiblePageNumbers = getVisiblePageNumbers(totalPages, currentPage);
// Lógica para mudar itens por página, resetando para a página 1
const handleItemsPerPageChange = (value: string) => { const handleItemsPerPageChange = (value: string) => {
setItemsPerPage(Number(value)); setItemsPerPage(Number(value));
setCurrentPage(1); // Resetar para a primeira página setCurrentPage(1);
}; };
// ----------------------------------------------------
return ( return (
<ManagerLayout> <ManagerLayout>
<div className="space-y-6 px-2 sm:px-4 md:px-6"> <div className="space-y-6 px-2 sm:px-4 md:px-6">
{/* Cabeçalho */} {/* Cabeçalho */}
<div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3"> <div className="flex flex-col sm:flex-row items-start sm:items-center justify-between gap-3">
<div> <div>
<h1 className="text-2xl font-bold text-gray-900">Médicos Cadastrados</h1> <h1 className="text-2xl font-bold text-gray-900">Médicos Cadastrados</h1>
<p className="text-sm text-gray-500">Gerencie todos os profissionais de saúde.</p> <p className="text-sm text-gray-500">Gerencie todos os profissionais de saúde.</p>
</div>
<Link href="/manager/home/novo" className="w-full sm:w-auto">
<Button className="w-full sm:w-auto bg-green-600 hover:bg-green-700">
<Plus className="w-4 h-4 mr-2" />
Adicionar Novo
</Button>
</Link>
</div> </div>
<Link href="/manager/home/novo" className="w-full sm:w-auto">
<Button className="w-full sm:w-auto bg-green-600 hover:bg-green-700">
<Plus className="w-4 h-4 mr-2" /> {/* Filtros e Itens por Página */}
Adicionar Novo <div className="flex flex-wrap items-center gap-3 bg-white p-3 sm:p-4 rounded-lg border border-gray-200">
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Especialidade
</span>
<Select value={specialtyFilter} onValueChange={setSpecialtyFilter}>
<SelectTrigger className="w-[160px] sm:w-[180px]">
<SelectValue placeholder="Especialidade" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Todas</SelectItem>
{uniqueSpecialties.map(specialty => (
<SelectItem key={specialty} value={specialty}>{specialty}</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Status
</span>
<Select value={statusFilter} onValueChange={setStatusFilter}>
<SelectTrigger className="w-[160px] sm:w-[180px]">
<SelectValue placeholder="Status" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">Todos</SelectItem>
<SelectItem value="Ativo">Ativo</SelectItem>
<SelectItem value="Férias">Férias</SelectItem>
<SelectItem value="Inativo">Inativo</SelectItem>
</SelectContent>
</Select>
</div>
<div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground">
Itens por página
</span>
<Select
onValueChange={handleItemsPerPageChange}
defaultValue={String(itemsPerPage)}>
<SelectTrigger className="w-[140px]">
<SelectValue placeholder="Itens por pág." />
</SelectTrigger>
<SelectContent>
<SelectItem value="5">5 por página</SelectItem>
<SelectItem value="10">10 por página</SelectItem>
<SelectItem value="20">20 por página</SelectItem>
</SelectContent>
</Select>
</div>
<Button variant="outline" className="ml-auto w-full md:w-auto">
<Filter className="w-4 h-4 mr-2" />
Filtro avançado
</Button> </Button>
</Link> </div>
</div>
{/* Filtros e Itens por Página (ATUALIZADO) */} {/* Tabela de Médicos */}
<div className="flex flex-wrap items-center gap-3 bg-white p-3 sm:p-4 rounded-lg border border-gray-200"> <div className="bg-white rounded-lg border border-gray-200 shadow-md overflow-hidden">
<Filter className="w-5 h-5 text-gray-400" /> {loading ? (
<div className="p-8 text-center text-gray-500">
{/* Filtro de Especialidade */} <Loader2 className="w-8 h-8 animate-spin mx-auto mb-3 text-green-600" />
<Select value={specialtyFilter} onValueChange={setSpecialtyFilter}> Carregando médicos...
<SelectTrigger className="w-[160px] sm:w-[180px]"> </div>
<SelectValue placeholder="Especialidade" /> ) : error ? (
</SelectTrigger> <div className="p-8 text-center text-red-600">{error}</div>
<SelectContent> ) : filteredDoctors.length === 0 ? (
<SelectItem value="all">Todas</SelectItem> <div className="p-8 text-center text-gray-500">
{uniqueSpecialties.map(specialty => ( {doctors.length === 0
<SelectItem key={specialty} value={specialty}>{specialty}</SelectItem> ? <>Nenhum médico cadastrado. <Link href="/manager/home/novo" className="text-green-600 hover:underline">Adicione um novo</Link>.</>
))} : "Nenhum médico encontrado com os filtros aplicados."
</SelectContent> }
</Select> </div>
) : (
{/* Filtro de Status */} <div className="overflow-x-auto">
<Select value={statusFilter} onValueChange={setStatusFilter}> <table className="w-full min-w-[600px]">
<SelectTrigger className="w-[160px] sm:w-[180px]"> <thead className="bg-gray-50 border-b border-gray-200">
<SelectValue placeholder="Status" /> <tr>
</SelectTrigger> <th className="text-left p-2 md:p-4 font-medium text-gray-700">Nome</th>
<SelectContent> <th className="text-left p-2 md:p-4 font-medium text-gray-700">CRM</th>
<SelectItem value="all">Todos</SelectItem> <th className="text-left p-2 md:p-4 font-medium text-gray-700">Especialidade</th>
<SelectItem value="Ativo">Ativo</SelectItem> <th className="text-left p-2 md:p-4 font-medium text-gray-700">Status</th>
<SelectItem value="Férias">Férias</SelectItem> <th className="text-left p-2 md:p-4 font-medium text-gray-700">Cidade/Estado</th>
<SelectItem value="Inativo">Inativo</SelectItem> <th className="text-right p-4 font-medium text-gray-700">Ações</th>
</SelectContent> </tr>
</Select> </thead>
<tbody className="bg-white divide-y divide-gray-200">
{/* Select de Itens por Página (ADICIONADO) */} {currentItems.map((doctor) => (
<Select <tr key={doctor.id} className="hover:bg-gray-50 transition">
onValueChange={handleItemsPerPageChange} <td className="px-4 py-3 font-medium text-gray-900">{doctor.full_name}</td>
defaultValue={String(itemsPerPage)} <td className="px-4 py-3 text-gray-500 hidden sm:table-cell">{doctor.crm}</td>
> <td className="px-4 py-3 text-gray-500 hidden md:table-cell">{doctor.specialty}</td>
<SelectTrigger className="w-[140px]"> <td className="px-4 py-3 text-gray-500 hidden lg:table-cell">{doctor.status || "N/A"}</td>
<SelectValue placeholder="Itens por pág." /> <td className="px-4 py-3 text-gray-500 hidden xl:table-cell">
</SelectTrigger> {(doctor.city || doctor.state)
<SelectContent> ? `${doctor.city || ""}${doctor.city && doctor.state ? '/' : ''}${doctor.state || ""}`
<SelectItem value="5">5 por página</SelectItem> : "N/A"}
<SelectItem value="10">10 por página</SelectItem> </td>
<SelectItem value="20">20 por página</SelectItem> <td className="px-4 py-3 text-right">
</SelectContent> {/* ===== INÍCIO DA ALTERAÇÃO ===== */}
</Select>
<Button variant="outline" className="ml-auto w-full md:w-auto">
<Filter className="w-4 h-4 mr-2" />
Filtro avançado
</Button>
</div>
{/* Tabela de Médicos */}
<div className="bg-white rounded-lg border border-gray-200 shadow-md overflow-hidden">
{loading ? (
<div className="p-8 text-center text-gray-500">
<Loader2 className="w-8 h-8 animate-spin mx-auto mb-3 text-green-600" />
Carregando médicos...
</div>
) : error ? (
<div className="p-8 text-center text-red-600">{error}</div>
) : filteredDoctors.length === 0 ? (
<div className="p-8 text-center text-gray-500">
{doctors.length === 0
? <>Nenhum médico cadastrado. <Link href="/manager/home/novo" className="text-green-600 hover:underline">Adicione um novo</Link>.</>
: "Nenhum médico encontrado com os filtros aplicados."
}
</div>
) : (
<div className="overflow-x-auto">
<table className="min-w-[700px] w-full divide-y divide-gray-200 text-sm sm:text-base">
<thead className="bg-gray-50">
<tr>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider">Nome</th>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider hidden sm:table-cell">CRM</th>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider hidden md:table-cell">Especialidade</th>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider hidden lg:table-cell">Status</th>
<th className="px-4 py-3 text-left font-medium text-gray-500 uppercase tracking-wider hidden xl:table-cell">Cidade/Estado</th>
<th className="px-4 py-3 text-right font-medium text-gray-500 uppercase tracking-wider">Ações</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{/* Mapeia APENAS os itens da página atual (currentItems) */}
{currentItems.map((doctor) => (
<tr key={doctor.id} className="hover:bg-gray-50 transition">
<td className="px-4 py-3 font-medium text-gray-900">{doctor.full_name}</td>
<td className="px-4 py-3 text-gray-500 hidden sm:table-cell">{doctor.crm}</td>
<td className="px-4 py-3 text-gray-500 hidden md:table-cell">{doctor.specialty}</td>
<td className="px-4 py-3 text-gray-500 hidden lg:table-cell">{doctor.status || "N/A"}</td>
<td className="px-4 py-3 text-gray-500 hidden xl:table-cell">
{(doctor.city || doctor.state)
? `${doctor.city || ""}${doctor.city && doctor.state ? '/' : ''}${doctor.state || ""}`
: "N/A"}
</td>
<td className="px-4 py-3 text-right">
<div className="flex justify-end gap-2">
<Button variant="outline" size="icon" onClick={() => openDetailsDialog(doctor)} title="Visualizar Detalhes"><Eye className="h-4 w-4" /></Button>
<Button variant="outline" size="icon" onClick={() => handleEdit(doctor.id)} title="Editar"><Edit className="h-4 w-4 text-blue-600" /></Button>
<Button variant="outline" size="icon" onClick={() => openDeleteDialog(doctor.id)} title="Excluir"><Trash2 className="h-4 w-4 text-red-600" /></Button>
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<Button variant="ghost" className="h-8 w-8 p-0" title="Mais Ações"><MoreVertical className="h-4 w-4" /></Button> <div className="text-blue-600 cursor-pointer inline-block">Ações</div>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuItem><Calendar className="mr-2 h-4 w-4" />Agendar Consulta</DropdownMenuItem> <DropdownMenuItem onClick={() => openDetailsDialog(doctor)}>
<Eye className="mr-2 h-4 w-4" />
Ver detalhes
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href={`/manager/home/${doctor.id}/editar`}>
<Edit className="mr-2 h-4 w-4" />
Editar
</Link>
</DropdownMenuItem>
<DropdownMenuItem>
<Calendar className="mr-2 h-4 w-4" />
Marcar consulta
</DropdownMenuItem>
<DropdownMenuItem className="text-red-600" onClick={() => openDeleteDialog(doctor.id)}>
<Trash2 className="mr-2 h-4 w-4" />
Excluir
</DropdownMenuItem>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> {/* ===== FIM DA ALTERAÇÃO ===== */}
</td> </td>
</tr> </tr>
))} ))}
</tbody> </tbody>
</table> </table>
</div> </div>
)} )}
</div> </div>
{/* Paginação (ADICIONADA) */} {/* Paginação */}
{totalPages > 1 && ( {totalPages > 1 && (
<div className="flex flex-wrap justify-center items-center gap-2 mt-4 p-4 bg-white rounded-lg border border-gray-200 shadow-md"> <div className="flex flex-wrap justify-center items-center gap-2 mt-4 p-4 bg-white rounded-lg border border-gray-200 shadow-md">
{/* Botão Anterior */}
<button
onClick={goToPrevPage}
disabled={currentPage === 1}
className="flex items-center px-4 py-2 rounded-md font-medium transition-colors text-sm bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-300"
>
{"< Anterior"}
</button>
{/* Números das Páginas */}
{visiblePageNumbers.map((number) => (
<button <button
key={number} onClick={goToPrevPage}
onClick={() => paginate(number)} disabled={currentPage === 1}
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-gray-300 ${ className="flex items-center px-4 py-2 rounded-md font-medium transition-colors text-sm bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-300"
currentPage === number >
{"< Anterior"}
</button>
{visiblePageNumbers.map((number) => (
<button
key={number}
onClick={() => paginate(number)}
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-gray-300 ${currentPage === number
? "bg-green-600 text-white shadow-md border-green-600" ? "bg-green-600 text-white shadow-md border-green-600"
: "bg-gray-100 text-gray-700 hover:bg-gray-200" : "bg-gray-100 text-gray-700 hover:bg-gray-200"
}`} }`}
>
{number}
</button>
))}
<button
onClick={goToNextPage}
disabled={currentPage === totalPages}
className="flex items-center px-4 py-2 rounded-md font-medium transition-colors text-sm bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-300"
> >
{number} {"Próximo >"}
</button> </button>
))} </div>
)}
{/* Botão Próximo */} {/* Dialogs de Exclusão e Detalhes */}
<button <AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
onClick={goToNextPage} <AlertDialogContent>
disabled={currentPage === totalPages} <AlertDialogHeader>
className="flex items-center px-4 py-2 rounded-md font-medium transition-colors text-sm bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-300" <AlertDialogTitle>Confirma a exclusão?</AlertDialogTitle>
> <AlertDialogDescription>
{"Próximo >"} Esta ação é irreversível e excluirá permanentemente o registro deste médico.
</button> </AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={loading}>Cancelar</AlertDialogCancel>
<AlertDialogAction onClick={handleDelete} className="bg-red-600 hover:bg-red-700" disabled={loading}>
{loading ? <Loader2 className="w-4 h-4 mr-2 animate-spin" /> : null}
Excluir
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div> <AlertDialog open={detailsDialogOpen} onOpenChange={setDetailsDialogOpen}>
)} <AlertDialogContent className="max-w-[95%] sm:max-w-lg">
<AlertDialogHeader>
<AlertDialogTitle className="text-2xl">{doctorDetails?.nome}</AlertDialogTitle>
<AlertDialogDescription className="text-left text-gray-700">
{doctorDetails && (
<div className="space-y-3 text-left">
<h3 className="font-semibold mt-2">Informações Principais</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-y-2 gap-x-4 text-sm">
<div><strong>CRM:</strong> {doctorDetails.crm}</div>
<div><strong>Especialidade:</strong> {doctorDetails.especialidade}</div>
<div><strong>Celular:</strong> {doctorDetails.contato.celular || 'N/A'}</div>
<div><strong>Localização:</strong> {`${doctorDetails.endereco.cidade || 'N/A'}/${doctorDetails.endereco.estado || 'N/A'}`}</div>
</div>
{/* Dialogs de Exclusão e Detalhes (Sem alterações) */} <h3 className="font-semibold mt-4">Atendimento e Convênio</h3>
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}> <div className="grid grid-cols-1 sm:grid-cols-2 gap-y-2 gap-x-4 text-sm">
<AlertDialogContent> <div><strong>Convênio:</strong> {doctorDetails.convenio || 'N/A'}</div>
<AlertDialogHeader> <div><strong>VIP:</strong> {doctorDetails.vip ? "Sim" : "Não"}</div>
<AlertDialogTitle>Confirma a exclusão?</AlertDialogTitle> <div><strong>Status:</strong> {doctorDetails.status || 'N/A'}</div>
<AlertDialogDescription> <div><strong>Último atendimento:</strong> {doctorDetails.ultimo_atendimento || 'N/A'}</div>
Esta ação é irreversível e excluirá permanentemente o registro deste médico. <div><strong>Próximo atendimento:</strong> {doctorDetails.proximo_atendimento || 'N/A'}</div>
</AlertDialogDescription> </div>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={loading}>Cancelar</AlertDialogCancel>
<AlertDialogAction onClick={handleDelete} className="bg-red-600 hover:bg-red-700" disabled={loading}>
{loading ? <Loader2 className="w-4 h-4 mr-2 animate-spin" /> : null}
Excluir
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog open={detailsDialogOpen} onOpenChange={setDetailsDialogOpen}>
<AlertDialogContent className="max-w-[95%] sm:max-w-lg">
<AlertDialogHeader>
<AlertDialogTitle className="text-2xl">{doctorDetails?.nome}</AlertDialogTitle>
<AlertDialogDescription className="text-left text-gray-700">
{doctorDetails && (
<div className="space-y-3 text-left">
<h3 className="font-semibold mt-2">Informações Principais</h3>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-y-2 gap-x-4 text-sm">
<div><strong>CRM:</strong> {doctorDetails.crm}</div>
<div><strong>Especialidade:</strong> {doctorDetails.especialidade}</div>
<div><strong>Celular:</strong> {doctorDetails.contato.celular || 'N/A'}</div>
<div><strong>Localização:</strong> {`${doctorDetails.endereco.cidade || 'N/A'}/${doctorDetails.endereco.estado || 'N/A'}`}</div>
</div> </div>
)}
<h3 className="font-semibold mt-4">Atendimento e Convênio</h3> {doctorDetails === null && !loading && (
<div className="grid grid-cols-1 sm:grid-cols-2 gap-y-2 gap-x-4 text-sm"> <div className="text-red-600">Detalhes não disponíveis.</div>
<div><strong>Convênio:</strong> {doctorDetails.convenio || 'N/A'}</div> )}
<div><strong>VIP:</strong> {doctorDetails.vip ? "Sim" : "Não"}</div> </AlertDialogDescription>
<div><strong>Status:</strong> {doctorDetails.status || 'N/A'}</div> </AlertDialogHeader>
<div><strong>Último atendimento:</strong> {doctorDetails.ultimo_atendimento || 'N/A'}</div> <AlertDialogFooter>
<div><strong>Próximo atendimento:</strong> {doctorDetails.proximo_atendimento || 'N/A'}</div> <AlertDialogCancel>Fechar</AlertDialogCancel>
</div> </AlertDialogFooter>
</div> </AlertDialogContent>
)} </AlertDialog>
{doctorDetails === null && !loading && ( </div>
<div className="text-red-600">Detalhes não disponíveis.</div>
)}
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Fechar</AlertDialogCancel>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</ManagerLayout> </ManagerLayout>
); );
} }

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useState, useEffect, useRef, useCallback } from "react"; import React, { useState, useEffect, useCallback, useMemo } from "react";
import Link from "next/link"; import Link from "next/link";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { import {
@ -16,7 +16,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/components/ui/select"; } from "@/components/ui/select";
import { Plus, Edit, Trash2, Eye, Calendar, Filter } from "lucide-react"; import { Plus, Edit, Trash2, Eye, Calendar, Filter, Loader2 } from "lucide-react";
import { import {
AlertDialog, AlertDialog,
AlertDialogAction, AlertDialogAction,
@ -30,29 +30,22 @@ import {
import ManagerLayout from "@/components/manager-layout"; import ManagerLayout from "@/components/manager-layout";
import { patientsService } from "@/services/patientsApi.mjs"; import { patientsService } from "@/services/patientsApi.mjs";
// 📅 PASSO 1: Criar uma função para formatar a data
const formatDate = (dateString: string | null | undefined): string => { const formatDate = (dateString: string | null | undefined): string => {
// Se a data não existir, retorna um texto padrão
if (!dateString) { if (!dateString) {
return "N/A"; return "N/A";
} }
try { try {
const date = new Date(dateString); const date = new Date(dateString);
// Verifica se a data é válida após a conversão
if (isNaN(date.getTime())) { if (isNaN(date.getTime())) {
return "Data inválida"; return "Data inválida";
} }
const day = String(date.getDate()).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0'); // Mês é base 0, então +1 const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear(); const year = date.getFullYear();
const hours = String(date.getHours()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0');
return `${day}/${month}/${year} ${hours}:${minutes}`; return `${day}/${month}/${year} ${hours}:${minutes}`;
} catch (error) { } catch (error) {
// Se houver qualquer erro na conversão, retorna um texto de erro
return "Data inválida"; return "Data inválida";
} }
}; };
@ -63,16 +56,19 @@ export default function PacientesPage() {
const [convenioFilter, setConvenioFilter] = useState("all"); const [convenioFilter, setConvenioFilter] = useState("all");
const [vipFilter, setVipFilter] = useState("all"); const [vipFilter, setVipFilter] = useState("all");
const [patients, setPatients] = useState<any[]>([]); const [patients, setPatients] = useState<any[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(true); // Alterado para true
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const [page, setPage] = useState(1);
const [hasNext, setHasNext] = useState(true); // --- Estados de Paginação (ADICIONADOS) ---
const [isFetching, setIsFetching] = useState(false); const [itemsPerPage, setItemsPerPage] = useState(10);
const observerRef = useRef<HTMLDivElement | null>(null); const [currentPage, setCurrentPage] = useState(1);
// ---------------------------------------------
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
const [patientToDelete, setPatientToDelete] = useState<string | null>(null); const [patientToDelete, setPatientToDelete] = useState<string | null>(null);
const [detailsDialogOpen, setDetailsDialogOpen] = useState(false); const [detailsDialogOpen, setDetailsDialogOpen] = useState(false);
const [patientDetails, setPatientDetails] = useState<any | null>(null); const [patientDetails, setPatientDetails] = useState<any | null>(null);
const openDetailsDialog = async (patientId: string) => { const openDetailsDialog = async (patientId: string) => {
setDetailsDialogOpen(true); setDetailsDialogOpen(true);
setPatientDetails(null); setPatientDetails(null);
@ -84,75 +80,43 @@ export default function PacientesPage() {
} }
}; };
const fetchPacientes = useCallback( // --- LÓGICA DE BUSCA DE DADOS (ATUALIZADA) ---
async (pageToFetch: number) => { const fetchPacientes = useCallback(async () => {
if (isFetching || !hasNext) return; setLoading(true);
setIsFetching(true); setError(null);
setError(null); try {
try { const res = await patientsService.list();
const res = await patientsService.list(); const mapped = res.map((p: any) => ({
const mapped = res.map((p: any) => ({ id: String(p.id ?? ""),
id: String(p.id ?? ""), nome: p.full_name ?? "",
nome: p.full_name ?? "", telefone: p.phone_mobile ?? p.phone1 ?? "",
telefone: p.phone_mobile ?? p.phone1 ?? "", cidade: p.city ?? "",
cidade: p.city ?? "", estado: p.state ?? "",
estado: p.state ?? "", ultimoAtendimento: p.last_visit_at ?? "",
ultimoAtendimento: p.last_visit_at ?? "", proximoAtendimento: p.next_appointment_at ?? "",
proximoAtendimento: p.next_appointment_at ?? "", vip: Boolean(p.vip ?? false),
vip: Boolean(p.vip ?? false), convenio: p.convenio ?? "",
convenio: p.convenio ?? "", // se não existir, fica vazio status: p.status ?? undefined,
status: p.status ?? undefined, }));
})); setPatients(mapped);
setCurrentPage(1); // Resetar para a primeira página ao carregar
setPatients((prev) => { } catch (e: any) {
const all = [...prev, ...mapped]; setError(e?.message || "Erro ao buscar pacientes");
const unique = Array.from( setPatients([]);
new Map(all.map((p) => [p.id, p])).values() } finally {
); setLoading(false);
return unique; }
});
if (!mapped.id) setHasNext(false); // parar carregamento
else setPage((prev) => prev + 1);
} catch (e: any) {
setError(e?.message || "Erro ao buscar pacientes");
} finally {
setIsFetching(false);
}
},
[isFetching, hasNext]
);
useEffect(() => {
fetchPacientes(page);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
useEffect(() => { useEffect(() => {
if (!observerRef.current || !hasNext) return; fetchPacientes();
const observer = new window.IntersectionObserver((entries) => { }, [fetchPacientes]);
if (entries[0].isIntersecting && !isFetching && hasNext) {
fetchPacientes(page);
}
});
observer.observe(observerRef.current);
return () => {
if (observerRef.current) observer.unobserve(observerRef.current);
};
}, [fetchPacientes, page, hasNext, isFetching]);
const handleDeletePatient = async (patientId: string) => { const handleDeletePatient = async (patientId: string) => {
// Remove from current list (client-side deletion)
try { try {
const res = await patientsService.delete(patientId); await patientsService.delete(patientId);
// Recarrega a lista para refletir a exclusão
if (res) { await fetchPacientes();
alert(`${res.error} ${res.message}`);
}
setPatients((prev) =>
prev.filter((p) => String(p.id) !== String(patientId))
);
} catch (e: any) { } catch (e: any) {
setError(e?.message || "Erro ao deletar paciente"); setError(e?.message || "Erro ao deletar paciente");
} }
@ -179,6 +143,50 @@ export default function PacientesPage() {
return matchesSearch && matchesConvenio && matchesVip; return matchesSearch && matchesConvenio && matchesVip;
}); });
// --- LÓGICA DE PAGINAÇÃO (ADICIONADA) ---
const totalPages = Math.ceil(filteredPatients.length / itemsPerPage);
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = filteredPatients.slice(indexOfFirstItem, indexOfLastItem);
const paginate = (pageNumber: number) => setCurrentPage(pageNumber);
const goToPrevPage = () => {
setCurrentPage((prev) => Math.max(1, prev - 1));
};
const goToNextPage = () => {
setCurrentPage((prev) => Math.min(totalPages, prev + 1));
};
const getVisiblePageNumbers = (totalPages: number, currentPage: number) => {
const pages: number[] = [];
const maxVisiblePages = 5;
const halfRange = Math.floor(maxVisiblePages / 2);
let startPage = Math.max(1, currentPage - halfRange);
let endPage = Math.min(totalPages, currentPage + halfRange);
if (endPage - startPage + 1 < maxVisiblePages) {
if (endPage === totalPages) {
startPage = Math.max(1, totalPages - maxVisiblePages + 1);
}
if (startPage === 1) {
endPage = Math.min(totalPages, maxVisiblePages);
}
}
for (let i = startPage; i <= endPage; i++) {
pages.push(i);
}
return pages;
};
const visiblePageNumbers = getVisiblePageNumbers(totalPages, currentPage);
const handleItemsPerPageChange = (value: string) => {
setItemsPerPage(Number(value));
setCurrentPage(1); // Resetar para a primeira página
};
// ---------------------------------------------
return ( return (
<ManagerLayout> <ManagerLayout>
<div className="space-y-6"> <div className="space-y-6">
@ -193,16 +201,15 @@ export default function PacientesPage() {
</div> </div>
<div className="flex gap-2"> <div className="flex gap-2">
<Link href="/manager/pacientes/novo"> <Link href="/manager/pacientes/novo">
<Button className="w-full md:w-auto"> <Button className="w-full md:w-auto bg-green-600 hover:bg-green-700">
<Plus className="w-4 h-4 mr-2" /> <Plus className="w-4 h-4 mr-2" />
Adicionar Adicionar Novo
</Button> </Button>
</Link> </Link>
</div> </div>
</div> </div>
<div className="flex flex-col md:flex-row flex-wrap gap-4 bg-card p-4 rounded-lg border border-border"> <div className="flex flex-col md:flex-row flex-wrap gap-4 bg-card p-4 rounded-lg border border-border">
{/* Convênio */}
<div className="flex items-center gap-2 w-full md:w-auto"> <div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground"> <span className="text-sm font-medium text-foreground">
Convênio Convênio
@ -233,18 +240,18 @@ export default function PacientesPage() {
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
{/* SELETOR DE ITENS POR PÁGINA (ADICIONADO) */}
<div className="flex items-center gap-2 w-full md:w-auto"> <div className="flex items-center gap-2 w-full md:w-auto">
<span className="text-sm font-medium text-foreground"> <span className="text-sm font-medium text-foreground">Itens por página</span>
Aniversariantes <Select onValueChange={handleItemsPerPageChange} defaultValue={String(itemsPerPage)}>
</span>
<Select>
<SelectTrigger className="w-full md:w-32"> <SelectTrigger className="w-full md:w-32">
<SelectValue placeholder="Selecione" /> <SelectValue placeholder="Itens por pág." />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="today">Hoje</SelectItem> <SelectItem value="5">5 por página</SelectItem>
<SelectItem value="week">Esta semana</SelectItem> <SelectItem value="10">10 por página</SelectItem>
<SelectItem value="month">Este mês</SelectItem> <SelectItem value="20">20 por página</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -257,30 +264,34 @@ export default function PacientesPage() {
<div className="bg-white rounded-lg border border-gray-200"> <div className="bg-white rounded-lg border border-gray-200">
<div className="overflow-x-auto"> <div className="overflow-x-auto">
{error ? ( {loading ? (
<div className="p-8 text-center text-gray-500">
<Loader2 className="w-8 h-8 animate-spin mx-auto mb-3 text-green-600" />
Carregando pacientes...
</div>
) : error ? (
<div className="p-6 text-red-600">{`Erro ao carregar pacientes: ${error}`}</div> <div className="p-6 text-red-600">{`Erro ao carregar pacientes: ${error}`}</div>
) : ( ) : (
<table className="w-full min-w-[600px]"> <table className="w-full min-w-[600px]">
<thead className="bg-gray-50 border-b border-gray-200"> <thead className="bg-gray-50 border-b border-gray-200">
<tr> <tr>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Nome</th> <th className="text-left p-4 font-medium text-gray-700">Nome</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Telefone</th> <th className="text-left p-4 font-medium text-gray-700">Telefone</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Cidade</th> <th className="text-left p-4 font-medium text-gray-700">Cidade</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Estado</th> <th className="text-left p-4 font-medium text-gray-700">Último Atendimento</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Último atendimento</th> <th className="text-right p-4 font-medium text-gray-700">Ações</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Próximo atendimento</th>
<th className="text-left p-2 md:p-4 font-medium text-gray-700">Ações</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{filteredPatients.length === 0 ? ( {filteredPatients.length === 0 ? (
<tr> <tr>
<td colSpan={7} className="p-8 text-center text-gray-500"> <td colSpan={5} className="p-8 text-center text-gray-500">
{patients.length === 0 ? "Nenhum paciente cadastrado" : "Nenhum paciente encontrado com os filtros aplicados"} {patients.length === 0 ? "Nenhum paciente cadastrado." : "Nenhum paciente encontrado com os filtros aplicados."}
</td> </td>
</tr> </tr>
) : ( ) : (
filteredPatients.map((patient) => ( // Mapeando `currentItems` em vez de `filteredPatients`
currentItems.map((patient) => (
<tr key={patient.id} className="border-b border-gray-100 hover:bg-gray-50"> <tr key={patient.id} className="border-b border-gray-100 hover:bg-gray-50">
<td className="p-4"> <td className="p-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
@ -292,14 +303,11 @@ export default function PacientesPage() {
</td> </td>
<td className="p-4 text-gray-600">{patient.telefone}</td> <td className="p-4 text-gray-600">{patient.telefone}</td>
<td className="p-4 text-gray-600">{patient.cidade}</td> <td className="p-4 text-gray-600">{patient.cidade}</td>
<td className="p-4 text-gray-600">{patient.estado}</td>
{/* 📅 PASSO 2: Aplicar a formatação de data na tabela */}
<td className="p-4 text-gray-600">{formatDate(patient.ultimoAtendimento)}</td> <td className="p-4 text-gray-600">{formatDate(patient.ultimoAtendimento)}</td>
<td className="p-4 text-gray-600">{formatDate(patient.proximoAtendimento)}</td> <td className="p-4 text-right">
<td className="p-4">
<DropdownMenu> <DropdownMenu>
<DropdownMenuTrigger asChild> <DropdownMenuTrigger asChild>
<div className="text-blue-600 cursor-pointer">Ações</div> <div className="text-blue-600 cursor-pointer inline-block">Ações</div>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="end"> <DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}> <DropdownMenuItem onClick={() => openDetailsDialog(String(patient.id))}>
@ -329,11 +337,44 @@ export default function PacientesPage() {
</tbody> </tbody>
</table> </table>
)} )}
<div ref={observerRef} style={{ height: 1 }} />
{isFetching && <div className="p-4 text-center text-gray-500">Carregando mais pacientes...</div>}
</div> </div>
</div> </div>
{/* COMPONENTE DE PAGINAÇÃO (ADICIONADO) */}
{totalPages > 1 && (
<div className="flex flex-wrap justify-center items-center gap-2 mt-4 p-4 bg-white rounded-lg border border-gray-200 shadow-md">
<button
onClick={goToPrevPage}
disabled={currentPage === 1}
className="flex items-center px-4 py-2 rounded-md font-medium transition-colors text-sm bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-300"
>
{"< Anterior"}
</button>
{visiblePageNumbers.map((number) => (
<button
key={number}
onClick={() => paginate(number)}
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-gray-300 ${currentPage === number
? "bg-green-600 text-white shadow-md border-green-600"
: "bg-gray-100 text-gray-700 hover:bg-gray-200"
}`}
>
{number}
</button>
))}
<button
onClick={goToNextPage}
disabled={currentPage === totalPages}
className="flex items-center px-4 py-2 rounded-md font-medium transition-colors text-sm bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed border border-gray-300"
>
{"Próximo >"}
</button>
</div>
)}
{/* MODAIS (SEM ALTERAÇÃO) */}
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}> <AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
@ -357,7 +398,6 @@ export default function PacientesPage() {
</AlertDialogContent> </AlertDialogContent>
</AlertDialog> </AlertDialog>
{/* Modal de detalhes do paciente */}
<AlertDialog open={detailsDialogOpen} onOpenChange={setDetailsDialogOpen}> <AlertDialog open={detailsDialogOpen} onOpenChange={setDetailsDialogOpen}>
<AlertDialogContent> <AlertDialogContent>
<AlertDialogHeader> <AlertDialogHeader>
@ -369,63 +409,12 @@ export default function PacientesPage() {
<div className="text-red-600">{patientDetails.error}</div> <div className="text-red-600">{patientDetails.error}</div>
) : ( ) : (
<div className="space-y-2 text-left"> <div className="space-y-2 text-left">
<p> <p><strong>Nome:</strong> {patientDetails.full_name}</p>
<strong>Nome:</strong> {patientDetails.full_name} <p><strong>CPF:</strong> {patientDetails.cpf}</p>
</p> <p><strong>Email:</strong> {patientDetails.email}</p>
<p> <p><strong>Telefone:</strong> {patientDetails.phone_mobile ?? patientDetails.phone1 ?? patientDetails.phone2 ?? "-"}</p>
<strong>CPF:</strong> {patientDetails.cpf} <p><strong>Endereço:</strong> {`${patientDetails.street ?? "-"}, ${patientDetails.neighborhood ?? "-"}, ${patientDetails.city ?? "-"} - ${patientDetails.state ?? "-"}`}</p>
</p> <p><strong>Criado em:</strong> {formatDate(patientDetails.created_at)}</p>
<p>
<strong>Email:</strong> {patientDetails.email}
</p>
<p>
<strong>Telefone:</strong> {patientDetails.phone_mobile ?? patientDetails.phone1 ?? patientDetails.phone2 ?? "-"}
</p>
<p>
<strong>Nome social:</strong> {patientDetails.social_name ?? "-"}
</p>
<p>
<strong>Sexo:</strong> {patientDetails.sex ?? "-"}
</p>
<p>
<strong>Tipo sanguíneo:</strong> {patientDetails.blood_type ?? "-"}
</p>
<p>
<strong>Peso:</strong> {patientDetails.weight_kg ?? "-"}
{patientDetails.weight_kg ? "kg" : ""}
</p>
<p>
<strong>Altura:</strong> {patientDetails.height_m ?? "-"}
{patientDetails.height_m ? "m" : ""}
</p>
<p>
<strong>IMC:</strong> {patientDetails.bmi ?? "-"}
</p>
<p>
<strong>Endereço:</strong> {patientDetails.street ?? "-"}
</p>
<p>
<strong>Bairro:</strong> {patientDetails.neighborhood ?? "-"}
</p>
<p>
<strong>Cidade:</strong> {patientDetails.city ?? "-"}
</p>
<p>
<strong>Estado:</strong> {patientDetails.state ?? "-"}
</p>
<p>
<strong>CEP:</strong> {patientDetails.cep ?? "-"}
</p>
{/* 📅 PASSO 3: Aplicar a formatação de data no modal */}
<p>
<strong>Criado em:</strong> {formatDate(patientDetails.created_at)}
</p>
<p>
<strong>Atualizado em:</strong> {formatDate(patientDetails.updated_at)}
</p>
<p>
<strong>Id:</strong> {patientDetails.id ?? "-"}
</p>
</div> </div>
)} )}
</AlertDialogDescription> </AlertDialogDescription>

View File

@ -210,43 +210,56 @@ export default function UsersPage() {
{/* Filtro e Itens por Página */} {/* Filtro e Itens por Página */}
<div className="flex flex-wrap items-center gap-3 bg-white p-4 rounded-lg border border-gray-200"> <div className="flex flex-wrap items-center gap-3 bg-white p-4 rounded-lg border border-gray-200">
<Filter className="w-5 h-5 text-gray-400" />
{/* Select de Filtro por Papel - Ajustado para resetar a página */} {/* Select de Filtro por Papel - Ajustado para resetar a página */}
<Select <div className="flex items-center gap-2 w-full md:w-auto">
onValueChange={(value) => { <span className="text-sm font-medium text-foreground">
setSelectedRole(value); Filtrar por papel
setCurrentPage(1); // Resetar para a primeira página ao mudar o filtro </span>
}} <Select
value={selectedRole} onValueChange={(value) => {
> setSelectedRole(value);
<SelectTrigger className="w-[180px]"> setCurrentPage(1); // Resetar para a primeira página ao mudar o filtro
<SelectValue placeholder="Filtrar por papel" /> }}
</SelectTrigger> value={selectedRole}>
<SelectContent>
<SelectItem value="all">Todos</SelectItem> <SelectTrigger className="w-[180px]">
<SelectItem value="admin">Admin</SelectItem> <SelectValue placeholder="Filtrar por papel" />
<SelectItem value="gestor">Gestor</SelectItem> </SelectTrigger>
<SelectItem value="medico">Médico</SelectItem> <SelectContent>
<SelectItem value="secretaria">Secretária</SelectItem> <SelectItem value="all">Todos</SelectItem>
<SelectItem value="user">Usuário</SelectItem> <SelectItem value="admin">Admin</SelectItem>
</SelectContent> <SelectItem value="gestor">Gestor</SelectItem>
</Select> <SelectItem value="medico">Médico</SelectItem>
<SelectItem value="secretaria">Secretária</SelectItem>
<SelectItem value="user">Usuário</SelectItem>
</SelectContent>
</Select>
</div>
{/* Select de Itens por Página */} {/* Select de Itens por Página */}
<Select <div className="flex items-center gap-2 w-full md:w-auto">
onValueChange={handleItemsPerPageChange} <span className="text-sm font-medium text-foreground">
defaultValue={String(itemsPerPage)} Itens por página
> </span>
<SelectTrigger className="w-[140px]"> <Select
<SelectValue placeholder="Itens por pág." /> onValueChange={handleItemsPerPageChange}
</SelectTrigger> defaultValue={String(itemsPerPage)}
<SelectContent> >
<SelectItem value="5">5 por página</SelectItem> <SelectTrigger className="w-[140px]">
<SelectItem value="10">10 por página</SelectItem> <SelectValue placeholder="Itens por pág." />
<SelectItem value="20">20 por página</SelectItem> </SelectTrigger>
</SelectContent> <SelectContent>
</Select> <SelectItem value="5">5 por página</SelectItem>
<SelectItem value="10">10 por página</SelectItem>
<SelectItem value="20">20 por página</SelectItem>
</SelectContent>
</Select>
</div>
<Button variant="outline" className="ml-auto w-full md:w-auto">
<Filter className="w-4 h-4 mr-2" />
Filtro avançado
</Button>
</div> </div>
{/* Fim do Filtro e Itens por Página */} {/* Fim do Filtro e Itens por Página */}
@ -331,11 +344,10 @@ export default function UsersPage() {
<button <button
key={number} key={number}
onClick={() => paginate(number)} onClick={() => paginate(number)}
className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-gray-300 ${ className={`px-4 py-2 rounded-md font-medium transition-colors text-sm border border-gray-300 ${currentPage === number
currentPage === number ? "bg-green-600 text-white shadow-md border-green-600"
? "bg-green-600 text-white shadow-md border-green-600" : "bg-gray-100 text-gray-700 hover:bg-gray-200"
: "bg-gray-100 text-gray-700 hover:bg-gray-200" }`}
}`}
> >
{number} {number}
</button> </button>