import React, { useState, useEffect, useCallback } from 'react'; import { ShieldAlert, Plus, RotateCcw, Trash2, RefreshCw, X, Search, FileSearch } from 'lucide-react'; import { HybridBackend } from '../services/backend.ts'; import { useToast } from '../contexts/ToastContext.tsx'; import { useConfirm } from '../contexts/ConfirmContext.tsx'; import { PageHeader } from '../components/PageHeader.tsx'; const fmtBRL = (v: number) => 'R$ ' + Number(v || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2 }); const dataBR = (d: string) => (d || '').slice(0, 10).split('-').reverse().join('/'); // Modal de registro de glosa sobre um recebível (suporta glosa por item de GTO) const GlosaModal: React.FC<{ receb: any; onClose: () => void; onSaved: () => void }> = ({ receb, onClose, onSaved }) => { const toast = useToast(); const restante = Number(receb.valor) - Number(receb.valor_glosa || 0); const [valor, setValor] = useState(''); const [motivo, setMotivo] = useState(''); const [protocolo, setProtocolo] = useState(''); const [prazo, setPrazo] = useState(''); const [itens, setItens] = useState([]); const [itemSel, setItemSel] = useState(''); const [saving, setSaving] = useState(false); useEffect(() => { HybridBackend.getGlosaItens(receb.id).then(r => setItens(Array.isArray(r) ? r : [])).catch(() => setItens([])); }, [receb.id]); const pickItem = (id: string) => { setItemSel(id); const it = itens.find(i => i.id === id); if (it) setValor(String(it.valortotal)); }; const salvar = async () => { const v = Number(valor); if (!(v > 0)) { toast.error('INFORME UM VALOR DE GLOSA.'); return; } if (v > restante + 0.001) { toast.error(`GLOSA EXCEDE O RESTANTE (${fmtBRL(restante)}).`); return; } const it = itens.find(i => i.id === itemSel); setSaving(true); try { await HybridBackend.registrarGlosa({ financeiro_id: receb.id, valor: v, motivo, gto_item_id: itemSel || undefined, item_descricao: it?.descricao || undefined, protocolo: protocolo || undefined, prazo_recurso: prazo || undefined, }); toast.success('GLOSA REGISTRADA.'); onSaved(); onClose(); } catch (e: any) { toast.error((e.message || 'ERRO').toUpperCase()); } finally { setSaving(false); } }; const inCls = 'w-full bg-gray-50 border border-gray-200 rounded-xl px-3 py-2.5 text-sm font-bold text-gray-700 outline-none focus:border-teal-400'; const lbl = 'text-[10px] font-black text-gray-400 uppercase tracking-widest mb-1.5 block'; return (
e.stopPropagation()}>

Registrar glosa

{receb.convenio_nome || 'Convênio'} · {receb.pacientenome || '—'}

{receb.descricao}

Valor {fmtBRL(receb.valor)} · Restante {fmtBRL(restante)}

