Files
Investidor-V2/resources/js/components/desk-document-picker.tsx
T

176 lines
6.6 KiB
TypeScript

import { useId, useRef, useState } from 'react';
import { profileJson } from '@/lib/profile-json';
export type ReusableDeskDocument = {
id: string | null;
document_id: string;
file_name: string;
version_no: number | null;
source_reference: string | null;
category: string | null;
stage: string | null;
valid_until: string | null;
origin_label: string | null;
status: string;
malware_scan_status: string;
media_type: string | null;
size_bytes: number | null;
};
type Props = {
caseId: string;
disabled: boolean;
selected: ReusableDeskDocument | null;
onSelect: (document: ReusableDeskDocument | null) => void;
};
const key = (doc: ReusableDeskDocument) =>
doc.id ?? `upload:${doc.document_id}`;
export default function DeskDocumentPicker(props: Props) {
const group = useId();
const [query, setQuery] = useState('');
const [documents, setDocuments] = useState<ReusableDeskDocument[]>([]);
const [next, setNext] = useState<number | null>(null);
const [busy, setBusy] = useState(false);
const [message, setMessage] = useState('');
const latest = useRef(props);
latest.current = props;
const sequence = useRef(0);
async function search(offset = 0) {
if (busy || props.disabled) return;
const ticket = ++sequence.current;
const caseId = props.caseId;
setBusy(true);
setMessage('A pesquisar documentos autorizados…');
try {
const params = new URLSearchParams({
q: query,
offset: String(offset),
});
const result = await profileJson(
`/balcao-virtual/dossiers/${caseId}/documentos-disponiveis?${params}`,
{},
'GET',
);
if (ticket !== sequence.current || latest.current.caseId !== caseId)
return;
if (
!Array.isArray(result.documents) ||
(result.next_offset !== null &&
!Number.isInteger(result.next_offset))
)
throw new Error(
'Não foi possível confirmar os resultados. A selecção anterior foi conservada.',
);
setDocuments(result.documents);
setNext(result.next_offset);
setMessage(
result.documents.length
? 'Seleccione a origem que pretende reutilizar.'
: 'Sem documentos nesta página. Pode ajustar a pesquisa ou continuar, se houver mais resultados.',
);
} catch (error) {
if (ticket === sequence.current && latest.current.caseId === caseId)
setMessage(
error instanceof Error
? error.message
: 'Não foi possível pesquisar documentos.',
);
} finally {
if (ticket === sequence.current) setBusy(false);
}
}
return (
<div className="space-y-2 rounded border p-3">
<p>Reutilizar um documento autorizado</p>
<p className="text-sm">
Pesquise pelo nome do ficheiro, dossier ou referência. A
selecção conserva a origem e a validade declarada; a associação
exige confirmação.
</p>
<label className="block">
Nome ou referência do documento
<input
value={query}
maxLength={120}
disabled={props.disabled || busy}
onChange={(event) => {
setQuery(event.target.value);
setDocuments([]);
setNext(null);
setMessage(
'Pesquisa alterada. Pesquise para actualizar os resultados.',
);
}}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault();
void search();
}
}}
/>
</label>
<button
type="button"
disabled={props.disabled || busy}
onClick={() => void search()}
>
Pesquisar documentos
</button>
<p role="status">{message}</p>
{documents.map((doc) => (
<label className="block" key={key(doc)}>
<input
type="radio"
name={`document-origin-${group}`}
checked={
props.selected
? key(props.selected) === key(doc)
: false
}
disabled={props.disabled || busy}
onChange={() => props.onSelect(doc)}
/>{' '}
{doc.file_name} · versão {doc.version_no ?? 'por confirmar'}{' '}
· {doc.origin_label ?? 'Ficheiro enviado por si'} ·{' '}
{doc.source_reference || 'Referência por completar'}
{doc.stage ? ` · origem ${doc.stage}` : ''}
{doc.valid_until
? ` · validade declarada ${doc.valid_until}`
: ''}{' '}
· {doc.malware_scan_status}
</label>
))}
{next !== null && (
<button
type="button"
disabled={props.disabled || busy}
onClick={() => void search(next)}
>
Próxima página de documentos
</button>
)}
{props.selected && (
<div>
<p>
Origem seleccionada: {props.selected.file_name} ·{' '}
{props.selected.source_reference ||
'Referência por completar'}
{props.selected.valid_until
? ` · validade declarada ${props.selected.valid_until}`
: ''}
</p>
<button
type="button"
disabled={props.disabled || busy}
onClick={() => props.onSelect(null)}
>
Retirar origem seleccionada
</button>
</div>
)}
</div>
);
}