Files
Investidor-V2/resources/js/components/territorial-traceability-plan.tsx
T

219 lines
8.6 KiB
TypeScript

import { useForm } from '@inertiajs/react';
export type TraceabilityPlan = {
hash: string;
code_matches: boolean;
backup_ready: boolean;
applied: boolean;
reversed: boolean;
history: {
id: string;
action: string;
actor_user_id: number;
occurred_at: string;
reason: string | null;
}[];
plan: {
target: { database: string; port: number };
rows: {
key: string;
reference: string;
association: string;
role: string;
source_reference: string | null;
source_quote: string | null;
supports: string;
traceability: string;
existing_validation: string | null;
effect: string;
}[];
updates: {
id: string;
reference: string;
current: {
source_reference: string | null;
evidence_reference: string | null;
};
values: { source_reference: string; evidence_reference: string };
source_quote: string;
effect: string;
}[];
pending: { reference: string; reason: string }[];
};
};
export default function TerritorialTraceabilityPlan({
preview,
}: {
preview: TraceabilityPlan;
}) {
const form = useForm({
hash: preview.hash,
operation: 'traceability',
reverse: false,
reason: '',
confirm_reverse: false,
});
return (
<section className="space-y-4 rounded border p-5">
<h2 className="text-xl font-bold">
V2 — aplicar rastreabilidade territorial
</h2>
<p>
Destino: {preview.plan.target.database}:
{preview.plan.target.port}. Fontes oficiais identificadas são
aceites; não se repete validação da proveniência. Estados
existentes são preservados.
</p>
<p>
{preview.plan.rows.length} associações/fontes;{' '}
{preview.plan.updates.length} atualizações de referências.
Nenhuma nova implantação ou relação por proximidade.
</p>
<p role="status">
Backup/restauro/reversão:{' '}
{preview.backup_ready
? 'verificados (validade 24 horas)'
: 'pendentes ou expirados'}{' '}
· Código:{' '}
{preview.code_matches
? 'corresponde ao plano'
: 'plano deve ser atualizado'}{' '}
· Aplicação:{' '}
{preview.applied
? 'aplicada'
: preview.reversed
? 'revertida; novo plano necessário'
: 'não aplicada'}
</p>
<details>
<summary>Histórico de aplicação e reversão</summary>
{preview.history.map((event) => (
<p key={event.id}>
{event.occurred_at} · {event.action} · conta{' '}
{event.actor_user_id} · Motivo:{' '}
{event.reason ?? 'não registado no evento original'}
</p>
))}
</details>
<form
onSubmit={(e) => {
e.preventDefault();
form.transform(() => ({
hash: preview.hash,
operation: 'traceability',
reverse: preview.applied,
reason: form.data.reason,
confirm_reverse: form.data.confirm_reverse,
}));
form.post('/management/relationship-repair', {
preserveScroll: true,
});
}}
>
{preview.applied && (
<fieldset className="space-y-3 rounded border border-red-600 p-4">
<legend>
Reversão — restitui as referências anteriores
</legend>
<label className="block">
Motivo da reversão
<textarea
className="block w-full rounded border p-2"
value={form.data.reason}
maxLength={1000}
required
onChange={(event) =>
form.setData('reason', event.target.value)
}
/>
</label>
<label className="block">
<input
type="checkbox"
checked={form.data.confirm_reverse}
onChange={(event) =>
form.setData(
'confirm_reverse',
event.target.checked,
)
}
/>{' '}
Confirmo que pretendo reverter este incremento
</label>
</fieldset>
)}
<button
disabled={
form.processing ||
!preview.backup_ready ||
!preview.code_matches ||
preview.reversed ||
(preview.applied &&
(!form.data.confirm_reverse ||
!form.data.reason.trim()))
}
className="rounded bg-emerald-800 px-4 py-3 text-white disabled:opacity-50"
>
{preview.applied
? 'Reverter este incremento de rastreabilidade'
: 'Aplicar rastreabilidade territorial V2'}
</button>
{Object.values(form.errors).map((error) => (
<p role="alert" key={error}>
{error}
</p>
))}
</form>
<h3 className="font-bold">Alterações aos campos existentes</h3>
{preview.plan.updates.map((update) => (
<article className="border-t py-3" key={update.id}>
<h4 className="font-bold">{update.reference}</h4>
<p>
Referência atual:{' '}
{update.current.source_reference ?? 'não preenchida'};
evidência atual:{' '}
{update.current.evidence_reference ?? 'não preenchida'}.
</p>
<p>
Proposta: {update.values.source_reference}; dossier{' '}
{update.values.evidence_reference}.
</p>
<p>Fundamento: {update.source_quote}</p>
<p>{update.effect}</p>
</article>
))}
<h3 className="font-bold">Fundamento por associação</h3>
{preview.plan.rows.map((row) => (
<article className="border-t py-3" key={row.key}>
<h3 className="font-bold">
{row.reference} — {row.association} ({row.role})
</h3>
<p>
Atual:{' '}
{row.existing_validation ??
'sem campo de validação nesta associação'}
; estado preservado.
</p>
<p>
Proposta: ligar a referência e o fundamento. Fonte:{' '}
{row.source_reference ?? 'por recuperar no acervo'}
</p>
<p>Citação/âmbito: {row.source_quote ?? row.supports}</p>
<p>
Rastreabilidade: {row.traceability} · Efeito:{' '}
{row.effect}
</p>
</article>
))}
<h3 className="font-bold">
Pendências de rastreabilidade (não invalidam dados)
</h3>
{preview.plan.pending.map((row, i) => (
<p key={i}>
{row.reference}: {row.reason}
</p>
))}
</section>
);
}