211 lines
8.2 KiB
TypeScript
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>
|
|
);
|
|
}
|