fix(ar1): upload file sostituibile in Quadro F + Signature

Quadro F (Ar1Wizard.js): se uno slot ha gia un file selezionato si mostra
preview con pulsante Rimuovi che azzera lo state, invece di FileUpload
basic+auto+customUpload che dopo la prima selezione non riapre il picker.

Signature (Ar1Signature.js): aggiunti useRef + key={uploadAttempt} dinamica
sul FileUpload + helper resetUploadInput() chiamato in success/error/guard
ext, per forzare il remount e permettere di ricaricare un nuovo file senza
dover navigare via Torna alla Home.

Bug segnalati da test sandbox 2026-05-06.
This commit is contained in:
ECO
2026-05-06 14:36:09 +02:00
parent 09da2b7c25
commit fba47c6e77
2 changed files with 44 additions and 16 deletions

View File

@@ -27,6 +27,13 @@ const Ar1Signature = () => {
const [loading, setLoading] = useState(true);
const [generating, setGenerating] = useState(false);
const [uploading, setUploading] = useState(false);
const [uploadAttempt, setUploadAttempt] = useState(0);
const fileUploadRef = useRef(null);
const resetUploadInput = () => {
try { fileUploadRef.current?.clear?.(); } catch (_) {}
setUploadAttempt(n => n + 1);
};
const refreshForm = () => {
Ar1Service.getForm(formId,
@@ -76,6 +83,7 @@ const Ar1Signature = () => {
const ext = file.name.toLowerCase().slice(file.name.lastIndexOf('.'));
if (ext !== '.pdf' && ext !== '.p7m') {
if (toast.current) toast.current.show({ severity: 'warn', summary: 'Formato non valido', detail: 'Accettati: .pdf (PAdES) o .p7m (CAdES)' });
resetUploadInput();
return;
}
@@ -85,6 +93,7 @@ const Ar1Signature = () => {
Ar1Service.uploadSignature(formId, file,
(resp) => {
setUploading(false);
resetUploadInput();
refreshForm();
const outcome = resp?.outcome;
if (outcome === 'VERIFIED') {
@@ -98,6 +107,7 @@ const Ar1Signature = () => {
},
(err) => {
setUploading(false);
resetUploadInput();
if (err?.detail?.code === 'NO_SIGNATURE_DETECTED') {
if (toast.current) toast.current.show({
severity: 'error',
@@ -152,6 +162,8 @@ const Ar1Signature = () => {
<div>
<p>{__('Formati accettati: PDF con firma PAdES oppure file .p7m (CAdES). Dimensione massima 50 MB.', 'gepafin')}</p>
<FileUpload
ref={fileUploadRef}
key={uploadAttempt}
name="file"
mode="basic"
accept=".pdf,.p7m"

View File

@@ -250,22 +250,38 @@ const Ar1Wizard = () => {
{quadro.upload_slots.map(slot => (
<div key={slot.id} style={{ marginBottom: 14, padding: 10, border: '1px solid #ddd', borderRadius: 4 }}>
<label style={{ fontWeight: 500 }}>{slot.label}{slot.required ? ' *' : ''}</label>
<FileUpload
name={slot.id}
mode="basic"
accept={(slot.accept || []).join(',')}
maxFileSize={(slot.max_size_mb || 300) * 1024 * 1024}
disabled={isReadonly}
customUpload
uploadHandler={(e) => {
const file = e.files[0];
handleFieldChange(quadro.id, slot.id, { filename: file.name, size: file.size });
if (toast.current) toast.current.show({ severity: 'info', summary: 'File selezionato', detail: file.name });
}}
auto
chooseLabel={q[slot.id]?.filename || __('Scegli file', 'gepafin')}
style={{ marginTop: 6 }}
/>
{q[slot.id]?.filename ? (
<div style={{ marginTop: 6, display: 'flex', alignItems: 'center', gap: 8, padding: 8, background: '#f0f7ff', border: '1px solid #b3d4f0', borderRadius: 4 }}>
<i className="pi pi-file" style={{ color: '#003d7a' }} />
<span style={{ flex: 1, wordBreak: 'break-all' }}>{q[slot.id].filename}</span>
<Button
icon="pi pi-times"
label={__('Rimuovi', 'gepafin')}
severity="danger"
outlined
size="small"
disabled={isReadonly}
onClick={() => handleFieldChange(quadro.id, slot.id, null)}
/>
</div>
) : (
<FileUpload
name={slot.id}
mode="basic"
accept={(slot.accept || []).join(',')}
maxFileSize={(slot.max_size_mb || 300) * 1024 * 1024}
disabled={isReadonly}
customUpload
uploadHandler={(e) => {
const file = e.files[0];
handleFieldChange(quadro.id, slot.id, { filename: file.name, size: file.size });
if (toast.current) toast.current.show({ severity: 'info', summary: 'File selezionato', detail: file.name });
}}
auto
chooseLabel={__('Scegli file', 'gepafin')}
style={{ marginTop: 6 }}
/>
)}
</div>
))}
</div>