import { DossierPrefill } from '@/components/dossier-prefill'; import { createOperationKey } from '@/lib/operation-key'; import { deskFieldApplies } from '@/lib/desk-field-visibility'; import { deskEmpty, deskChoices, deskScalar, mergeDeskPrefill, type DeskAnswer, } from '@/lib/desk-field-values'; import DeskTypedChoice from '@/components/desk-typed-choice'; import DeskDraftRecovery from '@/components/desk-draft-recovery'; import DeskAttachmentField, { type AttachmentInventory, } from '@/components/desk-attachment-field'; import { useState } from 'react'; import type { Field } from '@/pages/desk/service-editor'; import { Link, useForm } from '@inertiajs/react'; import { Frame, card, input, button } from '@/pages/investment/shared'; import CountrySelect, { type Country } from '@/components/country-select'; import { TerritoryFields, type TerritoryOption, } from '@/components/investment-territory'; import { DocumentReview, RequestOperations, type DeskDocument, type DeskPayment, } from '@/components/desk-internal-operations'; const emptyDocuments: DeskDocument[] = []; export default function RequestPage({ request: initialRequest, case: dossier, definition, owner, manager, analyst, fieldsEditable, operationsEditable = !['closed', 'cancelled'].includes( initialRequest.status, ), states, transitions, findings, messages, analysts, territoryPaths, events, documents, reusableDocuments = emptyDocuments, payments, caseVersion, countries, toolPrefill, boundContextRevision = 0, serviceEffectiveFrom, returnReasons = {}, }: { returnReasons?: Record; serviceEffectiveFrom?: string | null; boundContextRevision?: number; request: { id: string; version: number; status: string; payload: Record; protocol: string | null; editable_fields: string[]; analyst_id: number | null; due_at: string | null; }; case: { id: string; title: string }; definition: { title: string; fields: Record; result: string; }; owner: boolean; manager: boolean; analyst: boolean; fieldsEditable: boolean; operationsEditable?: boolean; states: Record; transitions: string[]; findings: { code: string; explanation: string; recommendation: string; severity: string; }[]; messages: { id: string; body: string; name: string; kind: string; created_at: string; }[]; analysts: { id: number; name: string }[]; territoryPaths: Record; documents: DeskDocument[]; reusableDocuments?: DeskDocument[]; payments: DeskPayment[]; caseVersion: number; countries: Country[]; toolPrefill: { description: string; draft_id: string; draft_version: number; snapshot_sha256: string; } | null; events: { id: string; action: string; name: string; created_at: string; payload: Record; }[]; }) { const [recoveredRequest, setRecoveredRequest] = useState< typeof initialRequest | null >(null); const request = recoveredRequest?.id === initialRequest.id && recoveredRequest.version > initialRequest.version ? recoveredRequest : initialRequest; const [checkpointBusy, setCheckpointBusy] = useState(false); const [checkpointBlocked, setCheckpointBlocked] = useState(false); const [attachmentInventory, setAttachmentInventory] = useState< | (AttachmentInventory & { sourceDocuments: DeskDocument[]; sourceReusable: DeskDocument[]; }) | null >(null); const [attachmentBusy, setAttachmentBusy] = useState< Record >({}); const filesBusy = Object.values(attachmentBusy).some(Boolean); const inventoryNewer = attachmentInventory && attachmentInventory.case_version > caseVersion; const inventorySame = attachmentInventory && attachmentInventory.case_version === caseVersion; const currentInventory = { case_version: Math.max( attachmentInventory?.case_version ?? 0, caseVersion, ), documents: attachmentInventory && (inventoryNewer || (inventorySame && attachmentInventory.sourceDocuments === documents)) ? attachmentInventory.documents : documents, reusable_documents: attachmentInventory && (inventoryNewer || (inventorySame && attachmentInventory.sourceReusable === reusableDocuments)) ? (attachmentInventory.reusable_documents ?? emptyDocuments) : reusableDocuments, }; const form = useForm({ context_revision: 0, context_reviewed: false, version: request.version, payload: request.payload, consent: false, reason: '', submit: false, }); const decision = useForm({ version: request.version, status: transitions[0] ?? '', reason: '', editable_fields: [] as string[], return_reason_code: 'unspecified', }); const assignment = useForm({ version: request.version, analyst_id: String(request.analyst_id ?? ''), reason: '', due_at: '', }); const message = useForm({ body: '', client_key: createOperationKey() }); const canEdit = owner && fieldsEditable && ['draft', 'returned'].includes(request.status); return ( {serviceEffectiveFrom && (

Entrada em vigor do formulário deste pedido:{' '} {serviceEffectiveFrom}. O pedido conserva a versão com que foi criado.

)} Voltar ao dossier {dossier.title}

{definition.result}

{canEdit && ( [ key, field.type === 'attachment' ? { ...field, options: Object.fromEntries( currentInventory.documents.map( (document) => [ document.id, document.file_name, ], ), ), } : field, ], ), )} paused={form.processing} pendingWork={filesBusy} onBusy={setCheckpointBusy} onBlocked={setCheckpointBlocked} onRecover={(payload, reason, current) => { setRecoveredRequest({ ...request, ...current, payload: current.payload, }); form.setData((d) => ({ ...d, payload, reason, version: current.version, consent: false, context_reviewed: false, context_revision: 0, })); }} /> )} {owner && ( !['consent', 'attachment'].includes( definition.fields[k]?.type, ) && (request.status !== 'returned' || request.editable_fields.includes(k)), )} onReview={(revision, reviewed) => form.setData((d) => ({ ...d, context_revision: revision, context_reviewed: reviewed, })) } onApply={(patch, replace) => form.setData((d) => ({ ...d, payload: mergeDeskPrefill( d.payload, patch, definition.fields, replace, ), })) } /> )}

