feat(protese): Fase 2 — rastreabilidade de componentes + ocorrências com contraditório

Provedor de Prótese, Fase 2 (doc/PROVEDOR-PROTESE-DECISAO.md):

- Componentes: eixo 'direcao' (enviado clínica→lab | devolvido lab→clínica) + conferência
  (pendente/ok/divergente/ausente, com autor/data). Endpoint .../componentes/:id/conferir.
  UI: badges de direção e conferência + botões inline na aba Itens & Valores.
- Ocorrências (protese_os_ocorrencia): 10 categorias (parafuso substituído, peça danificada,
  retrabalho…), descrição, responsável, autor. Evidências reusam protese_os_foto (ocorrencia_id).
- Contraditório: aberta → respondida → em_analise → resolvida | contestada; 'resolvida' é final.
  Endpoints /ocorrencias, /ocorrencias/:id/resposta, /ocorrencias/:id/status. Nova aba
  "Ocorrências" (com contador) no modal da OS.
- Indicadores propositalmente NÃO implementados (só ocorrências resolvidas pesarão — Fase 5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-06-24 04:53:33 +02:00
parent 7ae6993c4b
commit 3ffaff9d7d
4 changed files with 271 additions and 24 deletions
+26 -3
View File
@@ -778,21 +778,44 @@ export const HybridBackend = {
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao mudar status');
return await res.json();
},
uploadProteseFoto: async (id: string, file: File, legenda?: string) => {
uploadProteseFoto: async (id: string, file: File, legenda?: string, ocorrenciaId?: string) => {
const fd = new FormData();
fd.append('foto', file);
if (legenda) fd.append('legenda', legenda);
if (ocorrenciaId) fd.append('ocorrencia_id', ocorrenciaId);
const res = await apiFetch(`${API_URL}/protese/os/${id}/foto`, { method: 'POST', body: fd });
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao anexar foto');
return await res.json();
},
// Conferência de componente (status: ok | divergente | ausente | pendente)
conferirProteseComponente: async (compId: string, status_conferencia: string) => {
const res = await apiFetch(`${API_URL}/protese/os/componentes/${compId}/conferir`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status_conferencia }) });
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao conferir');
return await res.json();
},
// Ocorrências (contraditório)
abrirProteseOcorrencia: async (osId: string, body: { categoria: string; descricao: string; responsavel?: string; componente_id?: string }) => {
const res = await apiFetch(`${API_URL}/protese/os/${osId}/ocorrencias`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao abrir ocorrência');
return await res.json();
},
responderProteseOcorrencia: async (ocId: string, resposta: string) => {
const res = await apiFetch(`${API_URL}/protese/os/ocorrencias/${ocId}/resposta`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ resposta }) });
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao responder');
return await res.json();
},
statusProteseOcorrencia: async (ocId: string, status: string) => {
const res = await apiFetch(`${API_URL}/protese/os/ocorrencias/${ocId}/status`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ status }) });
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao mudar status');
return await res.json();
},
deleteProteseFoto: async (fotoId: string) => {
const res = await apiFetch(`${API_URL}/protese/os/fotos/${fotoId}`, { method: 'DELETE' });
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao remover foto');
return await res.json();
},
// Componentes enviados ao laboratório
addProteseComponente: async (osId: string, body: { nome: string; quantidade?: number; observacao?: string }) => {
// Componentes enviados ao laboratório (direcao: 'enviado' clínica→lab | 'devolvido' lab→clínica)
addProteseComponente: async (osId: string, body: { nome: string; quantidade?: number; observacao?: string; direcao?: 'enviado' | 'devolvido' }) => {
const res = await apiFetch(`${API_URL}/protese/os/${osId}/componentes`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) });
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).error || 'Erro ao adicionar componente');
return await res.json();