feat(ar1-admin): messaggi errore Pydantic tradotti in italiano umano
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.
This commit is contained in:
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user