"use client"; import { useEffect, useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { MoreHorizontal, Plus, Search, Eye, Edit, Trash2, ArrowLeft, ShieldCheck } from "lucide-react"; import { Paciente, listarPacientes, buscarPacientes, buscarPacientePorId, excluirPaciente, listarAutorizacoesUsuario, atualizarAutorizacoesUsuario, type AuthorizationRole, } from "@/lib/api"; import { PatientRegistrationForm } from "@/components/forms/patient-registration-form"; import { getCurrentUser, atualizarPaciente } from "@/lib/api"; import { UpdateAuthorizationsDialog, type AuthorizationState, } from "@/components/dialogs/update-authorizations-dialog"; import { useToast } from "@/hooks/use-toast"; function normalizePaciente(p: any): Paciente { return { id: String(p.id ?? p.uuid ?? p.paciente_id ?? ""), full_name: p.full_name ?? p.name ?? p.nome ?? "", social_name: p.social_name ?? p.nome_social ?? null, cpf: p.cpf ?? "", rg: p.rg ?? p.document_number ?? null, sex: p.sex ?? p.sexo ?? null, birth_date: p.birth_date ?? p.data_nascimento ?? null, phone_mobile: p.phone_mobile ?? p.telefone ?? "", email: p.email ?? "", cep: p.cep ?? "", street: p.street ?? p.logradouro ?? "", number: p.number ?? p.numero ?? "", complement: p.complement ?? p.complemento ?? "", neighborhood: p.neighborhood ?? p.bairro ?? "", city: p.city ?? p.cidade ?? "", state: p.state ?? p.estado ?? "", notes: p.notes ?? p.observacoes ?? null, user_id: p.user_id ?? p.usuario_id ?? p.userId ?? null, }; } export default function PacientesPage() { const [patients, setPatients] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [search, setSearch] = useState(""); const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState(null); const [viewingPatient, setViewingPatient] = useState(null); const [authDialogOpen, setAuthDialogOpen] = useState(false); const [authTargetPatient, setAuthTargetPatient] = useState(null); const [authInitialRoles, setAuthInitialRoles] = useState(null); const [authorizationsLoading, setAuthorizationsLoading] = useState(false); const [authorizationsError, setAuthorizationsError] = useState(null); const [authorizationsSubmitDisabled, setAuthorizationsSubmitDisabled] = useState(false); const { toast } = useToast(); async function loadAll() { try { setLoading(true); const data = await listarPacientes({ page: 1, limit: 20 }); if (Array.isArray(data)) { setPatients(data.map(normalizePaciente)); } else { setPatients([]); } setError(null); } catch (e: any) { setPatients([]); setError(e?.message || "Erro ao carregar pacientes."); } finally { setLoading(false); } } useEffect(() => { loadAll(); }, []); const filtered = useMemo(() => { if (!search.trim()) return patients; const q = search.toLowerCase().trim(); const qDigits = q.replace(/\D/g, ""); return patients.filter((p) => { // Busca por nome const byName = (p.full_name || "").toLowerCase().includes(q); // Busca por CPF (remove formatação) const byCPF = qDigits.length >= 3 && (p.cpf || "").replace(/\D/g, "").includes(qDigits); // Busca por ID (UUID completo ou parcial) const byId = (p.id || "").toLowerCase().includes(q); // Busca por email const byEmail = (p.email || "").toLowerCase().includes(q); return byName || byCPF || byId || byEmail; }); }, [patients, search]); function handleAdd() { setEditingId(null); setShowForm(true); } function handleEdit(id: string) { setEditingId(id); setShowForm(true); } function handleView(patient: Paciente) { setViewingPatient(patient); } async function handleOpenAuthorizations(patient: Paciente) { setAuthTargetPatient(patient); setAuthDialogOpen(true); setAuthorizationsLoading(!!patient.user_id); setAuthorizationsError(null); setAuthInitialRoles(null); setAuthorizationsSubmitDisabled(false); if (!patient.user_id) { setAuthorizationsError( "Este paciente ainda não possui um usuário vinculado. Cadastre ou vincule um usuário para gerenciar autorizações.", ); setAuthInitialRoles({ paciente: true, medico: false }); setAuthorizationsSubmitDisabled(true); return; } try { const roles = await listarAutorizacoesUsuario(patient.user_id); if (!roles.length) { setAuthInitialRoles({ paciente: true, medico: false }); } else { setAuthInitialRoles({ paciente: roles.includes("paciente"), medico: roles.includes("medico"), }); } } catch (e: any) { setAuthorizationsError(e?.message || "Erro ao carregar autorizações."); } finally { setAuthorizationsLoading(false); } } function handleAuthDialogOpenChange(open: boolean) { if (!open) { setAuthDialogOpen(false); setAuthTargetPatient(null); setAuthInitialRoles(null); setAuthorizationsError(null); setAuthorizationsLoading(false); setAuthorizationsSubmitDisabled(false); } } async function handleConfirmAuthorizations(selection: AuthorizationState) { if (!authTargetPatient?.user_id) { toast({ title: "Usuário não vinculado", description: "Não foi possível atualizar as autorizações porque o usuário não está vinculado.", variant: "destructive", }); setAuthorizationsError( "Vincule este paciente a um usuário antes de ajustar as autorizações.", ); setAuthorizationsSubmitDisabled(true); return; } console.log("[Auth] Confirm clicked", selection, "targetUserId=", authTargetPatient?.user_id); setAuthorizationsLoading(true); setAuthorizationsError(null); const selectedRoles: AuthorizationRole[] = []; if (selection.paciente) selectedRoles.push("paciente"); if (selection.medico) selectedRoles.push("medico"); try { console.log("[Auth] Updating roles to server:", selectedRoles); const result = await atualizarAutorizacoesUsuario(authTargetPatient.user_id, selectedRoles); console.log("[Auth] Update result:", result); toast({ title: "Autorizações atualizadas", description: "As permissões deste paciente foram atualizadas com sucesso.", }); setAuthDialogOpen(false); setAuthTargetPatient(null); setAuthInitialRoles(null); await loadAll(); } catch (e: any) { toast({ title: "Erro ao atualizar autorizações", description: e?.message || "Não foi possível atualizar as autorizações.", variant: "destructive", }); } finally { setAuthorizationsLoading(false); } } async function handleDelete(id: string) { if (!confirm("Excluir este paciente?")) return; try { await excluirPaciente(id); setPatients((prev) => prev.filter((x) => String(x.id) !== String(id))); } catch (e: any) { alert(e?.message || "Não foi possível excluir."); } } async function handleSaved(p: Paciente) { // Normaliza e atualiza localmente let saved = normalizePaciente(p); // Vincula o registro de paciente ao usuário autenticado try { const user = await getCurrentUser(); // Preparar payload apenas com campos de PacienteInput e user_id const { id: _id, user_id: _oldUserId, ...rest } = saved; const payload = { ...rest, user_id: user.id }; const linked = await atualizarPaciente(saved.id, payload); saved = normalizePaciente(linked); } catch (e) { // Se falhar, mantém saved original console.warn("Falha ao vincular usuário ao paciente:", e); } // Atualiza lista com o registro vinculado setPatients((prev) => { const i = prev.findIndex((x) => String(x.id) === String(saved.id)); if (i < 0) return [saved, ...prev]; const clone = [...prev]; clone[i] = saved; return clone; }); setShowForm(false); } async function handleBuscarServidor() { const q = search.trim(); if (!q) return loadAll(); try { setLoading(true); setError(null); // Se parece com ID (UUID), busca diretamente if (q.includes('-') && q.length > 10) { const one = await buscarPacientePorId(q); setPatients(one ? [normalizePaciente(one)] : []); setError(one ? null : "Paciente não encontrado."); // Limpa o campo de busca para que o filtro não interfira setSearch(""); return; } // Para outros termos, usa busca avançada const results = await buscarPacientes(q); setPatients(results.map(normalizePaciente)); setError(results.length === 0 ? "Nenhum paciente encontrado." : null); // Limpa o campo de busca para que o filtro não interfira setSearch(""); } catch (e: any) { setPatients([]); setError(e?.message || "Erro na busca."); } finally { setLoading(false); } } function handleBuscarClick() { void handleBuscarServidor(); } if (loading) return

Carregando pacientes...

; if (error) return

{error}

; if (showForm) { return (

{editingId ? "Editar paciente" : "Novo paciente"}

setShowForm(false)} />
); } console.log( '[Page] Rendering dialog, passing onConfirm. Typeof handleConfirmAuthorizations is:', typeof handleConfirmAuthorizations, ); return (

Pacientes

Gerencie os pacientes

setSearch(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleBuscarServidor()} />
Nome CPF Telefone Cidade Estado Ações {filtered.length > 0 ? ( filtered.map((p) => ( {p.full_name || "(sem nome)"} {p.cpf || "-"} {p.phone_mobile || "-"} {p.city || "-"} {p.state || "-"} handleView(p)}> Ver handleEdit(String(p.id))}> Editar handleOpenAuthorizations(p)}> Atualizar autorizações handleDelete(String(p.id))} className="text-destructive"> Excluir )) ) : ( Nenhum paciente encontrado )}
Mostrando {filtered.length} de {patients.length}
); }