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):