"use client"; import { useState, useEffect, useRef, useCallback } from "react"; import Link from "next/link"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Plus, Edit, Trash2, Eye, Calendar, Filter } from "lucide-react"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; import SecretaryLayout from "@/components/secretary-layout"; export default function PacientesPage() { const [searchTerm, setSearchTerm] = useState(""); const [convenioFilter, setConvenioFilter] = useState("all"); const [vipFilter, setVipFilter] = useState("all"); const [patients, setPatients] = useState([]); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [page, setPage] = useState(1); const [hasNext, setHasNext] = useState(true); const [isFetching, setIsFetching] = useState(false); const observerRef = useRef(null); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [patientToDelete, setPatientToDelete] = useState(null); const [detailsDialogOpen, setDetailsDialogOpen] = useState(false); const [patientDetails, setPatientDetails] = useState(null); const openDetailsDialog = async (patientId: string) => { setDetailsDialogOpen(true); setPatientDetails(null); try { const res = await fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes/${patientId}`); if (!res.ok) throw new Error(`HTTP ${res.status}`); const json = await res.json(); setPatientDetails(json?.data ?? null); } catch (e: any) { setPatientDetails({ error: e?.message || "Erro ao buscar detalhes" }); } }; const fetchPacientes = useCallback( async (pageToFetch: number) => { if (isFetching || !hasNext) return; setIsFetching(true); setError(null); try { const res = await fetch(`https://mock.apidog.com/m1/1053378-0-default/pacientes?page=${pageToFetch}&limit=20`); if (!res.ok) throw new Error(`HTTP ${res.status}`); const json = await res.json(); const items = Array.isArray(json?.data) ? json.data : []; const mapped = items.map((p: any) => ({ id: String(p.id ?? ""), nome: p.nome ?? "", telefone: p?.contato?.celular ?? p?.contato?.telefone1 ?? p?.telefone ?? "", cidade: p?.endereco?.cidade ?? p?.cidade ?? "", estado: p?.endereco?.estado ?? p?.estado ?? "", ultimoAtendimento: p.ultimo_atendimento ?? p.ultimoAtendimento ?? undefined, proximoAtendimento: p.proximo_atendimento ?? p.proximoAtendimento ?? undefined, convenio: p.convenio ?? "", vip: Boolean(p.vip ?? false), status: p.status ?? undefined, })); setPatients((prev) => [...prev, ...mapped]); setHasNext(Boolean(json?.pagination?.has_next)); setPage(pageToFetch + 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(() => { if (!observerRef.current || !hasNext) return; const observer = new window.IntersectionObserver((entries) => { 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 = (patientId: string) => { // Remove from current list (client-side deletion) setPatients((prev) => prev.filter((p) => String(p.id) !== String(patientId))); setDeleteDialogOpen(false); setPatientToDelete(null); }; const openDeleteDialog = (patientId: string) => { setPatientToDelete(patientId); setDeleteDialogOpen(true); }; const filteredPatients = patients.filter((patient) => { const matchesSearch = patient.nome?.toLowerCase().includes(searchTerm.toLowerCase()) || patient.telefone?.includes(searchTerm); const matchesConvenio = convenioFilter === "all" || (patient.convenio ?? "") === convenioFilter; const matchesVip = vipFilter === "all" || (vipFilter === "vip" && patient.vip) || (vipFilter === "regular" && !patient.vip); return matchesSearch && matchesConvenio && matchesVip; }); return (

Pacientes

Gerencie as informações de seus pacientes

Convênio
VIP
Aniversariantes
{error ? (
{`Erro ao carregar pacientes: ${error}`}
) : ( {filteredPatients.length === 0 ? ( ) : ( filteredPatients.map((patient) => ( )) )}
Nome Telefone Cidade Estado Último atendimento Próximo atendimento Ações
{patients.length === 0 ? "Nenhum paciente cadastrado" : "Nenhum paciente encontrado com os filtros aplicados"}
{patient.nome?.charAt(0) || "?"}
{patient.nome}
{patient.telefone} {patient.cidade} {patient.estado} {patient.ultimoAtendimento} {patient.proximoAtendimento}
Ações
openDetailsDialog(String(patient.id))}> Ver detalhes Editar Marcar consulta openDeleteDialog(String(patient.id))}> Excluir
)}
{isFetching &&
Carregando mais pacientes...
}
Confirmar exclusão Tem certeza que deseja excluir este paciente? Esta ação não pode ser desfeita. Cancelar patientToDelete && handleDeletePatient(patientToDelete)} className="bg-red-600 hover:bg-red-700"> Excluir {/* Modal de detalhes do paciente */} Detalhes do Paciente {patientDetails === null ? (
Carregando...
) : patientDetails?.error ? (
{patientDetails.error}
) : (
Nome: {patientDetails.nome}
Telefone: {patientDetails?.contato?.celular ?? patientDetails?.contato?.telefone1 ?? patientDetails?.telefone ?? ""}
Cidade: {patientDetails?.endereco?.cidade ?? patientDetails?.cidade ?? ""}
Estado: {patientDetails?.endereco?.estado ?? patientDetails?.estado ?? ""}
Convênio: {patientDetails.convenio ?? ""}
VIP: {patientDetails.vip ? "Sim" : "Não"}
Status: {patientDetails.status ?? ""}
Último atendimento: {patientDetails.ultimo_atendimento ?? patientDetails.ultimoAtendimento ?? ""}
Próximo atendimento: {patientDetails.proximo_atendimento ?? patientDetails.proximoAtendimento ?? ""}
)}
Fechar
); }