import { buildOfferPackage } from '@/lib/iva-offer-package'; import { useRef, useState } from 'react'; import { emptyOfferRequest, offerRequestLabels, type OfferPreparation, } from '@/lib/iva-offer-preparations'; import { emptyOfferWorkflow, offerWrittenRequest, offerWorkflowSource, offerWorkflowApplied, recordOfferWorkflow, offerAttachmentValid, type OfferAttachment, type OfferWorkflow, } from '@/lib/iva-offer-workflow'; export function SimulationOfferWorkflow({ row, draft, onChange, }: { row: OfferPreparation; draft: Record; onChange: (patch: Partial) => void; }) { const stageLabels: Record = { request: 'Pedido em preparação', presented: 'Pedido apresentado', decision: 'Decisão recebida', form: 'Formulário preparado', }; const latest = useRef(row); latest.current = row; const [notice, setNotice] = useState(''); const [error, setError] = useState(''), [busy, setBusy] = useState(false), [documents, setDocuments] = useState([]), [draftId, setDraftId] = useState(''); const w = row.workflow ?? emptyOfferWorkflow(); const edit = (patch: Partial) => onChange({ workflow: { ...(latest.current.workflow ?? emptyOfferWorkflow()), ...patch, }, }); const action = async (fn: () => void | Promise) => { setBusy(true); try { await fn(); setError(''); } catch (e) { setError(String(e)); } finally { setBusy(false); } }; const current = async () => { const response = await fetch('/api/v1/simulation-drafts/current', { credentials: 'same-origin', cache: 'no-store', headers: { Accept: 'application/json' }, }); if (!response.ok) throw Error('Não foi possível localizar a gravação privada.'); const data = (await response.json()).data; if (!data?.id) throw Error( 'Guarde primeiro o rascunho no portal para lhe associar anexos privados.', ); if ( data.economic_snapshot?.context_opportunity !== draft.context_opportunity ) throw Error( 'A gravação actual pertence a outro contexto;guarde este rascunho antes de anexar.', ); setDraftId(data.id); return data.id as string; }; const list = async () => { const id = await current(), response = await fetch( `/api/v1/simulation-drafts/${id}/offer-documents`, { credentials: 'same-origin', headers: { Accept: 'application/json' }, }, ); if (response.status === 401 || response.status === 403) throw Error( 'Autentique-se para anexar ou consultar documentos privados;os dados preparados mantêm-se.', ); if (!response.ok) throw Error('Não foi possível recuperar anexos privados.'); setDocuments((await response.json()).data); }; const attach = async ( file: File, field: 'requestAttachment' | 'decisionAttachment' | 'formAttachment', ) => { const id = await current(), data = new FormData(); data.append('file', file); const csrf = document.querySelector( 'meta[name="csrf-token"]', )?.content; const xsrf = document.cookie .split('; ') .find((c) => c.startsWith('XSRF-TOKEN=')) ?.slice(11); const response = await fetch( `/api/v1/simulation-drafts/${id}/offer-documents`, { method: 'POST', credentials: 'same-origin', headers: { Accept: 'application/json', ...(csrf ? { 'X-CSRF-TOKEN': csrf } : {}), ...(xsrf ? { 'X-XSRF-TOKEN': decodeURIComponent(xsrf) } : {}), }, body: data, }, ); if (!response.ok) throw Error( response.status === 401 ? 'Autentique-se para guardar anexos privados.' : 'Anexo não confirmado;rascunho preservado. ' + (await response.text()).slice(0, 250), ); const doc = (await response.json()).data; if (!offerAttachmentValid(doc)) throw Error('Confirmação do anexo inválida.'); edit({ [field]: doc }); setDocuments((previous) => [ ...previous.filter((d) => d.id !== doc.id), doc, ]); }; const download = (blob: Blob, name: string) => { const url = URL.createObjectURL(blob), a = document.createElement('a'); a.href = url; a.download = name; a.click(); setTimeout(() => URL.revokeObjectURL(url), 1000); }; const packageForm = async () => { const result = await buildOfferPackage(latest.current, async (url) => { const response = await fetch(url, { credentials: 'same-origin' }); if (!response.ok) throw Error( 'Anexo conservado;aguarde análise antimalware concluída antes de descarregar o conjunto.', ); return response.arrayBuffer(); }); download( new Blob([Uint8Array.from(result.bytes).buffer], { type: 'application/zip', }), `${row.branch}-pedido-decisao-formulario.zip`, ); onChange({ workflow: result.row.workflow }); setNotice( 'Conjunto preparado com pedido,elementos do formulário e despacho original conferido porSHA256;descarga iniciada.Submissão à autoridade permanece externa.', ); }; const text = ( field: | 'recipient' | 'writtenRequest' | 'decisionIssuer' | 'formName' | 'formSource' | 'formContent' | 'submissionReference', label: string, ) => (