122 lines
4.5 KiB
TypeScript
122 lines
4.5 KiB
TypeScript
import { router } from '@inertiajs/react';
|
|
import { useState } from 'react';
|
|
import { createOperationKey } from '@/lib/operation-key';
|
|
import { contextApi } from '@/lib/investor-context';
|
|
|
|
export type LibrarySource = {
|
|
id: number;
|
|
title: string;
|
|
version_label: string | null;
|
|
metadata_version: number;
|
|
sha256: string;
|
|
};
|
|
export default function OpportunityLibraryReuse({
|
|
sources,
|
|
action,
|
|
etag,
|
|
}: {
|
|
sources: LibrarySource[];
|
|
action: string;
|
|
etag: string;
|
|
}) {
|
|
const [selected, setSelected] = useState('');
|
|
const [section, setSection] = useState('');
|
|
const [reason, setReason] = useState('');
|
|
const [key, setKey] = useState(createOperationKey);
|
|
const [message, setMessage] = useState('');
|
|
const [busy, setBusy] = useState(false);
|
|
return (
|
|
<section
|
|
aria-label="Reutilizar original da biblioteca"
|
|
className="space-y-3 rounded border p-4"
|
|
>
|
|
<h2 className="text-xl font-semibold">
|
|
Reutilizar original da biblioteca
|
|
</h2>
|
|
<p>
|
|
O suporte aponta para os mesmos bytes e conserva edição, hash e
|
|
histórico. Mantém-se privado; a ficha e o original têm
|
|
permissões e publicação próprias.
|
|
</p>
|
|
<label className="grid gap-1">
|
|
Original da biblioteca
|
|
<select
|
|
aria-label="Original da biblioteca"
|
|
className="w-full rounded border p-2"
|
|
value={selected}
|
|
onChange={(e) => setSelected(e.target.value)}
|
|
>
|
|
<option value="">Seleccionar original acessível</option>
|
|
{sources.map((s) => (
|
|
<option key={s.id} value={s.id}>
|
|
{s.title} · {s.version_label ?? 'sem edição'} ·
|
|
metadados v{s.metadata_version}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<label className="grid gap-1">
|
|
Secções utilizadas
|
|
<input
|
|
aria-label="Secções utilizadas"
|
|
className="w-full rounded border p-2"
|
|
value={section}
|
|
maxLength={255}
|
|
onChange={(e) => setSection(e.target.value)}
|
|
/>
|
|
</label>
|
|
<label className="grid gap-1">
|
|
Motivo da reutilização
|
|
<textarea
|
|
aria-label="Motivo da reutilização"
|
|
className="w-full rounded border p-2"
|
|
value={reason}
|
|
minLength={20}
|
|
maxLength={1000}
|
|
onChange={(e) => setReason(e.target.value)}
|
|
/>
|
|
</label>
|
|
<button
|
|
className="rounded border bg-emerald-800 px-4 py-2 text-white disabled:opacity-50"
|
|
type="button"
|
|
disabled={!selected || reason.length < 20 || busy}
|
|
onClick={async () => {
|
|
setBusy(true);
|
|
setMessage('');
|
|
try {
|
|
const result = await contextApi<{ message: string }>(
|
|
action,
|
|
{
|
|
resource_id: Number(selected),
|
|
section: section || null,
|
|
reason,
|
|
etag,
|
|
creation_key: key,
|
|
},
|
|
);
|
|
setMessage(result.message);
|
|
setKey(createOperationKey);
|
|
router.reload();
|
|
} catch (e) {
|
|
setMessage(
|
|
e instanceof Error
|
|
? e.message
|
|
: 'Não foi possível associar o original. Selecção preservada.',
|
|
);
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
}}
|
|
>
|
|
Associar original como suporte privado
|
|
</button>
|
|
<p role="status">
|
|
{message ||
|
|
(!sources.length
|
|
? 'Não há originais acessíveis à conta neste âmbito.'
|
|
: 'Seleccionar não grava nem divulga o original.')}
|
|
</p>
|
|
</section>
|
|
);
|
|
}
|