import React, { useState, useEffect, useCallback } from 'react'; import { X, Plus, GripVertical, Trash2, Pencil, FileText, ChevronLeft, Printer, Save, Search, Tag, ClipboardList } from 'lucide-react'; import { DragDropContext, Droppable, Draggable, DropResult } from '@hello-pangea/dnd'; import { HybridBackend } from '../services/backend.ts'; import { ModeloReceita, ItemReceita, Especialidade, Paciente, Dentista } from '../types.ts'; import { useHybridBackend } from '../hooks/useHybridBackend.ts'; import { useToast } from '../contexts/ToastContext.tsx'; const VIAS = ['VO', 'SL', 'IV', 'IM', 'SC', 'Tópico', 'Inalatória', 'Colírio', 'Nasal']; const FREQUENCIAS = ['1x/dia', '2x/dia', '3x/dia', '4x/dia', '6/6h', '8/8h', '12/12h', '24/24h', 'Dose única', 'Se necessário']; const DURACOES = ['1 dia', '2 dias', '3 dias', '5 dias', '7 dias', '10 dias', '14 dias', '21 dias', '30 dias', 'Contínuo']; const ITEM_VAZIO: Omit = { medicamento: '', dose: '', via: 'VO', frequencia: '8/8h', duracao: '7 dias', instrucoes: '' }; // --------------- Editor de item de medicamento --------------- const ItemEditor: React.FC<{ item: Omit; onChange: (v: Omit) => void; onSave: () => void; onCancel: () => void; }> = ({ item, onChange, onSave, onCancel }) => { const inp = "w-full border border-gray-200 rounded-lg px-3 py-2 text-xs focus:ring-2 focus:ring-teal-100 focus:border-teal-300 outline-none bg-white"; const sel = inp + " appearance-none"; return (
onChange({ ...item, medicamento: e.target.value.toUpperCase() })} placeholder="EX: AMOXICILINA 500MG" className={inp} />
onChange({ ...item, dose: e.target.value })} placeholder="EX: 500mg" className={inp} />
onChange({ ...item, instrucoes: e.target.value })} placeholder="EX: TOMAR APÓS AS REFEIÇÕES" className={inp} />
); }; // --------------- Editor de modelo --------------- const ModeloEditor: React.FC<{ modelo: ModeloReceita | null; especialidades: Especialidade[]; onSave: (m: ModeloReceita) => void; onBack: () => void; }> = ({ modelo, especialidades, onSave, onBack }) => { const toast = useToast(); const [nome, setNome] = useState(modelo?.nome || ''); const [espId, setEspId] = useState(modelo?.especialidadeId || ''); const [instrucoes, setInstrucoes] = useState(modelo?.instrucoes || ''); const [items, setItems] = useState(modelo?.items || []); const [editingIdx, setEditingIdx] = useState(null); const [newItem, setNewItem] = useState>(ITEM_VAZIO); const [adding, setAdding] = useState(false); const [saving, setSaving] = useState(false); const espNome = especialidades.find(e => e.id === espId)?.nome || ''; 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 addItem = () => { if (!newItem.medicamento.trim()) return; setItems(prev => [...prev, { ...newItem, id: `new_${Date.now()}`, ordem: prev.length }]); setNewItem(ITEM_VAZIO); setAdding(false); }; const updateItem = (idx: number, val: Omit) => { setItems(prev => prev.map((it, i) => i === idx ? { ...it, ...val } : it)); }; const removeItem = (idx: number) => setItems(prev => prev.filter((_, i) => i !== idx)); const handleSave = async () => { if (!nome.trim()) { toast.error('NOME DO MODELO OBRIGATÓRIO'); return; } setSaving(true); try { const payload = { nome: nome.toUpperCase(), especialidadeId: espId || undefined, especialidadeNome: espNome || undefined, instrucoes: instrucoes || undefined, items }; if (modelo?.id) { await HybridBackend.updateModeloReceita({ ...payload, id: modelo.id }); } else { const res = await HybridBackend.saveModeloReceita(payload); onSave({ ...payload, id: res.id, items }); return; } onSave({ ...payload, id: modelo.id, items }); } catch { toast.error('ERRO AO SALVAR MODELO'); } finally { setSaving(false); } }; const inp = "w-full border border-gray-200 rounded-lg px-3 py-2.5 text-sm focus:ring-2 focus:ring-teal-100 focus:border-teal-300 outline-none bg-white"; return (

{modelo ? 'Editar Modelo' : 'Novo Modelo'}

