import React, { useState, useEffect } from 'react'; import { FlaskConical, Building2, Clock, ShieldCheck, AlertTriangle, CheckCircle2, Circle, Package, History, Camera, MapPin, ChevronRight, Loader2 } from 'lucide-react'; import { HybridBackend } from '../services/backend.ts'; const fmtBRL = (v: number) => 'R$ ' + Number(v || 0).toLocaleString('pt-BR', { minimumFractionDigits: 2 }); const dataBR = (d: string) => (d || '').slice(0, 10).split('-').reverse().join('/'); const dataHoraBR = (d: string) => { if (!d) return ''; const x = new Date(d); return isNaN(x.getTime()) ? dataBR(d) : x.toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', year: '2-digit', hour: '2-digit', minute: '2-digit' }); }; const STATUS_LABEL: Record = { solicitado: 'Solicitado', recebido: 'Recebido', producao: 'Em produção', prova: 'Prova', ajuste: 'Ajuste', finalizado: 'Finalizado', entregue: 'Entregue', cancelado: 'Cancelado' }; const STATUS_COR: Record = { solicitado: 'bg-gray-100 text-gray-600', recebido: 'bg-blue-100 text-blue-700', producao: 'bg-amber-100 text-amber-700', prova: 'bg-indigo-100 text-indigo-700', ajuste: 'bg-orange-100 text-orange-700', finalizado: 'bg-teal-100 text-teal-700', entregue: 'bg-green-100 text-green-700', cancelado: 'bg-red-100 text-red-600' }; const FLUXO = ['solicitado', 'recebido', 'producao', 'prova', 'ajuste', 'finalizado', 'entregue']; const OCOR_CATS: Record = { nao_devolvido: 'Componente não devolvido', danificado: 'Componente danificado', incompativel: 'Componente incompatível', parafuso_substituido: 'Parafuso substituído', parafuso_desgastado: 'Parafuso desgastado', peca_incorreta: 'Peça incorreta', ajuste_inadequado: 'Ajuste inadequado', falha_fabricacao: 'Falha de fabricação', retrabalho: 'Retrabalho', outro: 'Outro' }; const Secao: React.FC<{ titulo: string; Icon: any; children: React.ReactNode }> = ({ titulo, Icon, children }) => (

{titulo}

{children}
); export const ProtesePublicView: React.FC<{ token: string }> = ({ token }) => { const [os, setOs] = useState(null); const [erro, setErro] = useState(''); const [loading, setLoading] = useState(true); useEffect(() => { HybridBackend.getProtesePublic(token).then(setOs).catch(e => setErro(e.message || 'Link inválido.')).finally(() => setLoading(false)); }, [token]); if (loading) return
; if (erro || !os) return (

{erro || 'Link inválido'}

Peça um novo link à clínica.

); const noLab = (os.local_atual || 'clinica') === 'laboratorio'; const idxAtual = FLUXO.indexOf(os.status); const cancelado = os.status === 'cancelado'; return (
{/* Topo / marca */}

ScoreOdonto

Acompanhamento de prótese

Somente leitura
{/* Cabeçalho da OS */}

{os.tipo}

OS {os.id}

{STATUS_LABEL[os.status] || os.status}
{os.clinica_nome && {os.clinica_nome}} {noLab ? 'Com o laboratório' : 'Na clínica'} {os.tipo_os === 'garantia' && Garantia}
{/* Etapas */} {cancelado ? (
OS cancelada
) : (
{FLUXO.map((s, i) => { const done = i < idxAtual, atual = i === idxAtual; const ev = (os.eventos || []).find((e: any) => e.status === s); return (
{done ? : atual ?
: } {STATUS_LABEL[s]} {ev ? dataBR(ev.created_at) : ''}
{i < FLUXO.length - 1 &&
} ); })}
)} {/* Dados */}
{os.paciente_nome &&
Paciente{os.paciente_nome}
} {os.dentista_nome &&
Dentista{os.dentista_nome}
} {os.dentes &&
Dentes{os.dentes}
} {os.material &&
Material{os.material}
} {os.cor &&
Cor{os.cor}
} {os.prazo_entrega &&
Prazo{dataBR(os.prazo_entrega)}
} {Number(os.custo) > 0 &&
Valor do lab{fmtBRL(os.custo)}
}
{os.observacoes &&

{os.observacoes}

} {os.garantia_ate &&
Garantia até {dataBR(os.garantia_ate)}
}
{/* Componentes */} {(os.componentes || []).length > 0 && (
{os.componentes.map((c: any) => (
{c.direcao === 'devolvido' ? 'Devolvido' : 'Enviado'} {c.quantidade}× {c.nome}
))}
)} {/* Fotos */} {(os.fotos || []).length > 0 && (
{os.fotos.map((f: any) => ( {f.legenda &&

{f.legenda}

}
))}
)} {/* Ocorrências */} {(os.ocorrencias || []).length > 0 && (
{os.ocorrencias.map((o: any) => (
{(o.status || '').replace('_', ' ')} {OCOR_CATS[o.categoria] || o.categoria}

{o.descricao}

{o.resposta &&

Resposta: {o.resposta}

}
))}
)} {/* Histórico */}
{(os.eventos || []).map((ev: any, i: number) => (
{ev.observacao || STATUS_LABEL[ev.status] || ev.status} {dataHoraBR(ev.created_at)}{ev.actor_nome ? ` · ${ev.actor_nome}` : ''}
))}
{/* CTA de conversão */}

Gostou de acompanhar por aqui?

Crie sua conta grátis e gerencie seu laboratório: bancada, etapas, financeiro e score.

Criar conta grátis

ScoreOdonto · Provedor de Prótese

); };