import React, { useState, useEffect } from "react"; import { Users, UserPlus, Search, Edit, Trash2, Phone, Mail, MapPin, FileText, Activity, } from "lucide-react"; import { listPatients, createPatient, updatePatient, deletePatient, } from "../services/pacienteService"; import userService from "../services/userService"; import toast from "react-hot-toast"; import { format } from "date-fns"; // import { ptBR } from 'date-fns/locale' // Removido, não utilizado interface Paciente { _id: string; nome: string; cpf?: string; telefone?: string; email?: string; dataNascimento?: string; altura?: number; peso?: number; endereco?: { rua?: string; numero?: string; bairro?: string; cidade?: string; cep?: string; }; convenio?: string; numeroCarteirinha?: string; observacoes?: string | null; ativo?: boolean; criadoEm?: string; } const CadastroSecretaria: React.FC = () => { const [pacientes, setPacientes] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(""); const [showForm, setShowForm] = useState(false); const [editingPaciente, setEditingPaciente] = useState(null); const [formData, setFormData] = useState({ nome: "", cpf: "", telefone: "", email: "", dataNascimento: "", altura: "", peso: "", endereco: { rua: "", numero: "", bairro: "", cidade: "", cep: "", }, convenio: "", numeroCarteirinha: "", observacoes: "", }); // Função para carregar pacientes const carregarPacientes = async () => { try { setLoading(true); const pacientesApi = await listPatients(); setPacientes( pacientesApi.data.map((p) => ({ _id: p.id, nome: p.nome, cpf: p.cpf, telefone: p.telefone, email: p.email, dataNascimento: p.dataNascimento, altura: p.alturaM ? Math.round(p.alturaM * 100) : undefined, peso: p.pesoKg, endereco: { rua: p.endereco?.rua, numero: p.endereco?.numero, bairro: p.endereco?.bairro, cidade: p.endereco?.cidade, cep: p.endereco?.cep, }, convenio: p.convenio, numeroCarteirinha: p.numeroCarteirinha, observacoes: p.observacoes || undefined, criadoEm: p.created_at, })) ); } catch (error) { console.error("Erro ao carregar pacientes:", error); toast.error("Erro ao carregar lista de pacientes"); } finally { setLoading(false); } }; useEffect(() => { carregarPacientes(); }, []); const calcularIMC = (altura?: number, peso?: number) => { if (!altura || !peso) return null; const alturaMetros = altura / 100; const imc = peso / (alturaMetros * alturaMetros); return imc.toFixed(1); }; const getIMCStatus = (imc: number) => { if (imc < 18.5) return { status: "Abaixo do peso", color: "text-blue-600" }; if (imc < 25) return { status: "Peso normal", color: "text-green-600" }; if (imc < 30) return { status: "Sobrepeso", color: "text-yellow-600" }; return { status: "Obesidade", color: "text-red-600" }; }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); try { setLoading(true); // NOTE: remote CPF validation removed to avoid false negatives // NOTE: remote CEP validation removed to avoid false negatives const pacienteData = { ...formData, altura: formData.altura ? parseFloat(formData.altura) : undefined, peso: formData.peso ? parseFloat(formData.peso) : undefined, ativo: true, criadoPor: "secretaria", criadoEm: new Date().toISOString(), atualizadoEm: new Date().toISOString(), }; if (editingPaciente) { await updatePatient(editingPaciente._id, pacienteData); toast.success("Paciente atualizado com sucesso!"); } else { await createPatient(pacienteData); toast.success("Paciente cadastrado com sucesso!"); } // (Refactor) Criação de secretária via fluxo real se condição atender (mantendo lógica anterior condicional) // OBS: Este bloco antes criava secretária mock ao cadastrar um novo paciente. // Caso essa associação não faça sentido de negócio, remover todo o bloco abaixo posteriormente. if (!editingPaciente && formData.email && formData.nome) { try { // Gera senha temporária segura simples; idealmente backend enviaria email de reset. const tempPassword = Math.random().toString(36).slice(-10) + "!A1"; const secResp = await userService.createSecretaria({ nome: formData.nome, email: formData.email, password: tempPassword, telefone: formData.telefone, }); if (secResp.success) { toast.success( "Secretária criada (fluxo real). Senha temporária gerada." ); console.info( "[CadastroSecretaria] Secretária criada: ", secResp.data?.id ); } else { // Não bloquear fluxo principal de paciente toast.error( "Falha ao criar secretária (fluxo real): " + (secResp.error || "erro desconhecido") ); } } catch (err) { console.warn("Falha inesperada ao criar secretária:", err); toast.error("Erro inesperado ao criar secretária"); } } // resetForm removido, não existe setEditingPaciente(null); setShowForm(false); } catch (error) { console.error("Erro ao salvar paciente:", error); toast.error("Erro ao salvar paciente. Tente novamente."); } finally { setLoading(false); } }; const handleEdit = (paciente: Paciente) => { setFormData({ nome: paciente.nome || "", cpf: paciente.cpf || "", telefone: paciente.telefone || "", email: paciente.email || "", dataNascimento: paciente.dataNascimento ? paciente.dataNascimento.split("T")[0] : "", altura: paciente.altura?.toString() || "", peso: paciente.peso?.toString() || "", endereco: { rua: paciente.endereco?.rua || "", numero: paciente.endereco?.numero || "", bairro: paciente.endereco?.bairro || "", cidade: paciente.endereco?.cidade || "", cep: paciente.endereco?.cep || "", }, convenio: paciente.convenio || "", numeroCarteirinha: paciente.numeroCarteirinha || "", observacoes: paciente.observacoes || "", }); setEditingPaciente(paciente); setShowForm(true); }; const handleDelete = async (pacienteId: string) => { if (window.confirm("Tem certeza que deseja excluir este paciente?")) { try { await deletePatient(pacienteId); toast.success("Paciente removido com sucesso!"); carregarPacientes(); } catch (error) { console.error("Erro ao remover paciente:", error); toast.error("Erro ao remover paciente"); } } }; const filteredPacientes = pacientes.filter( (paciente) => (paciente.nome || "").toLowerCase().includes(searchTerm.toLowerCase()) || (paciente.cpf || "").includes(searchTerm) || (paciente.telefone || "").includes(searchTerm) ); return (

