import React, { useState, useEffect } from 'react'; import { X, Plus, GripVertical, Trash2, Printer, ChevronLeft, Search, Microscope, Activity, Image, FileX } from 'lucide-react'; import { DragDropContext, Droppable, Draggable, DropResult } from '@hello-pangea/dnd'; import { HybridBackend } from '../services/backend.ts'; import { PedidoExame, ItemExame, Paciente, Dentista } from '../types.ts'; import { useHybridBackend } from '../hooks/useHybridBackend.ts'; import { useToast } from '../contexts/ToastContext.tsx'; const CATALOG: { categoria: string; icon: React.ReactNode; cor: string; items: { nome: string; regiao?: string }[] }[] = [ { categoria: 'RAIO-X', icon: , cor: 'blue', items: [ { nome: 'Periapical' }, { nome: 'Interproximal (Bite-Wing)' }, { nome: 'Panorâmica' }, { nome: 'Oclusal Superior' }, { nome: 'Oclusal Inferior' }, { nome: 'Cefalométrica Lateral' }, { nome: 'P.A. de Face' }, ], }, { categoria: 'TOMOGRAFIA', icon: , cor: 'purple', items: [ { nome: 'Cone Beam — Dente Único', regiao: '' }, { nome: 'Cone Beam — Terço Anterior' }, { nome: 'Cone Beam — Arcada Completa Superior' }, { nome: 'Cone Beam — Arcada Completa Inferior' }, { nome: 'Cone Beam — Ambas Arcadas' }, { nome: 'ATM (Articulação Temporomandibular)' }, ], }, { categoria: 'OUTROS', icon: , cor: 'green', items: [ { nome: 'Cefalometria Analítica' }, { nome: 'Fotografia Clínica Extraoral' }, { nome: 'Fotografia Clínica Intraoral' }, { nome: 'Modelos de Gesso / Escaneamento' }, { nome: 'Biópsia' }, { nome: 'Exame Laboratorial' }, ], }, ]; const COR: Record = { blue: { bg: 'bg-blue-50', text: 'text-blue-700', border: 'border-blue-200', chip: 'bg-blue-100 text-blue-700' }, purple: { bg: 'bg-purple-50', text: 'text-purple-700', border: 'border-purple-200', chip: 'bg-purple-100 text-purple-700' }, green: { bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200', chip: 'bg-green-100 text-green-700' }, }; // --------------- Print --------------- const PrintView: React.FC<{ pedido: PedidoExame; dentista?: Dentista; onClose: () => void }> = ({ pedido, dentista, onClose }) => { const hoje = new Date().toLocaleDateString('pt-BR', { day: '2-digit', month: 'long', year: 'numeric' }); const grupos = CATALOG.map(c => ({ ...c, selecionados: pedido.items.filter(it => it.categoria === c.categoria) })).filter(c => c.selecionados.length > 0); return (

Pré-visualização

PEDIDO DE EXAMES

{dentista &&

{dentista.nome} — CRO: {dentista.cro || '—'}/{dentista.cro_uf || '—'}

}
Paciente

{pedido.pacienteNome}

Data

{hoje}

{grupos.map(g => (

{g.categoria}

    {g.selecionados.map((it, i) => (
  • {i + 1}.
    {it.nome} {it.regiao && — Região: {it.regiao}} {it.observacao &&

    {it.observacao}

    }
  • ))}
))}
{pedido.observacoes && (
Obs: {pedido.observacoes}
)}

Assinatura e Carimbo

{hoje}

); }; // --------------- Detail chip for exam item --------------- const ItemChip: React.FC<{ item: ItemExame; onChange: (v: Partial) => void; onRemove: () => void; dragHandle: React.ReactNode; isDragging: boolean; }> = ({ item, onChange, onRemove, dragHandle, isDragging }) => { const [expanded, setExpanded] = useState(false); const cat = CATALOG.find(c => c.categoria === item.categoria); const cor = COR[cat?.cor || 'blue']; return (
{dragHandle} {item.categoria}

{item.nome}

