[FEAT] Supply chain UI: i18n sp.* IT/EN + import mostra errors[] dettaglio righe scartate

- public/js/i18n.js: +38 chiavi sp.* (IT+EN) per pulsanti header, categorie, template, import.
- supply-chain.html: data-i18n sui 4 pulsanti header; l'import CSV ora mostra il
  dettaglio delle righe scartate (d.errors[] dal backend) in un <details> e tiene
  aperta la modale se ci sono errori (prima si chiudeva sempre, errori invisibili).
- version 1.10.1.

Inline JS validato (node --check). File statici -> live via nginx.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-05-31 14:37:22 +02:00
co-authored by Claude Opus 4.8
parent ce0387ed4f
commit b7f361b87c
3 changed files with 58 additions and 7 deletions
+12 -6
View File
@@ -370,10 +370,10 @@
<header class="content-header">
<h2 data-i18n="supply_chain.title">Sicurezza Supply Chain</h2>
<div class="content-header-actions">
<button class="btn btn-secondary" onclick="openCategoriesModal()"><i class="fas fa-tags"></i> Categorie</button>
<button class="btn btn-secondary" onclick="openTemplatesModal()"><i class="fas fa-clipboard-list"></i> Template</button>
<button class="btn btn-secondary" onclick="openImportModal()"><i class="fas fa-file-import"></i> Importa</button>
<button class="btn btn-primary" onclick="openCreateModal()">+ Nuovo Fornitore</button>
<button class="btn btn-secondary" onclick="openCategoriesModal()"><i class="fas fa-tags"></i> <span data-i18n="sp.categories_btn">Categorie</span></button>
<button class="btn btn-secondary" onclick="openTemplatesModal()"><i class="fas fa-clipboard-list"></i> <span data-i18n="sp.templates_btn">Template</span></button>
<button class="btn btn-secondary" onclick="openImportModal()"><i class="fas fa-file-import"></i> <span data-i18n="sp.import_btn">Importa</span></button>
<button class="btn btn-primary" onclick="openCreateModal()"><span data-i18n="sp.new_supplier">+ Nuovo Fornitore</span></button>
</div>
</header>
@@ -1277,10 +1277,16 @@
const res = await api.importSuppliers(rows);
if (res.success) {
const d = res.data || {};
resEl.innerHTML = `<span style="color:#16a34a;">Completato: creati ${d.created||0}, aggiornati ${d.updated||0}, saltati ${d.skipped||0}.</span>`;
let html = `<span style="color:#16a34a;">Completato: creati ${d.created||0}, aggiornati ${d.updated||0}, saltati ${d.skipped||0}.</span>`;
if (Array.isArray(d.errors) && d.errors.length) {
const items = d.errors.slice(0, 20).map(e => `<li>Riga ${(e.row != null ? e.row + 1 : '?')}: ${escapeHtml(String(e.error || ''))}</li>`).join('');
html += `<details style="margin-top:8px;"><summary style="cursor:pointer;color:#b45309;">${d.errors.length} riga/e scartata/e — dettagli</summary><ul style="margin:6px 0 0 18px;color:#b45309;font-size:13px;">${items}</ul></details>`;
}
resEl.innerHTML = html;
showNotification(`Import: ${d.created||0} creati, ${d.updated||0} aggiornati`, 'success');
loadSuppliers();
setTimeout(() => { const m = document.getElementById('importModalDyn'); if (m) m.remove(); }, 1800);
const keepOpen = Array.isArray(d.errors) && d.errors.length > 0;
if (!keepOpen) setTimeout(() => { const m = document.getElementById('importModalDyn'); if (m) m.remove(); }, 1800);
} else {
resEl.innerHTML = `<span style="color:#dc2626;">${res.message || 'Errore import'}</span>`;
}