Cadastro de Pacientes

Gerencie o cadastro de pacientes da clínica

{/* Estatísticas */}

Total de Pacientes

{pacientes.length}

Com Convênio

{ pacientes.filter( (p) => p.convenio && p.convenio !== "Particular" ).length }

Cadastros Hoje

{ pacientes.filter((p) => { const hoje = new Date().toISOString().split("T")[0]; return p.criadoEm?.startsWith(hoje); }).length }

Com Dados Físicos

{pacientes.filter((p) => p.altura && p.peso).length}

{/* Busca */}
setSearchTerm(e.target.value)} className="pl-10 form-input" />
{/* Lista de Pacientes */} {loading ? (
) : (
{filteredPacientes.map((paciente) => { const imc = calcularIMC(paciente.altura, paciente.peso); const imcStatus = imc ? getIMCStatus(parseFloat(imc)) : null; return ( ); })}
Paciente Contato Dados Físicos Convênio Ações
{paciente.nome || "Nome não informado"}
CPF: {paciente.cpf || "Não informado"}
Nascimento:{" "} {paciente.dataNascimento ? format( new Date(paciente.dataNascimento), "dd/MM/yyyy" ) : "Não informado"}
{paciente.telefone || "Não informado"}
{paciente.email || "Não informado"}
{paciente.endereco?.cidade || "Cidade não informada"}
{paciente.altura && (
Altura: {paciente.altura} cm
)} {paciente.peso && (
Peso: {paciente.peso} kg
)} {imc && imcStatus && (
IMC: {imc} ({imcStatus.status})
)} {!paciente.altura && !paciente.peso && (
Dados não informados
)}
{paciente.convenio || "Não informado"}
{paciente.numeroCarteirinha && (
Carteirinha: {paciente.numeroCarteirinha}
)}
)} {/* Modal de Formulário */} {showForm && (

{editingPaciente ? "Editar Paciente" : "Novo Paciente"}

{/* Nome */}
setFormData({ ...formData, nome: e.target.value }) } className="form-input" required />
{/* CPF com máscara */}
{ let v = e.target.value.replace(/\D/g, ""); if (v.length > 11) v = v.slice(0, 11); v = v.replace(/(\d{3})(\d)/, "$1.$2"); v = v.replace(/(\d{3})(\d)/, "$1.$2"); v = v.replace(/(\d{3})(\d{1,2})$/, "$1-$2"); setFormData({ ...formData, cpf: v }); }} className="form-input" placeholder="000.000.000-00" required />
{/* Telefone com máscara internacional */}
{ let v = e.target.value.replace(/\D/g, ""); if (v.length > 13) v = v.slice(0, 13); if (v.length >= 2) v = "+55 " + v; if (v.length >= 4) v = v.replace(/(\+55 )(\d{2})(\d)/, "$1$2 $3"); if (v.length >= 9) v = v.replace( /(\+55 \d{2} )(\d{5})(\d{4})/, "$1$2-$3" ); setFormData({ ...formData, telefone: v }); }} className="form-input" placeholder="+55 XX XXXXX-XXXX" required />
setFormData({ ...formData, email: e.target.value }) } className="form-input" required />
setFormData({ ...formData, dataNascimento: e.target.value, }) } className="form-input" required />
setFormData({ ...formData, altura: e.target.value }) } className="form-input" placeholder="Ex: 170" />
setFormData({ ...formData, peso: e.target.value }) } className="form-input" placeholder="Ex: 70.5" />
setFormData({ ...formData, endereco: { ...formData.endereco, cep: e.target.value, }, }) } className="form-input" />
setFormData({ ...formData, endereco: { ...formData.endereco, rua: e.target.value, }, }) } className="form-input" />
setFormData({ ...formData, endereco: { ...formData.endereco, numero: e.target.value, }, }) } className="form-input" />
setFormData({ ...formData, endereco: { ...formData.endereco, bairro: e.target.value, }, }) } className="form-input" />
setFormData({ ...formData, endereco: { ...formData.endereco, cidade: e.target.value, }, }) } className="form-input" />
setFormData({ ...formData, numeroCarteirinha: e.target.value, }) } className="form-input" />