179 lines
7.9 KiB
TypeScript
179 lines
7.9 KiB
TypeScript
import { useForm } from '@inertiajs/react';
|
|
import { useRef } from 'react';
|
|
import { Panel, controlClass } from '@/components/procurement';
|
|
import { Button } from '@/components/ui/button';
|
|
import { date } from '@/types/procurement';
|
|
|
|
export type ReceiptDocument = {
|
|
id: number;
|
|
document_type: string;
|
|
original_name: string;
|
|
size: number;
|
|
sha256: string;
|
|
uploaded_by: number;
|
|
created_at: string;
|
|
};
|
|
|
|
export default function ReceiptDocuments({
|
|
receiptId,
|
|
ready,
|
|
canAdd,
|
|
documents,
|
|
}: {
|
|
receiptId: number;
|
|
ready: boolean;
|
|
canAdd: boolean;
|
|
documents: ReceiptDocument[];
|
|
}) {
|
|
const input = useRef<HTMLInputElement>(null);
|
|
const form = useForm<{
|
|
operation_key: string;
|
|
document_type: string;
|
|
file: File | null;
|
|
}>({
|
|
operation_key: crypto.randomUUID(),
|
|
document_type: 'delivery_note',
|
|
file: null,
|
|
});
|
|
const labels: Record<string, string> = {
|
|
delivery_note: 'Guia de remessa',
|
|
quality: 'Evidência de qualidade',
|
|
other: 'Outro documento',
|
|
};
|
|
return (
|
|
<Panel
|
|
title="Anexos da recepção"
|
|
description="Evidências ligadas à confirmação preservada. Anexar não altera quantidades, qualidade ou entradas de stock."
|
|
>
|
|
<div id="receipt-documents" />
|
|
{!ready ? (
|
|
<p className="text-sm">
|
|
Anexos ainda indisponíveis nesta instalação.
|
|
</p>
|
|
) : (
|
|
<>
|
|
{documents.length === 0 && (
|
|
<p className="text-sm text-muted-foreground">
|
|
Sem anexos nesta recepção.
|
|
</p>
|
|
)}
|
|
<ul className="space-y-3">
|
|
{documents.map((document) => (
|
|
<li
|
|
key={document.id}
|
|
className="rounded border p-3 text-sm"
|
|
>
|
|
<a
|
|
className="font-medium underline"
|
|
href={`/recepcoes/${receiptId}/documentos/${document.id}`}
|
|
>
|
|
{document.original_name}
|
|
</a>
|
|
<p>
|
|
{labels[document.document_type]} ·{' '}
|
|
{Math.ceil(document.size / 1024)} KB ·
|
|
Registado por #{document.uploaded_by} ·{' '}
|
|
{date(document.created_at)}
|
|
</p>
|
|
<details className="mt-2">
|
|
<summary className="cursor-pointer">
|
|
Identificação do ficheiro preservado
|
|
</summary>
|
|
<code className="break-all">
|
|
SHA-256 {document.sha256}
|
|
</code>
|
|
</details>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
{canAdd && (
|
|
<details className="mt-4 rounded border p-3">
|
|
<summary className="cursor-pointer text-sm font-medium">
|
|
Acrescentar evidência
|
|
</summary>
|
|
<form
|
|
className="mt-3 grid gap-3"
|
|
onSubmit={(event) => {
|
|
event.preventDefault();
|
|
form.post(
|
|
`/recepcoes/${receiptId}/documentos`,
|
|
{
|
|
preserveScroll: true,
|
|
onSuccess: () => {
|
|
form.setData({
|
|
operation_key:
|
|
crypto.randomUUID(),
|
|
document_type:
|
|
'delivery_note',
|
|
file: null,
|
|
});
|
|
if (input.current)
|
|
input.current.value = '';
|
|
},
|
|
},
|
|
);
|
|
}}
|
|
>
|
|
<label className="grid gap-1 text-sm">
|
|
Tipo de documento
|
|
<select
|
|
className={controlClass}
|
|
value={form.data.document_type}
|
|
onChange={(event) =>
|
|
form.setData(
|
|
'document_type',
|
|
event.target.value,
|
|
)
|
|
}
|
|
>
|
|
{Object.entries(labels).map(
|
|
([value, label]) => (
|
|
<option
|
|
key={value}
|
|
value={value}
|
|
>
|
|
{label}
|
|
</option>
|
|
),
|
|
)}
|
|
</select>
|
|
</label>
|
|
<label className="grid gap-1 text-sm">
|
|
Ficheiro PDF, JPG ou PNG — até 2 MB
|
|
<input
|
|
ref={input}
|
|
type="file"
|
|
accept=".pdf,.jpg,.jpeg,.png"
|
|
required
|
|
onChange={(event) =>
|
|
form.setData(
|
|
'file',
|
|
event.target.files?.[0] ?? null,
|
|
)
|
|
}
|
|
/>
|
|
</label>
|
|
<p className="text-sm text-muted-foreground">
|
|
Ficheiros idênticos do mesmo tipo são
|
|
reutilizados. Os documentos anteriores
|
|
permanecem preservados.
|
|
</p>
|
|
{Object.entries(form.errors).map(
|
|
([key, error]) => (
|
|
<p role="alert" key={key}>
|
|
{error}
|
|
</p>
|
|
),
|
|
)}
|
|
<Button disabled={form.processing}>
|
|
Anexar à recepção
|
|
</Button>
|
|
</form>
|
|
</details>
|
|
)}
|
|
</>
|
|
)}
|
|
</Panel>
|
|
);
|
|
}
|