import React, { useState, useEffect, useMemo } from 'react'; import { Users, Trash2, Check, Building2, AlertCircle, PlusCircle, Camera, Stethoscope, ChevronRight } from 'lucide-react'; import { PageHeader } from '../components/PageHeader.tsx'; import { HybridBackend } from '../services/backend.ts'; import { Paciente, Dentista, Procedimento, Especialidade } from '../types.ts'; import { useToast } from '../contexts/ToastContext.tsx'; import { create } from 'zustand'; // --- STORES --- interface GTOItem { id: string; procedimentoId: string; codigoTUSS: string; codigoInterno: string; descricao: string; dente?: string; face?: string; arco?: string; quantidade: number; valorUnitario: number; valorTotal: number; observacao?: string; } interface GTOStore { paciente: Paciente | null; dentista: Dentista | null; credenciada: string; items: GTOItem[]; setPaciente: (p: Paciente | null) => void; setDentista: (d: Dentista | null) => void; setCredenciada: (c: string) => void; addItem: (item: GTOItem) => void; removeItem: (id: string) => void; clear: () => void; } const useGTOStore = create((set) => ({ paciente: null, dentista: null, credenciada: 'CASSEMS - SEDE CENTRAL', items: [], setPaciente: (paciente) => set({ paciente }), setDentista: (dentista) => set({ dentista }), setCredenciada: (credenciada) => set({ credenciada }), addItem: (item) => set((state) => ({ items: [...state.items, item].slice(0, 20) })), removeItem: (id) => set((state) => ({ items: state.items.filter(i => i.id !== id) })), clear: () => set({ paciente: null, dentista: null, items: [] }), })); // --- ODONTOGRAM DATA (4x2 Grid Layout) --- const DENTES_ADULTO = { q1: [14, 13, 12, 11, 18, 17, 16, 15], // Top: 14-11, Bottom: 18-15 q2: [21, 22, 23, 24, 25, 26, 27, 28], // Top: 21-24, Bottom: 25-28 q4: [44, 43, 42, 41, 48, 47, 46, 45], // Top: 44-41, Bottom: 48-45 q3: [31, 32, 33, 34, 35, 36, 37, 38], // Top: 31-34, Bottom: 35-38 extras: { q1: 19, q2: 29, q4: 49, q3: 39 } }; const DENTES_CRIANCA = { // Balanced 4x2 distribution with nulls for spacing (matching image layout) q1: [null, 53, 52, 51, null, null, 55, 54], q2: [61, 62, 63, null, 64, 65, null, null], q4: [null, 43, 42, 41, null, null, 46, 45], q3: [71, 72, 73, null, 74, 75, null, null] }; // --- COMPONENT --- export const LancarGTO: React.FC = () => { const store = useGTOStore(); const toast = useToast(); const [pacientes, setPacientes] = useState([]); const [dentistas, setDentistas] = useState([]); const [especialidades, setEspecialidades] = useState([]); const [procedimentos, setProcedimentos] = useState([]); const [selectedEsp, setSelectedEsp] = useState(''); const [selectedProc, setSelectedProc] = useState(null); const [pacienteTipo, setPacienteTipo] = useState<'ADULTO' | 'CRIANCA'>('ADULTO'); const [selectedDentes, setSelectedDentes] = useState([]); const [selectedArco, setSelectedArco] = useState<'AI' | 'AS' | null>(null); const [face, setFace] = useState(''); const [obs, setObs] = useState(''); useEffect(() => { const load = async () => { const p = await HybridBackend.getPacientes(); const d = await HybridBackend.getDentistas(); const es = await HybridBackend.getEspecialidades(); const pr = await HybridBackend.getProcedimentos(); setPacientes(p); setDentistas(d); setEspecialidades(es); setProcedimentos(pr); }; load(); }, []); const filteredProcedimentos = useMemo(() => { if (!selectedEsp) return []; return procedimentos.filter(p => p.especialidadeId === selectedEsp); }, [selectedEsp, procedimentos]); const toggleDente = (num: string) => { if (selectedProc?.tipo_regiao !== 'DENTE') { toast.error("ESTE PROCEDIMENTO NÃO PERMITE SELEÇÃO DE DENTE INDIVIDUAL."); return; } setSelectedArco(null); setSelectedDentes(prev => prev.includes(num) ? prev.filter(d => d !== num) : [...prev, num] ); }; const selectArco = (arco: 'AI' | 'AS') => { if (selectedProc?.tipo_regiao !== 'ARCO') { toast.error("ESTE PROCEDIMENTO NÃO PERMITE SELEÇÃO DE ARCO."); return; } setSelectedDentes([]); setSelectedArco(arco); }; const handleAddItem = () => { if (!selectedProc) { toast.error("SELECIONE UM PROCEDIMENTO PRIMEIRO."); return; } // Validação de Região if (selectedProc.tipo_regiao === 'DENTE' && selectedDentes.length === 0) { toast.error("ESTE PROCEDIMENTO EXIGE A SELEÇÃO DE AO MENOS UM DENTE."); return; } if (selectedProc.tipo_regiao === 'ARCO' && !selectedArco) { toast.error("ESTE PROCEDIMENTO EXIGE A SELEÇÃO DE UM ARCO (AI/AS)."); return; } // Validação de Face if (selectedProc.exige_face && (!face || face.trim() === '')) { toast.error("A FACE É OBRIGATÓRIA PARA ESTE PROCEDIMENTO."); return; } const newItem: GTOItem = { id: Math.random().toString(36).substring(7), procedimentoId: selectedProc.id, codigoTUSS: selectedProc.codigo || '000000', codigoInterno: selectedProc.codigoInterno || '', descricao: selectedProc.nome, dente: selectedDentes.join(', '), arco: selectedArco || undefined, face: face, quantidade: 1, valorUnitario: selectedProc.valorParticular || 0, valorTotal: selectedProc.valorParticular || 0, observacao: obs, }; store.addItem(newItem); toast.success("ADICIONADO AO RASCUNHO."); // Reset selection fields setSelectedDentes([]); setSelectedArco(null); setFace(''); setObs(''); }; const handleFinalize = async () => { if (!store.paciente || !store.dentista || store.items.length === 0) { toast.error("PACIENTE, DENTISTA E PELO MENOS 1 ITEM SÃO OBRIGATÓRIOS."); return; } try { toast.success("GTO GERADA COM SUCESSO!"); store.clear(); setSelectedEsp(''); setSelectedProc(null); } catch { toast.error("ERRO AO GERAR GTO."); } }; const totalGeral = store.items.reduce((sum, i) => sum + i.valorTotal, 0); const renderDenteBtn = (num: number | string | null, keyIdx?: number) => { if (num === null) return
; const sStr = num.toString(); const isSelected = selectedDentes.includes(sStr); const isDisabled = !!selectedProc && selectedProc.tipo_regiao !== 'DENTE'; return ( ); }; return (
{/* LEFT PANEL: ODONTOGRAM & ITEMS */}
{/* ODONTOGRAM BOX */}
{/* Dynamic Odontogram Grid */}
{/* Visual Separators */}
{/* Q1 & Q2 (SUPERIOR) */}
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q1)}
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q1 : DENTES_CRIANCA.q1).map((d, i) => renderDenteBtn(d, i))}
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q2 : DENTES_CRIANCA.q2).map((d, i) => renderDenteBtn(d, i))}
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q2)}
{/* Q4 & Q3 (INFERIOR) */}
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q4)}
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q4 : DENTES_CRIANCA.q4).map((d, i) => renderDenteBtn(d, i))}
{(pacienteTipo === 'ADULTO' ? DENTES_ADULTO.q3 : DENTES_CRIANCA.q3).map((d, i) => renderDenteBtn(d, i))}
{pacienteTipo === 'ADULTO' && renderDenteBtn(DENTES_ADULTO.extras.q3)}
{/* RASCUNHO LIST */}
PROCEDIMENTOS DO RASCUNHO
{store.items.length}/20 ITENS
{store.items.length === 0 && (
NÃO HÁ ITENS ADICIONADOS
)} {store.items.map((item) => (
COD {item.codigoTUSS}

{item.descricao}

{store.paciente?.nome}
DENTE: {item.dente || '--'}
ARCO: {item.arco || '--'}
FACE: {item.face || '--'}
VALOR: R$ {item.valorTotal.toFixed(2)}
{item.observacao && (
OBS: {item.observacao}
)}
))}