{itens.length > 0 && (
)}
setValor(e.target.value)} placeholder="0,00" className={inCls} />
setMotivo(e.target.value.toUpperCase())} placeholder="EX: CÓDIGO NÃO AUTORIZADO..." className={inCls + ' uppercase'} />
setProtocolo(e.target.value.toUpperCase())} placeholder="Nº" className={inCls + ' uppercase'} />
setPrazo(e.target.value)} className={inCls} />
); }; export const GlosasView: React.FC = () => { const toast = useToast(); const confirm = useConfirm(); const [aba, setAba] = useState<'registrar' | 'glosas'>('registrar'); const [recebiveis, setRecebiveis] = useState([]); const [glosas, setGlosas] = useState([]); const [loading, setLoading] = useState(false); const [busca, setBusca] = useState(''); const [filtro, setFiltro] = useState<'' | 'glosado' | 'recurso' | 'recuperado'>(''); const [modalReceb, setModalReceb] = useState(null); const carregar = useCallback(async () => { setLoading(true); try { const [r, g] = await Promise.all([HybridBackend.getGlosaRecebiveis(), HybridBackend.getGlosas(filtro || undefined)]); setRecebiveis(Array.isArray(r) ? r : []); setGlosas(Array.isArray(g) ? g : []); } catch { /* silent */ } finally { setLoading(false); } }, [filtro]); useEffect(() => { carregar(); }, [carregar]); const recuperar = async (g: any) => { if (!await confirm(`RECUPERAR a glosa de ${fmtBRL(g.valor)} (recurso ganho)? O valor volta ao recebível.`)) return; try { await HybridBackend.recuperarGlosa(g.id); toast.success('GLOSA RECUPERADA.'); carregar(); } catch (e: any) { toast.error((e.message || 'ERRO').toUpperCase()); } }; const recorrer = async (g: any) => { const protocolo = window.prompt('Nº do protocolo do recurso (opcional):') || ''; const prazo = window.prompt('Prazo do recurso (AAAA-MM-DD, opcional):') || ''; try { await HybridBackend.recorrerGlosa(g.id, { protocolo: protocolo || undefined, prazo_recurso: prazo || undefined }); toast.success('RECURSO ABERTO.'); carregar(); } catch (e: any) { toast.error((e.message || 'ERRO').toUpperCase()); } }; const excluir = async (g: any) => { if (!await confirm('EXCLUIR esta glosa? (correção)')) return; try { await HybridBackend.deleteGlosa(g.id); toast.success('GLOSA EXCLUÍDA.'); carregar(); } catch (e: any) { toast.error((e.message || 'ERRO').toUpperCase()); } }; const recebFiltrados = recebiveis.filter(r => !busca || (r.descricao || '').toUpperCase().includes(busca.toUpperCase()) || (r.pacientenome || '').toUpperCase().includes(busca.toUpperCase()) || (r.convenio_nome || '').toUpperCase().includes(busca.toUpperCase())); return (
{([['registrar', 'Recebíveis / Marcar glosa'], ['glosas', `Glosas (${glosas.length})`]] as const).map(([id, label]) => ( ))}
{loading ?
: aba === 'registrar' ? (
setBusca(e.target.value)} placeholder="Buscar recebível..." className="w-full bg-white border border-gray-200 rounded-xl pl-9 pr-3 py-2 text-sm font-bold text-gray-700 outline-none focus:border-teal-400" />
{recebFiltrados.length === 0 ?
Nenhum recebível de convênio em aberto.
: ( {['Convênio', 'Descrição', 'Paciente', 'Venc.', 'Valor', 'Glosado', 'Restante', ''].map(h => )} {recebFiltrados.map(r => { const restante = Number(r.valor) - Number(r.valor_glosa || 0); return ( ); })}
{h}
{r.convenio_nome || '—'} {r.descricao} {r.pacientenome || '—'} {dataBR(r.datavencimento)} {fmtBRL(r.valor)} {Number(r.valor_glosa) > 0 ? fmtBRL(r.valor_glosa) : '—'} {fmtBRL(restante)}
)}
) : (
{([['', 'Todas'], ['glosado', 'Glosadas'], ['recurso', 'Em recurso'], ['recuperado', 'Recuperadas']] as const).map(([id, label]) => ( ))}
{glosas.length === 0 ?
Nenhuma glosa.
: ( {['Convênio', 'Recebível', 'Paciente', 'Valor', 'Motivo', 'Status', 'Por', ''].map(h => )} {glosas.map(g => ( ))}
{h}
{g.convenio_nome || '—'} {g.financeiro_descricao}{g.item_descricao && item: {g.item_descricao}} {g.pacientenome || '—'} {fmtBRL(g.valor)} {g.motivo || '—'}{g.protocolo && prot. {g.protocolo}{g.prazo_recurso ? ` · prazo ${dataBR(g.prazo_recurso)}` : ''}} {g.status} {g.criado_por_nome || '—'} {g.status === 'glosado' && } {(g.status === 'glosado' || g.status === 'recurso') && }
)}
)} {modalReceb && setModalReceb(null)} onSaved={carregar} />}
); };