fix(ar1-admin): crash su error Pydantic (detail array) -> formatErrorDetail helper

Bug: click 'Crea versione' con version vuota o invalida -> pagina bianca + 'error'.
Causa: il BE restituisce 422 con detail come ARRAY Pydantic [{loc, msg, type}].
Il codice faceva detail: err?.detail || 'fallback' -> array passato a PrimeReact Toast
-> React render 'Objects are not valid as a React child' -> crash unhandled.

Fix: helper formatErrorDetail(detail, fallback) che normalizza:
  - string -> ritorna direttamente
  - array Pydantic -> 'loc.loc: msg; loc.loc: msg' (filtrato 'body')
  - object -> JSON.stringify
  - altro -> String(x)

Applicato con regex a tutti i pattern 'err?.detail || "fallback"' nel file (tutti
i toast show di errore nei vari handler: saveLayout, saveNewVersion, savePolicy,
savePecRule, deletePecRule, saveEmail, runPreview, runBulk, loadXxx).
This commit is contained in:
BFLOWS
2026-04-23 15:24:05 +02:00
parent 84ada138f2
commit cad839aea0

View File

@@ -58,8 +58,24 @@ const PEC_KIND_OPTIONS = [
{ label: PEC_KIND_LABEL.AR1_BULK_MANUAL, value: 'AR1_BULK_MANUAL' } { label: PEC_KIND_LABEL.AR1_BULK_MANUAL, value: 'AR1_BULK_MANUAL' }
]; ];
// Normalizza il campo detail che puo essere string o array Pydantic [{loc, msg, type}]
const formatErrorDetail = (detail, fallback) => {
if (!detail) return fallback || 'Errore';
if (typeof detail === 'string') return detail;
if (Array.isArray(detail)) {
return detail.map(e => {
const loc = Array.isArray(e.loc) ? e.loc.filter(x => x !== 'body').join('.') : '';
const msg = e.msg || e.message || JSON.stringify(e);
return loc ? `${loc}: ${msg}` : msg;
}).join('; ');
}
if (typeof detail === 'object') return JSON.stringify(detail);
return String(detail);
};
/** /**
* Ar1AdminConfig — configurazione AR1 per superadmin. (build 1776950454) * Ar1AdminConfig — configurazione AR1 per superadmin. (build 1776950637)
* URL: /ar1-admin * URL: /ar1-admin
* *
* 5 sezioni (TabView): * 5 sezioni (TabView):
@@ -163,19 +179,19 @@ const Ar1AdminConfig = () => {
setLoadingTpl(true); setLoadingTpl(true);
Ar1Service.listTemplates( Ar1Service.listTemplates(
(resp) => { setTemplates(resp?.items || resp || []); setLoadingTpl(false); }, (resp) => { setTemplates(resp?.items || resp || []); setLoadingTpl(false); },
(err) => { setLoadingTpl(false); if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Caricamento template fallito' }); } (err) => { setLoadingTpl(false); if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Caricamento template fallito') }); }
); );
}; };
const loadPolicy = () => { const loadPolicy = () => {
Ar1Service.getPolicy( Ar1Service.getPolicy(
(resp) => { setPolicy(resp); setPolicyDraft(resp); }, (resp) => { setPolicy(resp); setPolicyDraft(resp); },
(err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Caricamento policy fallito' }); } (err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Caricamento policy fallito') }); }
); );
}; };
const loadPecRules = () => { const loadPecRules = () => {
Ar1Service.listPecSchedule( Ar1Service.listPecSchedule(
(resp) => setPecRules(resp?.items || resp || []), (resp) => setPecRules(resp?.items || resp || []),
(err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Caricamento regole fallito' }); } (err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Caricamento regole fallito') }); }
); );
}; };
const loadDocCategories = () => { const loadDocCategories = () => {
@@ -192,7 +208,7 @@ const Ar1AdminConfig = () => {
setAvailableVariables(resp?.available_variables || []); setAvailableVariables(resp?.available_variables || []);
setLoadingEmail(false); setLoadingEmail(false);
}, },
(err) => { setLoadingEmail(false); if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Caricamento testi fallito' }); } (err) => { setLoadingEmail(false); if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Caricamento testi fallito') }); }
); );
}; };
@@ -268,7 +284,7 @@ const Ar1AdminConfig = () => {
setEditLayoutOpen(false); setEditLayoutOpen(false);
loadTemplates(); loadTemplates();
}, },
(err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Salvataggio fallito' }); } (err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Salvataggio fallito') }); }
); );
}; };
@@ -292,7 +308,7 @@ const Ar1AdminConfig = () => {
setNewVersionOpen(false); setNewVersionOpen(false);
loadTemplates(); loadTemplates();
}, },
(err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Creazione fallita' }); } (err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Creazione fallita') }); }
); );
}; };
@@ -319,7 +335,7 @@ const Ar1AdminConfig = () => {
}, },
(err) => { (err) => {
setSavingPolicy(false); setSavingPolicy(false);
if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Salvataggio fallito' }); if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Salvataggio fallito') });
} }
); );
}; };
@@ -353,7 +369,7 @@ const Ar1AdminConfig = () => {
setPecDialogOpen(false); setPecDialogOpen(false);
loadPecRules(); loadPecRules();
}; };
const onKo = (err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Salvataggio fallito' }); }; const onKo = (err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Salvataggio fallito') }); };
if (pecEditing) Ar1Service.updatePecRule(pecEditing.id, payload, onOk, onKo); if (pecEditing) Ar1Service.updatePecRule(pecEditing.id, payload, onOk, onKo);
else Ar1Service.createPecRule(payload, onOk, onKo); else Ar1Service.createPecRule(payload, onOk, onKo);
}; };
@@ -372,7 +388,7 @@ const Ar1AdminConfig = () => {
if (toast.current) toast.current.show({ severity: 'success', summary: 'Eliminata', detail: 'Regola eliminata' }); if (toast.current) toast.current.show({ severity: 'success', summary: 'Eliminata', detail: 'Regola eliminata' });
loadPecRules(); loadPecRules();
}, },
(err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Eliminazione fallita' }); } (err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Eliminazione fallita') }); }
); );
} }
}); });
@@ -400,7 +416,7 @@ const Ar1AdminConfig = () => {
}, },
(err) => { (err) => {
setBulkRunning(false); setBulkRunning(false);
if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Invio fallito' }); if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Invio fallito') });
} }
); );
}; };
@@ -433,7 +449,7 @@ const Ar1AdminConfig = () => {
setEditEmailOpen(false); setEditEmailOpen(false);
loadEmailTemplates(); loadEmailTemplates();
}, },
(err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Salvataggio fallito' }); } (err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Salvataggio fallito') }); }
); );
}; };
@@ -444,7 +460,7 @@ const Ar1AdminConfig = () => {
setPreviewSubject(resp.subject); setPreviewSubject(resp.subject);
setPreviewHtml(resp.body_html); setPreviewHtml(resp.body_html);
}, },
(err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: err?.detail || 'Anteprima fallita' }); } (err) => { if (toast.current) toast.current.show({ severity: 'error', summary: 'Errore', detail: formatErrorDetail(err?.detail, 'Anteprima fallita') }); }
); );
}; };