import { createOperationKey } from '@/lib/operation-key';
import { Link, useForm, usePage } from '@inertiajs/react';
import { Frame, card, input, button } from '@/pages/investment/shared';
import {
InvestorGuidance,
type Guidance,
} from '@/components/investor-guidance';
import {
DocumentReview,
type DeskDocument,
} from '@/components/desk-internal-operations';
type Stage = {
title: string;
requirements: string;
status: string;
entity: string;
dependencies: string[];
evidence: string | null;
};
function LinkReview({
link,
}: {
link: { id: string; version: number; url: string; contact: string | null };
}) {
const form = useForm({
version: link.version,
available: false,
evidence: '',
contact: link.contact ?? '',
});
return (
);
}
type Event = {
id: string;
action: string;
name: string;
created_at: string;
payload: Record;
};
export default function Case({
case: dossier,
canWrite,
manager,
requests,
services,
documents,
workflow,
stepStates,
states,
guidance,
events,
links,
legacyRequest,
}: {
case: {
id: string;
title: string;
version: number;
workflow: Record;
investor_id?: string;
project_id?: string;
journey_id?: string;
};
canWrite: boolean;
manager: boolean;
requests: { id: string; status: string; protocol: string | null }[];
services: { id: string; title: string; version: number }[];
documents: DeskDocument[];
links: {
id: string;
entity: string;
purpose: string;
contact: string | null;
status: string;
verified_at: string | null;
version: number;
url: string;
monitor: { available: boolean; reason: string; contact: string };
}[];
legacyRequest: {
id: number;
reference: string;
status: string;
next_action: string | null;
next_action_due_at: string | null;
responsible_name: string | null;
} | null;
workflow?: Record;
stepStates: Record;
states: Record;
guidance: Guidance | null;
events: Event[];
}) {
const actor = usePage<{ auth: { user: { id: number } } }>().props.auth.user;
const requestForm = useForm({
service_version_id: services[0]?.id ?? '',
client_key: createOperationKey(),
});
const stageForm = useForm({
version: dossier.version,
stage: 'E0',
status: 'preparing',
reason: '',
evidence: '',
document_id: '',
valid_until: '',
exception: false,
});
const upload = useForm<{
version: number;
file: File | null;
document_id: string;
category: string;
source_reference: string;
consent: boolean;
}>({
version: dossier.version,
file: null,
document_id: '',
category: 'project_evidence',
source_reference: '',
consent: false,
});
return (
{legacyRequest && (
Pedido anterior associado — sem nova submissão
{legacyRequest.reference} · {legacyRequest.status} ·
Responsável:{' '}
{legacyRequest.responsible_name ?? 'Por atribuir'}
{legacyRequest.next_action ??
'A aguardar orientação do responsável'}{' '}
· Prazo interno:{' '}
{legacyRequest.next_action_due_at ?? 'Não definido'}
{manager && (
Continuar no registo original do pedido e CRM
)}
)}
Voltar ao Balcão Virtual
Um dossier, os mesmos pedidos, documentos, conversas e tarefas.
Projecto existente:{' '}
{dossier.project_id ? 'associado' : 'ainda não associado'} ·
Investidor:{' '}
{dossier.investor_id
? 'conta existente associada'
: 'por associar'}
.
{guidance ? (
) : (
A extensão de orientação aguarda instalação autenticada.
)}
Pedido de apoio no fluxo existente
A recomendação ajuda a preparar. Só a sua submissão cria um
pedido recebido; a resposta é assinada pelo responsável.
{requests.map((r) => (
{r.protocol ?? 'Continuar rascunho de apoio'} —{' '}
{states[r.status]}
))}
{canWrite && (
{
e.preventDefault();
requestForm.post(
`/balcao-virtual/dossiers/${dossier.id}/pedidos`,
);
}}
>
Serviço publicado
requestForm.setData(
'service_version_id',
e.target.value,
)
}
>
Seleccione
{services.map((s) => (
{s.title} — v{s.version}
))}
Preparar rascunho de pedido
{!services.length && (
Sem serviços publicados. A orientação e os
dossiers continuam disponíveis.
)}
{Object.entries(requestForm.errors).map(([k, v]) => (
{v}
))}
)}
Etapas documentais do Balcão
Estes estados descrevem preparação e conferência no portal.
A conclusão interna não significa que uma instituição emitiu
aprovação, registo ou licença. Os requisitos do plano são
afirmações da fonte; a aplicabilidade deve ser documentada
no ato específico.
{Object.entries(workflow ?? dossier.workflow).map(
([code, s]) => (
{code} — {s.title}
{stepStates[s.status]} · Entidade: {s.entity}
{s.requirements}
Dependências:{' '}
{s.dependencies.join(', ') ||
'Nenhuma nesta etapa'}{' '}
· Evidência: {s.evidence ?? 'Por documentar'}
),
)}
{(canWrite || manager) && (
{
e.preventDefault();
stageForm.setData('version', dossier.version);
stageForm.transform((d) => ({
...d,
version: dossier.version,
}));
stageForm.post(
`/balcao-virtual/dossiers/${dossier.id}/etapas`,
);
}}
>
Etapa
stageForm.setData('stage', e.target.value)
}
>
{Object.entries(dossier.workflow).map(
([c, s]) => (
{c} — {s.title}
),
)}
Estado
stageForm.setData('status', e.target.value)
}
>
{Object.entries(stepStates)
.filter(
([s]) =>
manager ||
![
'completed',
'not_applicable',
].includes(s),
)
.map(([s, label]) => (
{label}
))}
Motivo
stageForm.setData('reason', e.target.value)
}
/>
Evidência / referência
stageForm.setData(
'evidence',
e.target.value,
)
}
/>
Identificador do documento de suporte
stageForm.setData(
'document_id',
e.target.value,
)
}
/>
Conclusão e não aplicabilidade exigem revisão por
outra pessoa autorizada e evidência documental.
Registar etapa
{Object.entries(stageForm.errors).map(([k, v]) => (
{v}
))}
)}
Documentos privados reutilizados
{documents.map((d) => (
{d.file_name} · {d.category} ·{' '}
{d.malware_scan_status}
{d.source_reference}
{d.status === 'active' &&
d.malware_scan_status === 'clean' && (
Descarregar com verificação de autorização e
hash
)}
))}
{canWrite && (
{
e.preventDefault();
upload.transform((d) => ({
...d,
version: dossier.version,
}));
upload.post(
`/balcao-virtual/dossiers/${dossier.id}/documentos`,
);
}}
>
Ficheiro privado
upload.setData(
'file',
e.target.files?.[0] ?? null,
)
}
/>
Ou identificador de documento já autorizado
upload.setData(
'document_id',
e.target.value,
)
}
/>
Proveniência e referência
upload.setData(
'source_reference',
e.target.value,
)
}
/>
Assunto da evidência — não equivale a validação
upload.setData('category', e.target.value)
}
>
Documento geral do projecto
Mercado
Localização
Recursos
Financiamento
upload.setData('consent', e.target.checked)
}
/>
Autorizo associar este documento ao dossier privado.
Associar documento
{Object.entries(upload.errors).map(([k, v]) => (
{v}
))}
)}
{manager && !canWrite && (
)}
Ligações institucionais e alternativas
O encaminhamento não transmite o dossier. Ligações
indisponíveis não impedem os serviços internos.
{links.map((link) => (
{link.entity} — {link.purpose} ·{' '}
{link.monitor.available
? `Disponibilidade conferida internamente em ${link.verified_at}; não valida conteúdo ou decisão oficial`
: 'Ligação por verificar ou indisponível'}
Contacto alternativo: {link.monitor.contact}
Abrir encaminhamento
{' '}
·{' '}
Ligação indisponível: continuar internamente
{manager && }
))}
{!links.length && (
Sem ligações verificadas. Prepare os documentos e
solicite apoio no pedido existente.
)}
Histórico de autoria, decisões e resultados
{events.map((event) => (
{event.action} · {event.name} · {event.created_at}
{JSON.stringify(event.payload, null, 2)}
))}
);
}