commit21
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user