{expanded && (
onChange({ regiao: e.target.value })} placeholder="ex: dente 16, arcada inf." className="w-full border border-gray-200 rounded-lg px-2.5 py-1.5 text-xs focus:ring-2 focus:ring-teal-100 outline-none" />
onChange({ observacao: e.target.value })} placeholder="ex: urgente, pós-cirúrgico" className="w-full border border-gray-200 rounded-lg px-2.5 py-1.5 text-xs focus:ring-2 focus:ring-teal-100 outline-none" />
)}
); }; // --------------- Modal principal --------------- export const PedidoExameModal: React.FC<{ paciente: Paciente; onClose: () => void }> = ({ paciente, onClose }) => { const toast = useToast(); const { data: dentistasRaw } = useHybridBackend(HybridBackend.getDentistas); const dentistas = dentistasRaw ?? []; const [items, setItems] = useState([]); const [observacoes, setObservacoes] = useState(''); const [busca, setBusca] = useState(''); const [catAtiva, setCatAtiva] = useState(null); const [view, setView] = useState<'editor' | 'print'>('editor'); const [saving, setSaving] = useState(false); const dentistaPrincipal = dentistas[0]; useEffect(() => { const h = (e: KeyboardEvent) => { if (e.key === 'Escape') { if (view !== 'editor') setView('editor'); else onClose(); } }; document.addEventListener('keydown', h); return () => document.removeEventListener('keydown', h); }, [view, onClose]); const catalogFiltrado = CATALOG.map(c => ({ ...c, items: c.items.filter(it => { if (catAtiva && c.categoria !== catAtiva) return false; if (busca && !it.nome.toLowerCase().includes(busca.toLowerCase())) return false; return true; }), })).filter(c => c.items.length > 0); const addItem = (catNome: string, itemNome: string, regiao?: string) => { if (items.some(it => it.nome === itemNome && it.categoria === catNome)) { toast.error('EXAME JÁ ADICIONADO'); return; } setItems(prev => [...prev, { id: `ex_${Date.now()}_${Math.random()}`, nome: itemNome, categoria: catNome, regiao: regiao || '', observacao: '', ordem: prev.length, }]); }; const updateItem = (id: string, patch: Partial) => { setItems(prev => prev.map(it => it.id === id ? { ...it, ...patch } : it)); }; const removeItem = (id: string) => setItems(prev => prev.filter(it => it.id !== id)); const onDragEnd = (result: DropResult) => { if (!result.destination) return; const list = [...items]; const [moved] = list.splice(result.source.index, 1); list.splice(result.destination.index, 0, moved); setItems(list.map((it, i) => ({ ...it, ordem: i }))); }; const handleGerarPedido = async () => { if (items.length === 0) { toast.error('ADICIONE AO MENOS UM EXAME'); return; } setSaving(true); try { await HybridBackend.savePedidoExame({ pacienteId: paciente.id, pacienteNome: paciente.nome, profissionalId: dentistaPrincipal?.id, profissionalNome: dentistaPrincipal?.nome, data: new Date().toISOString().slice(0, 10), observacoes: observacoes || undefined, items, }); setView('print'); } catch { toast.error('ERRO AO SALVAR PEDIDO'); } finally { setSaving(false); } }; const pedidoParaImprimir: PedidoExame = { id: 'preview', pacienteId: paciente.id, pacienteNome: paciente.nome, profissionalId: dentistaPrincipal?.id, profissionalNome: dentistaPrincipal?.nome, data: new Date().toISOString().slice(0, 10), observacoes, items, }; if (view === 'print') { return (
setView('editor')} />
); } return (
{/* Header */}

Pedido de Exames

{paciente.nome}

{/* Body: catálogo + selecionados */}
{/* Catálogo (esquerda) */}
setBusca(e.target.value)} placeholder="Buscar exame..." className="w-full pl-8 pr-3 py-2 border border-gray-200 rounded-lg text-xs focus:ring-2 focus:ring-teal-100 outline-none" />
{CATALOG.map(c => { const cor = COR[c.cor]; return ( ); })}
{catalogFiltrado.map(cat => { const cor = COR[cat.cor]; return (
{cat.icon} {cat.categoria}
{cat.items.map(it => { const jaSelecionado = items.some(s => s.nome === it.nome && s.categoria === cat.categoria); return ( ); })}
); })} {catalogFiltrado.length === 0 && (

Nenhum exame encontrado

)}
{/* Selecionados (direita) */}

Exames Selecionados {items.length > 0 && ({items.length})}

{items.length > 0 && ( )}
{items.length === 0 ? (

Nenhum exame selecionado

Clique nos exames ao lado para adicionar

) : ( {provided => (
{items.map((it, idx) => ( {(prov, snap) => (
} onChange={patch => updateItem(it.id, patch)} onRemove={() => removeItem(it.id)} />
)}
))} {provided.placeholder}
)}
)}
{/* Observações + ação */}
setObservacoes(e.target.value)} placeholder="ex: urgente, pós-cirúrgico, encaminhar para..." className="w-full border border-gray-200 rounded-lg px-3 py-2 text-xs focus:ring-2 focus:ring-teal-100 outline-none" />
); };