{request.protocol ? 'Pedido submetido pelo investidor' : 'Rascunho — ainda não submetido'}

{states[request.status]} · Protocolo:{' '} {request.protocol ?? 'Ainda sem protocolo'}

Responsável real:{' '} {request.analyst_id ?? 'A aguardar atribuição'} · Prazo interno: {request.due_at ?? 'Não definido'}

{request.protocol && ( Consultar e imprimir recibo interno )}
{canEdit && toolPrefill && (

Reutilizar dados do projeto

A descrição está vazia neste pedido. Pode reutilizar o resumo já guardado na ferramenta deste dossier, versão{' '} {toolPrefill.draft_version}.

{toolPrefill.description}

Esta ação guarda o rascunho com os dados preenchidos nesta página. Reveja-os antes de submeter.

)}

Verificações automáticas de preparação

{findings.map((f) => (

{f.explanation} {f.recommendation} ( {f.severity === 'error' ? 'Contradição nos valores declarados' : 'Informação / evidência pendente'} )

))}

A análise automática não é uma resposta do responsável nem uma aprovação oficial.

{ e.preventDefault(); if ( checkpointBusy || checkpointBlocked || filesBusy || !canEdit ) return; form.transform((d) => ({ ...d, version: request.version, submit: false, })); form.put(`/balcao-virtual/pedidos/${request.id}`); }} > form.setData('payload', { ...form.data.payload, country: value, }) } disabled={ !canEdit || (request.status === 'returned' && !request.editable_fields.includes('country')) } /> form.setData('payload', { ...form.data.payload, declared_origin_country_code: value, }) } />

Indique o país de onde apresenta este pedido, se desejar. Este campo é distinto do país do requerente.

{Object.entries(definition.fields) .filter(([key]) => deskFieldApplies( key, definition.fields, form.data.payload, ), ) .map(([key, f]) => f.type === 'territory' ? (
{f.label} {f.required ? ' *' : ''} form.setData('payload', { ...form.data.payload, [key]: path.at(-1)?.id ?? '', }) } /> {form.errors[`payload.${key}`] && (

{form.errors[`payload.${key}`]}

)}
) : f.type === 'attachment' ? (
setAttachmentBusy((state) => ({ ...state, [key]: value, })) } onChange={(value) => form.setData('payload', { ...form.data.payload, [key]: value, }) } onInventory={(result, select) => { setAttachmentInventory({ ...result, reusable_documents: result.reusable_documents ?? currentInventory.reusable_documents, sourceDocuments: documents, sourceReusable: reusableDocuments, }); const linkId = result.link_id; if (select && linkId) form.setData((d) => { const ids = deskChoices( d.payload[key], ); const uploaded = result.documents.find( (document) => document.id === linkId, ); const alreadySelected = ids.includes(linkId) || (!!uploaded && ids.some( (id) => result.documents.find( ( document, ) => document.id === id, ) ?.document_id === uploaded.document_id, )); return { ...d, payload: { ...d.payload, [key]: alreadySelected || ids.length >= (f.max_files ?? 1) ? ids : [ ...ids, linkId, ], }, }; }); }} /> {Object.entries(form.errors) .filter( ([path]) => path === `payload.${key}` || path.startsWith(`payload.${key}.`), ) .map(([path, error]) => (

{error}

))}
) : ['multiselect', 'consent'].includes(f.type) ? (
form.setData('payload', { ...form.data.payload, [key]: value, }) } /> {Object.entries(form.errors) .filter( ([path]) => path === `payload.${key}` || path.startsWith(`payload.${key}.`), ) .map(([path, error]) => (

{error}

))}
) : (