[UI] SWAP V2: Bootstrap Italia ora UFFICIALE su 27 pagine app (kill della vecchia UI)

Promosse le pagine -bi a nomi ufficiali (contenuto BI), link -bi->plain, titoli '(BI)' rimossi.
La vecchia UI delle pagine app e sostituita. Backup: git (commit 36967f9) + /tmp/nis2-v1-backup.

Promosse (27): auth (login/register/onboarding/forgot/reset) + app (dashboard/assessment/
risks/incidents/policies/supply-chain/training/assets/reports/settings/isms/acn-gap/normative/
whistleblowing/kb/cross-analysis/service-continuity/guida/companies) + admin (index/orgs/users).
common-bi.js: sidebar BI ora linka le pagine ufficiali.

ESCLUSA: index.html (landing marketing 1298 righe) -> conversione dedicata a parte (la -bi
era 557 righe, rischio perdita contenuti). Pagine marketing/demo (presentation/simulate/
workflow/architecture/index-en) restano vecchio stile (fuori scope app autenticata).

Verifica produzione: 27/27 servite 200 con BI, 0 link -bi residui, 0 '(BI)' nei titoli,
auth intatta (401 su pwd errata), common-bi.js node --check OK.

Rollback: git revert HEAD  (oppure ripristino da /tmp/nis2-v1-backup).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-12 14:49:15 +02:00
co-authored by Claude Opus 4.8
parent 36967f9452
commit 1c64211685
28 changed files with 2474 additions and 3105 deletions
+36 -22
View File
@@ -4,6 +4,10 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Supply Chain - NIS2 Agile</title>
<!-- Bootstrap Italia v2.18.1 self-hostato (regola: MAI CDN). Caricato PRIMA di style.css
cosi' style.css (design system esistente) resta autorevole per i widget renderizzati
via JS (tabelle/modali/form) -> parita' funzionale assoluta. BI veste la chrome. -->
<link rel="stylesheet" href="/vendor/bootstrap-italia/dist/css/bootstrap-italia.min.css">
<link rel="stylesheet" href="/css/style.css">
<style>
/* ── Supply Chain Page Styles ─────────────────────────── */
@@ -368,11 +372,11 @@
<main class="main-content">
<header class="content-header">
<h2 data-i18n="supply_chain.title">Sicurezza Supply Chain</h2>
<h1 class="h2" data-i18n="supply_chain.title">Sicurezza Supply Chain</h1>
<div class="content-header-actions">
<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-secondary" onclick="openCategoriesModal()"><svg class="icon icon-sm" aria-hidden="true"><use href="vendor/bootstrap-italia/dist/svg/sprites.svg#it-bookmark"></use></svg> <span data-i18n="sp.categories_btn">Categorie</span></button>
<button class="btn btn-secondary" onclick="openTemplatesModal()"><svg class="icon icon-sm" aria-hidden="true"><use href="vendor/bootstrap-italia/dist/svg/sprites.svg#it-files"></use></svg> <span data-i18n="sp.templates_btn">Template</span></button>
<button class="btn btn-secondary" onclick="openImportModal()"><svg class="icon icon-sm" aria-hidden="true"><use href="vendor/bootstrap-italia/dist/svg/sprites.svg#it-upload"></use></svg> <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>
@@ -428,14 +432,14 @@
<!-- Filters + Table -->
<div id="suppliers-list-section">
<div class="filters-bar">
<select class="form-select" id="filter-criticality" onchange="applyFilters()">
<select class="form-select" id="filter-criticality" onchange="applyFilters()" aria-label="Filtra per criticità">
<option value="">Tutte le Criticita'</option>
<option value="critical">Critico</option>
<option value="high">Alto</option>
<option value="medium">Medio</option>
<option value="low">Basso</option>
</select>
<select class="form-select" id="filter-status" onchange="applyFilters()">
<select class="form-select" id="filter-status" onchange="applyFilters()" aria-label="Filtra per stato">
<option value="">Tutti gli Stati</option>
<option value="active">Attivo</option>
<option value="under_review">In Revisione</option>
@@ -462,6 +466,16 @@
<script src="/js/api.js"></script>
<script src="/js/common.js"></script>
<!-- Bootstrap Italia bundle (componenti) + override sidebar BI (common-bi.js).
common-bi.js va DOPO common.js: ridefinisce solo loadSidebar() con markup BI,
riusando tutti gli helper di common.js (zero backend). -->
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
<script>
if (window.bootstrap && bootstrap.loadFonts) {
bootstrap.loadFonts('/vendor/bootstrap-italia/dist/fonts');
}
</script>
<script src="/js/common-bi.js"></script>
<script src="/js/i18n.js"></script>
<script src="/js/help.js"></script>
<script>
@@ -920,31 +934,31 @@
<form id="supplier-form" onsubmit="return false;">
<div class="form-row" style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
<div class="form-group">
<label class="form-label">Nome Fornitore <span class="required">*</span></label>
<label for="form-name" class="form-label">Nome Fornitore <span class="required">*</span></label>
<input type="text" class="form-input" id="form-name" value="${isEdit ? escapeHtml(supplier.name) : ''}" required placeholder="Nome dell'azienda">
</div>
<div class="form-group">
<label class="form-label">Partita IVA</label>
<label for="form-vat" class="form-label">Partita IVA</label>
<input type="text" class="form-input" id="form-vat" value="${isEdit && supplier.vat_number ? escapeHtml(supplier.vat_number) : ''}" placeholder="IT01234567890">
</div>
</div>
<div class="form-row" style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
<div class="form-group">
<label class="form-label">Referente</label>
<label for="form-contact-name" class="form-label">Referente</label>
<input type="text" class="form-input" id="form-contact-name" value="${isEdit && supplier.contact_name ? escapeHtml(supplier.contact_name) : ''}" placeholder="Nome e cognome">
</div>
<div class="form-group">
<label class="form-label">Email Contatto</label>
<label for="form-contact-email" class="form-label">Email Contatto</label>
<input type="email" class="form-input" id="form-contact-email" value="${isEdit && supplier.contact_email ? escapeHtml(supplier.contact_email) : ''}" placeholder="email@azienda.it">
</div>
</div>
<div class="form-row" style="display:grid;grid-template-columns:2fr 1fr;gap:16px;">
<div class="form-group">
<label class="form-label">Tipo Servizio <span class="required">*</span></label>
<label for="form-service-type" class="form-label">Tipo Servizio <span class="required">*</span></label>
<input type="text" class="form-input" id="form-service-type" value="${isEdit && supplier.service_type ? escapeHtml(supplier.service_type) : ''}" required placeholder="es. Cloud Hosting, Sviluppo SW, Consulenza">
</div>
<div class="form-group">
<label class="form-label">Criticita'</label>
<label for="form-criticality" class="form-label">Criticita'</label>
<select class="form-select" id="form-criticality">
${criticalityOptions}
</select>
@@ -958,21 +972,21 @@
<small style="color:var(--gray-500);font-size:0.75rem;">Classifica il fornitore (es. Cloud Provider). Gestisci le categorie dalla tab "Categorie".</small>
</div>
<div class="form-group">
<label class="form-label">Descrizione Servizio</label>
<label for="form-service-desc" class="form-label">Descrizione Servizio</label>
<textarea class="form-textarea" id="form-service-desc" rows="3" placeholder="Descrivi il servizio fornito...">${isEdit && supplier.service_description ? escapeHtml(supplier.service_description) : ''}</textarea>
</div>
<div class="form-row" style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
<div class="form-group">
<label class="form-label">Inizio Contratto</label>
<label for="form-contract-start" class="form-label">Inizio Contratto</label>
<input type="date" class="form-input" id="form-contract-start" value="${isEdit && supplier.contract_start_date ? supplier.contract_start_date.substring(0, 10) : ''}">
</div>
<div class="form-group">
<label class="form-label">Scadenza Contratto</label>
<label for="form-contract-expiry" class="form-label">Scadenza Contratto</label>
<input type="date" class="form-input" id="form-contract-expiry" value="${isEdit && supplier.contract_expiry_date ? supplier.contract_expiry_date.substring(0, 10) : ''}">
</div>
</div>
<div class="form-group">
<label class="form-label">Note</label>
<label for="form-notes" class="form-label">Note</label>
<textarea class="form-textarea" id="form-notes" rows="3" placeholder="Note aggiuntive...">${isEdit && supplier.notes ? escapeHtml(supplier.notes) : ''}</textarea>
</div>
</form>
@@ -1198,7 +1212,7 @@
ov.innerHTML = `
<div style="background:#fff;border-radius:10px;max-width:640px;width:100%;max-height:90vh;overflow:auto;">
<div style="display:flex;justify-content:space-between;align-items:center;padding:16px 20px;border-bottom:1px solid #e2e8f0;">
<h3 style="margin:0;"><i class="fas fa-file-import"></i> Importa fornitori</h3>
<h3 style="margin:0;"><svg class="icon icon-sm" aria-hidden="true"><use href="vendor/bootstrap-italia/dist/svg/sprites.svg#it-upload"></use></svg> Importa fornitori</h3>
<button onclick="document.getElementById('importModalDyn').remove()" style="background:none;border:none;font-size:22px;cursor:pointer;line-height:1;">&times;</button>
</div>
<div style="padding:20px;">
@@ -1208,14 +1222,14 @@
<code>service_type</code>, <code>criticality</code> (low/medium/high/critical),
<code>category_slug</code>, <code>external_ref</code>. I record con stesso <code>external_ref</code> vengono aggiornati.
</p>
<input type="file" id="importFile" accept=".csv,text/csv" class="form-input" style="margin-bottom:10px;">
<textarea id="importCsv" class="form-input" rows="8" style="width:100%;font-family:monospace;font-size:13px;"
<input type="file" id="importFile" accept=".csv,text/csv" class="form-input" style="margin-bottom:10px;" aria-label="Seleziona file CSV fornitori da importare">
<textarea id="importCsv" class="form-input" rows="8" style="width:100%;font-family:monospace;font-size:13px;" aria-label="Dati CSV fornitori"
placeholder="name,vat_number,contact_email,criticality,category_slug&#10;ACME Srl,IT01234567890,security@acme.it,high,cloud_provider"></textarea>
<div id="importResult" style="margin-top:10px;font-size:14px;"></div>
</div>
<div style="display:flex;justify-content:flex-end;gap:10px;padding:16px 20px;border-top:1px solid #e2e8f0;">
<button class="btn btn-secondary" onclick="document.getElementById('importModalDyn').remove()">Annulla</button>
<button class="btn btn-primary" id="importRunBtn" onclick="runSupplierImport()"><i class="fas fa-upload"></i> Importa</button>
<button class="btn btn-primary" id="importRunBtn" onclick="runSupplierImport()"><svg class="icon icon-sm icon-white" aria-hidden="true"><use href="vendor/bootstrap-italia/dist/svg/sprites.svg#it-upload"></use></svg> Importa</button>
</div>
</div>`;
document.body.appendChild(ov);
@@ -1272,7 +1286,7 @@
resEl.innerHTML = '<span style="color:#dc2626;">Troppi record (max 1000).</span>';
return;
}
btn.disabled = true; btn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Importo...';
btn.disabled = true; btn.innerHTML = '<span class="progress-spinner progress-spinner-active size-sm progress-spinner-inverse" role="status"><span class="visually-hidden">Caricamento…</span></span> Importo...';
try {
const res = await api.importSuppliers(rows);
if (res.success) {
@@ -1293,7 +1307,7 @@
} catch (e) {
resEl.innerHTML = `<span style="color:#dc2626;">Errore: ${(e && e.message) || e}</span>`;
} finally {
btn.disabled = false; btn.innerHTML = '<i class="fas fa-upload"></i> Importa';
btn.disabled = false; btn.innerHTML = '<svg class="icon icon-sm icon-white" aria-hidden="true"><use href="vendor/bootstrap-italia/dist/svg/sprites.svg#it-upload"></use></svg> Importa';
}
}