Files
Investidor-V2/resources/js/components/partnership-publication.tsx
T

211 lines
8.2 KiB
TypeScript

import { Link } from '@inertiajs/react';
import { useEffect } from 'react';
import {
usePublication,
type PublicationProps,
} from '@/lib/partnership-publication';
import {
Field,
Errors,
control,
button,
labels,
} from '@/pages/partnerships/shared';
const actions: Record<string, string> = {
submit: 'Submeter para publicação',
publish: 'Publicar',
return: 'Devolver para correcção',
reject: 'Rejeitar com fundamento',
suspend: 'Suspender publicação',
archive: 'Arquivar publicação',
renew: 'Renovar em rascunho',
};
const fields: Record<string, string> = {
title: 'Título',
offer: 'Oferta',
seeking: 'Procura',
kind: 'Oferta ou procura',
partnership_type: 'Tipo de parceria',
sector_id: 'Referência do sector',
organization_id: 'Referência da organização',
deadline: 'Prazo',
summary: 'Resumo público',
requirements: 'Requisitos',
confidentiality: 'Partilha dos detalhes',
availability: 'Disponibilidade',
consent: 'Autorização de partilha',
mandatory: 'Critérios obrigatórios',
origins: 'Origens',
locations: 'Localizações',
};
export default function PartnershipPublication({
p,
actorId,
onDirty,
}: {
p: PublicationProps;
actorId: number;
onDirty: (value: boolean) => void;
}) {
const edit = usePublication(p, actorId);
useEffect(() => {
onDirty(edit.dirty);
}, [edit.dirty, onDirty]);
useEffect(() => () => onDirty(false), [onDirty]);
if (!edit.dirty && !p.owner && !p.moderator) return null;
const active = edit.current?.actions ?? [];
return (
<section
className="space-y-4 rounded border p-5"
data-publication-form="publication"
>
<h2 className="text-xl font-bold">Pré-visualização e publicação</h2>
<p>
Estado: {labels[p.record.status] ?? p.record.status}.
Verificação da organização, sector e localização permanecem por
confirmar quando não há evidência.
</p>
<p>
A acção aplica-se à versão revista da proposta. Submeter
encaminha para moderação; renovar prepara um rascunho. Estas
acções não decidem candidaturas nem alteram salas ou marcos.
</p>
<button
type="button"
className="underline"
disabled={edit.refreshing || edit.form.processing}
onClick={edit.refresh}
>
Comparar com a proposta actual
</button>
{edit.problem && <p role="alert">{edit.problem}</p>}
{edit.invalid && (
<p role="alert">
A resposta ou conta actual não corresponde a esta
preparação. A gravação está suspensa.
</p>
)}
{(edit.stale || edit.needsCompare || edit.recovery) && (
<div className="space-y-3 rounded border p-3">
<p>
Reveja o contexto antes de outra acção. O motivo
preparado foi conservado.
</p>
<p>
Versão revista: {edit.base?.version} ·{' '}
{labels[edit.base?.status ?? '']} → Actual:{' '}
{edit.current?.version} ·{' '}
{labels[edit.current?.status ?? '']}
</p>
{edit.changes.length > 0 && (
<div className="overflow-x-auto">
<table className="w-full text-left">
<thead>
<tr>
<th>Campo</th>
<th>Antes</th>
<th>Actualmente</th>
</tr>
</thead>
<tbody>
{edit.changes.map((key) => (
<tr key={key}>
<th>{fields[key] ?? key}</th>
<td className="break-words whitespace-pre-wrap">
{edit.base?.values[key] ||
'Não indicado'}
</td>
<td className="break-words whitespace-pre-wrap">
{edit.current?.values[key] ||
'Não indicado'}
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
<p>
Adoptar esta versão conserva o motivo e permite preparar
uma nova tentativa. Uma tentativa anterior sem
confirmação deixa de ser recuperada por este formulário;
o seu registo no processo mantém-se.
</p>
{edit.mustRefresh && (
<p>
Compare com a proposta actual antes de adoptar uma
nova versão.
</p>
)}
<button
type="button"
className={button}
disabled={
edit.invalid ||
edit.mustRefresh ||
edit.refreshing ||
edit.form.processing
}
onClick={edit.adopt}
>
Revi esta versão; conservar o motivo e preparar nova
acção
</button>
</div>
)}
{(active.length > 0 || edit.dirty) && (
<Field label="Motivo da ação">
<textarea
className={control}
value={edit.form.data.reason}
onChange={(event) => edit.change(event.target.value)}
/>
</Field>
)}
{!active.length && edit.dirty && (
<p>
O estado ou acesso actual não permite nova acção. O motivo
preparado foi conservado em memória.
</p>
)}
<Errors errors={edit.form.errors} />
{edit.recovery && (
<button
type="button"
className={button}
disabled={
edit.invalid ||
!edit.can(edit.recovery) ||
edit.refreshing ||
edit.form.processing
}
onClick={() => edit.send(edit.recovery!, true)}
>
Recuperar a mesma tentativa: {actions[edit.recovery]}
</button>
)}
<div className="flex flex-wrap gap-3">
{active.map((action) => (
<button
type="button"
className={button}
key={action}
disabled={edit.blocked || !edit.form.data.reason.trim()}
onClick={() => edit.send(action)}
>
{actions[action]}
</button>
))}
{p.canEdit && (
<Link
className="underline"
href={`/parcerias/${p.record.id}/editar`}
>
Editar rascunho
</Link>
)}
</div>
</section>
);
}