From ac1c18c737dcac936d4f4cd6a5e68fb8bd6d56af Mon Sep 17 00:00:00 2001 From: BFLOWS Date: Thu, 23 Apr 2026 15:27:30 +0200 Subject: [PATCH] feat(ar1-admin): messaggi errore Pydantic tradotti in italiano umano MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ora il toast error mostra es. 'Versione: deve essere nel formato X.Y.Z (esempio: 1.1.0)' invece di 'version: String should match pattern ^\d+\.\d+\.\d+$'. 2 nuove utility: - FIELD_LABELS_IT: mapping field name -> label IT (version -> 'Versione', kind -> 'Tipo regola', subject -> 'Oggetto', body_html -> 'Corpo HTML', validity_days -> 'Validita', ecc. — 20 campi mappati) - translatePydanticMsg(msg, type, ctx): riconosce i type Pydantic comuni: * string_pattern_mismatch + ctx.pattern semver -> 'deve essere nel formato X.Y.Z (esempio: 1.1.0)' * missing -> 'campo obbligatorio mancante' * string_type / int_type / bool_type -> 'deve essere stringa/intero/bool' * greater_than_equal / less_than_equal -> 'deve essere almeno N / al massimo N' (usando ctx.ge / ctx.le) * string_too_short / too_long -> 'troppo corto/lungo (min/max N caratteri)' * value_error -> rimuove il prefisso 'Value error, ' * fallback: msg originale (non rompe nulla per casi non mappati) formatErrorDetail ora usa entrambi: estrae l'ultimo loc (field name piu preciso), lo traduce via FIELD_LABELS_IT, concatena col msg tradotto. --- src/modules/ar1/pages/Ar1AdminConfig.js | 60 ++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/src/modules/ar1/pages/Ar1AdminConfig.js b/src/modules/ar1/pages/Ar1AdminConfig.js index ace11ed..3d12427 100644 --- a/src/modules/ar1/pages/Ar1AdminConfig.js +++ b/src/modules/ar1/pages/Ar1AdminConfig.js @@ -59,15 +59,65 @@ const PEC_KIND_OPTIONS = [ ]; -// Normalizza il campo detail che puo essere string o array Pydantic [{loc, msg, type}] +// Mapping field name -> label italiano umano +const FIELD_LABELS_IT = { + version: 'Versione', + layout_config: 'Configurazione layout', + activate_now: 'Attiva subito', + kind: 'Tipo regola', + offset_days: 'Giorni offset', + recurring_interval_days: 'Intervallo ricorrenza', + enabled: 'Attiva', + description: 'Descrizione', + subject: 'Oggetto', + body_html: 'Corpo HTML', + body_text: 'Corpo testo', + validity_days: 'Validita', + popup_dismiss_hours: 'Ore dismiss pop-up', + company_document_category_id: 'Categoria documento', + company_ids: 'ID aziende', + only_expired: 'Solo scadute', + only_missing: 'Solo senza AR1', + dry_run: 'Simulazione', +}; + +// Traduce messaggi Pydantic comuni in italiano umano +const translatePydanticMsg = (msg, pydanticType, ctx) => { + if (!msg) return ''; + const m = String(msg); + // semver pattern + if (m.includes("should match pattern") && ctx?.pattern?.includes('\\d+\\.\\d+\\.\\d+')) { + return 'deve essere nel formato X.Y.Z (esempio: 1.1.0)'; + } + // string pattern generico + if (pydanticType === 'string_pattern_mismatch') return `formato non valido (atteso: ${ctx?.pattern || 'pattern specifico'})`; + // tipi base + if (pydanticType === 'missing') return 'campo obbligatorio mancante'; + if (pydanticType === 'string_type') return 'deve essere una stringa'; + if (pydanticType === 'int_type' || pydanticType === 'int_parsing') return 'deve essere un numero intero'; + if (pydanticType === 'bool_type') return 'deve essere vero o falso'; + // range + if (pydanticType === 'greater_than_equal') return `deve essere almeno ${ctx?.ge}`; + if (pydanticType === 'less_than_equal') return `deve essere al massimo ${ctx?.le}`; + if (pydanticType === 'string_too_short') return `troppo corto (minimo ${ctx?.min_length || ''} caratteri)`; + if (pydanticType === 'string_too_long') return `troppo lungo (massimo ${ctx?.max_length || ''} caratteri)`; + // value error (validator custom) + if (pydanticType === 'value_error') return m.replace(/^Value error,\s*/, ''); + // fallback: lascio msg originale + return m; +}; + +// Normalizza il campo detail che puo essere string o array Pydantic [{loc, msg, type, ctx}] 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; + const locArr = Array.isArray(e.loc) ? e.loc.filter(x => x !== 'body') : []; + const lastLoc = locArr[locArr.length - 1]; + const fieldLabel = FIELD_LABELS_IT[lastLoc] || lastLoc || ''; + const msg = translatePydanticMsg(e.msg, e.type, e.ctx); + return fieldLabel ? `${fieldLabel}: ${msg}` : msg; }).join('; '); } if (typeof detail === 'object') return JSON.stringify(detail); @@ -75,7 +125,7 @@ const formatErrorDetail = (detail, fallback) => { }; /** - * Ar1AdminConfig — configurazione AR1 per superadmin. (build 1776950637) + * Ar1AdminConfig — configurazione AR1 per superadmin. (build 1776950842) * URL: /ar1-admin * * 5 sezioni (TabView):