setNome(e.target.value.toUpperCase())} className={inp} placeholder="EX: ANTIBIÓTICO PÓS-CIRÚRGICO" />
setInstrucoes(e.target.value)} className={inp} placeholder="EX: USO EM CASO DE INFECÇÃO ODONTOGÊNICA" />
{/* Lista de medicamentos */}
{!adding && }
{adding && (
{ setAdding(false); setNewItem(ITEM_VAZIO); }} />
)} {provided => (
{items.map((it, idx) => ( {(prov, snap) => (
{editingIdx === idx ? (
updateItem(idx, v)} onSave={() => setEditingIdx(null)} onCancel={() => setEditingIdx(null)} />
) : (

{it.medicamento}

{[it.dose, it.via, it.frequencia, it.duracao].filter(Boolean).join(' · ')}

{it.instrucoes &&

{it.instrucoes}

}
)}
)}
))} {provided.placeholder} {items.length === 0 && !adding && (
Nenhum medicamento adicionado ainda
)}
)}
); }; // --------------- Preview de impressão --------------- const PrintPreview: React.FC<{ modelo: ModeloReceita; paciente: Paciente; dentista?: Dentista; onClose: () => void; }> = ({ modelo, paciente, dentista, onClose }) => { const hoje = new Date().toLocaleDateString('pt-BR', { day: '2-digit', month: 'long', year: 'numeric' }); return (

Pré-visualização

RECEITUÁRIO

{dentista &&

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

}
Paciente

{paciente.nome}

Data

{hoje}

{modelo.instrucoes &&
{modelo.instrucoes}
}
{modelo.items.map((it, i) => (

{i + 1}. {it.medicamento}

{[it.dose, it.via, it.frequencia, `por ${it.duracao}`].filter(Boolean).join(' — ')}

{it.instrucoes &&

{it.instrucoes}

}
))}

Assinatura e Carimbo

{hoje}

); }; // --------------- Modal principal --------------- export const ReceitaModal: React.FC<{ paciente: Paciente; onClose: () => void }> = ({ paciente, onClose }) => { const toast = useToast(); const { data: especialidadesRaw } = useHybridBackend(HybridBackend.getEspecialidades); const especialidades = especialidadesRaw ?? []; const { data: dentistasRaw } = useHybridBackend(HybridBackend.getDentistas); const dentistas = dentistasRaw ?? []; const [modelos, setModelos] = useState([]); const [loading, setLoading] = useState(true); const [filtroEsp, setFiltroEsp] = useState(''); const [busca, setBusca] = useState(''); const [view, setView] = useState<'lista' | 'editor' | 'print'>('lista'); const [modeloAtivo, setModeloAtivo] = useState(null); const [printModelo, setPrintModelo] = useState(null); const dentistaPrincipal = dentistas[0]; const carregar = useCallback(async () => { setLoading(true); try { setModelos(await HybridBackend.getModelosReceita()); } catch { toast.error('Erro ao carregar modelos'); } finally { setLoading(false); } }, []); useEffect(() => { carregar(); }, [carregar]); useEffect(() => { const h = (e: KeyboardEvent) => { if (e.key === 'Escape') { if (view !== 'lista') setView('lista'); else onClose(); } }; document.addEventListener('keydown', h); return () => document.removeEventListener('keydown', h); }, [view, onClose]); const handleDelete = async (id: string) => { if (!confirm('Excluir este modelo?')) return; await HybridBackend.deleteModeloReceita(id); setModelos(prev => prev.filter(m => m.id !== id)); }; const filtrados = modelos.filter(m => { const matchEsp = !filtroEsp || m.especialidadeId === filtroEsp; const matchBusca = !busca || m.nome.toLowerCase().includes(busca.toLowerCase()); return matchEsp && matchBusca; }); return (
{view === 'lista' && ( <>

Modelos de Receita

{paciente.nome}

{/* Sidebar especialidades */}

Especialidade

{especialidades.map(e => ( ))}
{/* Lista de modelos */}
setBusca(e.target.value)} placeholder="Buscar modelo..." 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" />
{loading &&
Carregando...
} {!loading && filtrados.length === 0 && (

Nenhum modelo encontrado

)} {filtrados.map(m => (

{m.nome}

{m.especialidadeNome && {m.especialidadeNome}} {m.items.length} medicamento{m.items.length !== 1 ? 's' : ''}
{m.instrucoes &&

{m.instrucoes}

}
{m.items.length > 0 && (
{m.items.slice(0, 3).map(it => (

• {it.medicamento} — {it.dose} {it.frequencia}

))} {m.items.length > 3 &&

+{m.items.length - 3} mais...

}
)}
))}
)} {view === 'editor' && ( { carregar(); setView('lista'); toast.success('MODELO SALVO!'); }} onBack={() => setView('lista')} /> )} {view === 'print' && printModelo && ( setView('lista')} /> )}
); };