TOTAL ACUMULADO

R$ {totalGeral.toFixed(2)}

{/* RIGHT PANEL: WORKFLOW SELECTORS */}
{/* STEP 1: PACIENTE */}
01. Paciente
{/* STEP 2: ESPECIALIDADE & PROCEDIMENTO */}
02. Especialidade
{selectedEsp && (
{selectedProc && (

REGRAS DE SELEÇÃO

{selectedProc.tipo_regiao === 'DENTE' ? 'EXIGE DENTE(S)' : selectedProc.tipo_regiao === 'ARCO' ? 'EXIGE ARCO (AI/AS)' : 'PROCEDIMENTO GERAL'} {selectedProc.exige_face && ( EXIGE FACE CLINICA )}
)}
)}
{/* STEP 3: DENTISTA */}
03. Dentista Executor
{/* STEP 4: DETALHES (FACE/OBS) */}
setFace(e.target.value.toUpperCase())} disabled={selectedProc?.tipo_regiao === 'GERAL' && !selectedProc?.exige_face} className={`w-full p-4 border-2 rounded-2xl text-sm font-black outline-none transition-all ${selectedProc?.exige_face ? 'bg-red-50 border-red-100 focus:border-red-400' : 'bg-gray-50 border-gray-100 focus:border-amber-400'} `} placeholder={selectedProc?.exige_face ? "DIGITE AS FACES..." : "OPCIONAL"} />