"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" 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); console.log(res) 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(); console.log(res) const mapped = 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 ?? "", // se não existir, fica vazio 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); // 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(() => { 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 */}
Convênio
VIP
Aniversariantes
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.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: {patientDetails.created_at ?? "-"}

Atualizado em: {patientDetails.updated_at ?? "-"}

)}
Fechar
); }