import React, { useState, useEffect, useCallback } from 'react'; import { Plus, Activity, ArrowRight, ArrowLeft, X, AlertTriangle, Info, ShieldAlert, Camera, Upload, CheckCircle, Clock, Stethoscope, FileText, Heart, Sparkles, ChevronDown, ChevronUp, User, Calendar, Percent, Scissors, RotateCcw, Wrench, ShieldCheck, Droplets, FileDown, ClipboardCheck, AlertCircle, Brush, Crosshair } from 'lucide-react'; import { HybridBackend } from '../services/backend.ts'; import { TratamentoOrto, EvolucaoOrto, TipoEventoOrto, CooperacaoOrto } from '../types.ts'; import { useHybridBackend } from '../hooks/useHybridBackend.ts'; import { useToast } from '../contexts/ToastContext.tsx'; import { PageHeader } from '../components/PageHeader.tsx'; /* ================================================================ HELPER: calcula idade a partir de data de nascimento ================================================================ */ function calcAge(dob?: string): number | null { if (!dob) return null; const birth = new Date(dob); const today = new Date(); let age = today.getFullYear() - birth.getFullYear(); const m = today.getMonth() - birth.getMonth(); if (m < 0 || (m === 0 && today.getDate() < birth.getDate())) age--; return age; } function tipoPacienteLabel(age: number | null): string { if (age === null) return ''; if (age < 10) return 'INFANTIL'; if (age <= 17) return 'ADOLESCENTE'; return 'ADULTO'; } const COOPERATION_COLORS: Record = { 'Boa': 'bg-green-100 text-green-700', 'Média': 'bg-amber-100 text-amber-700', 'Ruim': 'bg-red-100 text-red-700', }; const FLAG_STYLES: Record = { 'danger': { bg: 'bg-red-50 border-red-200', text: 'text-red-700', icon: }, 'warning': { bg: 'bg-amber-50 border-amber-200', text: 'text-amber-700', icon: }, 'info': { bg: 'bg-blue-50 border-blue-200', text: 'text-blue-600', icon: }, }; const PROCEDURE_BUTTONS = [ { label: 'TROCA FIO', icon: }, { label: 'ELÁSTICO', icon: }, { label: 'IPR', icon: }, { label: 'RECOLAGEM', icon: }, { label: 'AJUSTE TORQUE', icon: }, { label: 'PROFILAXIA', icon: }, ]; /* ================================================================ SECTION 1 – PATIENT HEADER (Dashboard fixo) ================================================================ */ const PatientHeader: React.FC<{ t: TratamentoOrto }> = ({ t }) => { const age = calcAge(t.dataNascimento); const tipoLabel = tipoPacienteLabel(age); return (
{t.pacienteNome.substring(0, 2)}

{t.pacienteNome} {age !== null && — {age} ANOS}

{tipoLabel && {tipoLabel}} {t.classificacao || t.diagnostico?.classeAngle} FASE: {t.faseAtual} FIO {t.fioAtual} {t.cooperacao && COOPERAÇÃO {t.cooperacao.toUpperCase()}}
ÚLTIMA: {t.ultimaConsulta ? new Date(t.ultimaConsulta).toLocaleDateString() : '—'}
PRÓX: {t.proximaConsulta ? new Date(t.proximaConsulta).toLocaleDateString() : '—'}
{t.mesesRestantes ?? '?'} MESES RESTANTES
{/* Progress bar */} {t.percentualConcluido != null && (
PROGRESSO DO TRATAMENTO {t.percentualConcluido}%
)} {/* Flags */} {t.flags && t.flags.length > 0 && (
{t.flags.map((f, i) => { const s = FLAG_STYLES[f.tipo] || FLAG_STYLES.info; return (
{s.icon} {f.mensagem}
); })}
)} {t.responsavel &&

RESPONSÁVEL: {t.responsavel}

}
); }; /* ================================================================ SECTION 2 – TRIAGEM CLÍNICA ================================================================ */ const ClinicalTriage: React.FC<{ t: TratamentoOrto }> = ({ t }) => { const [open, setOpen] = useState(false); const tr = t.triagem; if (!tr) return null; const age = calcAge(t.dataNascimento); const isAdult = age !== null && age >= 18; return (
{open && (
{isAdult && ( <>
CAMPOS ADULTO
)}
)}
); }; /* ================================================================ SECTION 3 – DIAGNÓSTICO ================================================================ */ const Diagnosis: React.FC<{ t: TratamentoOrto }> = ({ t }) => { const [open, setOpen] = useState(false); const d = t.diagnostico; if (!d) return null; return (
{open && (
)}
); }; /* ================================================================ SECTION 4 – PLANO DE TRATAMENTO ================================================================ */ const TreatmentPlan: React.FC<{ t: TratamentoOrto }> = ({ t }) => { const [open, setOpen] = useState(true); return (
{open && (
{t.valor != null && } {t.formaPagamento && }
{t.sequenciaFios && (
SEQUÊNCIA DE FIOS

{t.sequenciaFios}

)}
)}
); }; /* ================================================================ SECTION 5 – TIMELINE INTELIGENTE ================================================================ */ const Timeline: React.FC<{ evolucoes: EvolucaoOrto[] }> = ({ evolucoes }) => { const sorted = [...evolucoes].sort((a, b) => new Date(b.data).getTime() - new Date(a.data).getTime()); return (

LINHA DO TEMPO — {evolucoes.length} EVENTO{evolucoes.length !== 1 && 'S'}

{sorted.map((ev) => (
{new Date(ev.data).toLocaleDateString()} {ev.tipo}
PRÓX: {new Date(ev.proximoRetorno).toLocaleDateString()}

{ev.procedimento}

{ev.fio && FIO: {ev.fio}} {ev.elastico && ELÁSTICO: {ev.elastico}} {ev.procedimentos?.map((p, i) => {p})} {ev.higiene != null && ( = 7 ? 'bg-green-50 text-green-600' : ev.higiene >= 5 ? 'bg-amber-50 text-amber-600' : 'bg-red-50 text-red-600'}`}> HIGIENE: {ev.higiene}/10 )} {ev.cooperacao && COOP: {ev.cooperacao.toUpperCase()}}
{ev.observacoes &&

{ev.observacoes}

}
))}
); }; /* ================================================================ SECTION 6 – NOVA EVOLUÇÃO ================================================================ */ const NewEvolution: React.FC<{ tratamentoId: string; currentFio?: string; onSaved: () => void }> = ({ tratamentoId, currentFio, onSaved }) => { const toast = useToast(); const [tipo, setTipo] = useState('Manutenção'); const [fio, setFio] = useState(currentFio || ''); const [elastico, setElastico] = useState(''); const [selectedProcs, setSelectedProcs] = useState([]); const [higiene, setHigiene] = useState(7); const [cooperacao, setCooperacao] = useState('Boa'); const [observacoes, setObservacoes] = useState(''); const [proximoRetorno, setProximoRetorno] = useState(''); const toggleProc = (label: string) => { setSelectedProcs(prev => prev.includes(label) ? prev.filter(p => p !== label) : [...prev, label]); }; const handleSave = async () => { const procSummary = [tipo, fio && `FIO ${fio}`, elastico && `ELÁSTICO ${elastico}`, ...selectedProcs].filter(Boolean).join(' + '); try { await HybridBackend.saveEvolucaoOrto(tratamentoId, { tipo, procedimento: procSummary.toUpperCase(), proximoRetorno, fio: fio || undefined, elastico: elastico || undefined, procedimentos: selectedProcs.length > 0 ? selectedProcs : undefined, higiene, cooperacao, observacoes: observacoes.toUpperCase() || undefined, }); toast.success('EVOLUÇÃO SALVA COM SUCESSO!'); // Reset setTipo('Manutenção'); setElastico(''); setSelectedProcs([]); setHigiene(7); setCooperacao('Boa'); setObservacoes(''); setProximoRetorno(''); onSaved(); } catch { toast.error('ERRO AO SALVAR EVOLUÇÃO.'); } }; return (

NOVA EVOLUÇÃO

{/* Procedure quick buttons */}
{PROCEDURE_BUTTONS.map(pb => ( ))}
setHigiene(Number(e.target.value))} className="w-full mt-2" />
{higiene}/10
setProximoRetorno(e.target.value)} className="w-full border border-gray-300 rounded-lg p-2.5 bg-white mt-1" />