"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"; import { patientsService } from "@/services/patientsApi.mjs"; // --- INÍCIO DA CORREÇÃO --- interface Patient { id: string; nome: string; telefone: string; cidade: string; estado: string; ultimoAtendimento: string; proximoAtendimento: string; vip: boolean; convenio: string; status?: string; // Propriedades detalhadas para o modal full_name?: string; cpf?: string; email?: string; phone_mobile?: string; phone1?: string; phone2?: string; social_name?: string; sex?: string; blood_type?: string; weight_kg?: number; height_m?: number; bmi?: number; street?: string; neighborhood?: string; city?: string; // <-- Adicionado state?: string; // <-- Adicionado cep?: string; created_at?: string; updated_at?: string; } // --- FIM DA CORREÇÃO --- // Função para formatar a data const formatDate = (dateString: string | null | undefined): string => { if (!dateString) { return "N/A"; } try { const date = new Date(dateString); if (isNaN(date.getTime())) { return "Data inválida"; } const day = String(date.getDate()).padStart(2, '0'); const month = String(date.getMonth() + 1).padStart(2, '0'); const year = date.getFullYear(); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); return `${day}/${month}/${year} ${hours}:${minutes}`; } catch (error) { return "Data inválida"; } }; 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 patientsService.getById(patientId); setPatientDetails(res[0]); } 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 patientsService.list(); const mapped: Patient[] = res.map((p: any) => ({ id: String(p.id ?? ""), nome: p.full_name ?? "", telefone: p.phone_mobile ?? p.phone1 ?? "", cidade: p.city ?? "", estado: p.state ?? "", ultimoAtendimento: p.last_visit_at ?? "", proximoAtendimento: p.next_appointment_at ?? "", vip: Boolean(p.vip ?? false), convenio: p.convenio ?? "", status: p.status ?? undefined, })); setPatients((prev) => { const all = [...prev, ...mapped]; const unique = Array.from(new Map(all.map((p) => [p.id, p])).values()); return unique; }); if (mapped.length === 0) { setHasNext(false); } else { setPage((prev) => prev + 1); } } catch (e: any) { setError(e?.message || "Erro ao buscar pacientes"); } finally { setIsFetching(false); } }, [isFetching, hasNext] ); useEffect(() => { fetchPacientes(1); // 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 = async (patientId: string) => { try { await patientsService.delete(patientId); setPatients((prev) => prev.filter((p) => p.id !== patientId)); } catch (e: any) { setError(e?.message || "Erro ao deletar paciente"); alert("Erro ao deletar paciente."); } 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 && !isFetching ? ( ) : ( 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} {formatDate(patient.ultimoAtendimento)} {formatDate(patient.proximoAtendimento)} openDetailsDialog(patient.id)}> Ver detalhes Editar Marcar consulta openDeleteDialog(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 Detalhes do Paciente {patientDetails === null ? (
Carregando...
) : 'error' in patientDetails ? (
{patientDetails.error}
) : (

Nome: {patientDetails.full_name}

CPF: {patientDetails.cpf || "-"}

Email: {patientDetails.email || "-"}

Telefone: {patientDetails.phone_mobile ?? patientDetails.phone1 ?? patientDetails.phone2 ?? "-"}

Nome social: {patientDetails.social_name ?? "-"}

Sexo: {patientDetails.sex ?? "-"}

Tipo sanguíneo: {patientDetails.blood_type ?? "-"}

Peso: {patientDetails.weight_kg ? `${patientDetails.weight_kg}kg` : "-"}

Altura: {patientDetails.height_m ? `${patientDetails.height_m}m` : "-"}

IMC: {patientDetails.bmi ?? "-"}

Endereço: {patientDetails.street ?? "-"}

Bairro: {patientDetails.neighborhood ?? "-"}

Cidade: {patientDetails.city ?? "-"}

Estado: {patientDetails.state ?? "-"}

CEP: {patientDetails.cep ?? "-"}

Criado em: {formatDate(patientDetails.created_at)}

Atualizado em: {formatDate(patientDetails.updated_at)}

Id: {patientDetails.id ?? "-"}

)}
Fechar
); }