[UI] Bootstrap Italia rollout (Opzione B) — vendor self-hostato + 16 pagine -bi + sidebar BI
Pipeline a trittici (Esecutore->Revisore->Controllore avversariale), 6 trittici T1-T6. Tutto ADDITIVO: nessuna pagina/JS live modificata, produzione intatta. Swap non incluso. - public/vendor/bootstrap-italia/ : BI v2.18.1 self-hostato (CSS+JS bundle+16 woff2+sprite), MAI CDN - 16 pagine *-bi.html (login/register/onboarding/forgot/reset/index + dashboard/assessment/ risks/incidents/policies/supply-chain/training/assets/reports/settings) - public/js/common-bi.js : sidebar/topbar in markup BI (additivo, riusa stessi ID + common.js) - Verifica sul path servito reale: 16/16 -> 200, 0 CDN, 0 FontAwesome residui, parita ID/hook completa, WCAG AA - docs/SISTEMA_TRITTICI_AGENTI.md : design del sistema a trittici Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
06c94b3c05
commit
4d676f2e21
@@ -0,0 +1,170 @@
|
||||
/* ═══════════════════════════════════════════════════════════════════
|
||||
* common-bi.js — Override Bootstrap Italia per la chrome (sidebar)
|
||||
* ───────────────────────────────────────────────────────────────────
|
||||
* PILOTA "App-pages onda B" (T5).
|
||||
*
|
||||
* Regole rispettate:
|
||||
* - ADDITIVO: NON sovrascrive common.js. Va caricato DOPO common.js e
|
||||
* ridefinisce SOLO loadSidebar() con markup Bootstrap Italia.
|
||||
* - ZERO backend: riusa tutti gli helper di common.js (_loadUserInfo,
|
||||
* _setupMobileToggle, _loadVersionFooter, _loadFirmBranding,
|
||||
* _renderImpersonateBanner, switchLang, _getSavedLang, icon*()).
|
||||
* - Stessi ID DOM della sidebar originale (sidebar-user-name,
|
||||
* sidebar-user-avatar, sidebar-user-role, sidebar-org-switcher,
|
||||
* sidebar-version, .lang-btn) → gli helper esistenti funzionano
|
||||
* invariati senza toccare api.js.
|
||||
* - Accessibilità: <nav aria-label>, link active con aria-current,
|
||||
* bottoni con aria-label, focus nativo BI.
|
||||
* - I link puntano alle varianti "-bi.html" quando esistono, con
|
||||
* fallback all'originale per le pagine non ancora convertite.
|
||||
* ═══════════════════════════════════════════════════════════════════ */
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// Pagine già convertite a Bootstrap Italia in questa onda (+ login).
|
||||
// Per queste la navigazione resta in contesto "-bi". Le altre puntano
|
||||
// ancora all'originale (parità funzionale, nessun link rotto).
|
||||
var BI_PAGES = {
|
||||
'login.html': 'login-bi.html',
|
||||
'dashboard.html': 'dashboard-bi.html',
|
||||
'assessment.html': 'assessment-bi.html',
|
||||
'risks.html': 'risks-bi.html',
|
||||
'incidents.html': 'incidents-bi.html',
|
||||
'policies.html': 'policies-bi.html',
|
||||
'supply-chain.html': 'supply-chain-bi.html',
|
||||
'training.html': 'training-bi.html',
|
||||
'assets.html': 'assets-bi.html',
|
||||
'reports.html': 'reports-bi.html',
|
||||
'settings.html': 'settings-bi.html'
|
||||
};
|
||||
|
||||
function biHref(href) {
|
||||
return BI_PAGES[href] || href;
|
||||
}
|
||||
|
||||
function loadSidebarBI() {
|
||||
var container = document.getElementById('sidebar');
|
||||
if (!container) return;
|
||||
|
||||
// La pagina corrente può essere sia "x.html" sia "x-bi.html":
|
||||
// normalizziamo togliendo il suffisso -bi per l'evidenziazione active.
|
||||
var raw = window.location.pathname.split('/').pop() || 'dashboard.html';
|
||||
var currentBase = raw.replace(/-bi\.html$/, '.html');
|
||||
|
||||
// Stesso albero di navigazione di common.js (icone riusate dagli
|
||||
// helper globali icon*()). label/href/i18nKey identici.
|
||||
var navItems = [
|
||||
{
|
||||
label: 'Principale', i18nKey: 'nav.main',
|
||||
items: [
|
||||
{ name: 'Dashboard', href: 'dashboard.html', icon: iconGrid(), i18nKey: 'nav.dashboard' },
|
||||
{ name: 'Compliance Journey', href: 'workflow.html', icon: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/></svg>' },
|
||||
{ name: 'Gap Analysis', href: 'assessment.html', icon: iconClipboardCheck(), i18nKey: 'nav.gap_analysis' },
|
||||
{ name: 'Gap Analysis ACN', href: 'acn-gap.html', icon: iconClipboardCheck(), i18nKey: 'nav.acn_gap' },
|
||||
{ name: 'Modello SGSI (ISO 27001)', href: 'isms.html', icon: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M2.166 4.999A11.954 11.954 0 0010 1.944 11.954 11.954 0 0017.834 5c.11.65.166 1.32.166 2.001 0 5.225-3.34 9.67-8 11.317C5.34 16.67 2 12.225 2 7c0-.682.057-1.35.166-2.001zm11.541 3.708a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>', i18nKey: 'nav.isms' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Gestione', i18nKey: 'nav.management',
|
||||
items: [
|
||||
{ name: 'Rischi', href: 'risks.html', icon: iconShieldExclamation(), i18nKey: 'nav.risks' },
|
||||
{ name: 'Incidenti', href: 'incidents.html', icon: iconBell(), i18nKey: 'nav.incidents' },
|
||||
{ name: 'Policy', href: 'policies.html', icon: iconDocumentText(), i18nKey: 'nav.policies' },
|
||||
{ name: 'Supply Chain', href: 'supply-chain.html', icon: iconLink(), i18nKey: 'nav.supply_chain' },
|
||||
{ name: 'Segnalazioni', href: 'whistleblowing.html', icon: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M5 9V7a5 5 0 0110 0v2a2 2 0 012 2v5a2 2 0 01-2 2H5a2 2 0 01-2-2v-5a2 2 0 012-2zm8-2v2H7V7a3 3 0 016 0z" clip-rule="evenodd"/></svg>' },
|
||||
{ name: 'Normative', href: 'normative.html', icon: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M4 4a2 2 0 012-2h4.586A2 2 0 0112 2.586L15.414 6A2 2 0 0116 7.414V16a2 2 0 01-2 2H6a2 2 0 01-2-2V4zm2 6a1 1 0 011-1h6a1 1 0 110 2H7a1 1 0 01-1-1zm1 3a1 1 0 100 2h6a1 1 0 100-2H7z" clip-rule="evenodd"/></svg>' },
|
||||
{ name: 'AI Cross-Analysis', href: 'cross-analysis.html', icon: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M13 6a3 3 0 11-6 0 3 3 0 016 0zM18 8a2 2 0 11-4 0 2 2 0 014 0zM14 15a4 4 0 00-8 0v3h8v-3zM6 8a2 2 0 11-4 0 2 2 0 014 0zM16 18v-3a5.972 5.972 0 00-.75-2.906A3.005 3.005 0 0119 15v3h-3zM4.75 12.094A5.973 5.973 0 004 15v3H1v-3a3 3 0 013.75-2.906z"/></svg>' },
|
||||
{ name: 'Knowledge Base', href: 'kb.html', icon: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M9 4.804A7.968 7.968 0 005.5 4c-1.255 0-2.443.29-3.5.804v10A7.969 7.969 0 015.5 14c1.669 0 3.218.51 4.5 1.385A7.962 7.962 0 0114.5 14c1.255 0 2.443.29 3.5.804v-10A7.968 7.968 0 0014.5 4c-1.255 0-2.443.29-3.5.804V12a1 1 0 11-2 0V4.804z"/></svg>' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Operativo', i18nKey: 'nav.operations',
|
||||
items: [
|
||||
{ name: 'Formazione', href: 'training.html', icon: iconAcademicCap(), i18nKey: 'nav.training' },
|
||||
{ name: 'Asset', href: 'assets.html', icon: iconServer(), i18nKey: 'nav.assets' },
|
||||
{ name: 'Audit & Report', href: 'reports.html', icon: iconChartBar(), i18nKey: 'nav.audit' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Sistema', i18nKey: 'nav.system',
|
||||
items: [
|
||||
{ name: 'Guida all\'uso', href: 'guida.html', icon: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/></svg>' },
|
||||
{ name: 'Impostazioni', href: 'settings.html', icon: iconCog(), i18nKey: 'nav.settings' },
|
||||
{ name: 'Architettura', href: 'architecture.html', icon: iconCubeTransparent() },
|
||||
{ name: 'Simulazione Demo', href: 'simulate.html', icon: '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd"/></svg>' },
|
||||
{ name: 'Integrazioni Esterne', href: 'integrazioniext.html', icon: '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M11 3a1 1 0 100 2h2.586l-6.293 6.293a1 1 0 101.414 1.414L15 6.414V9a1 1 0 102 0V4a1 1 0 00-1-1h-5z"/><path d="M5 5a2 2 0 00-2 2v8a2 2 0 002 2h8a2 2 0 002-2v-3a1 1 0 10-2 0v3H5V7h3a1 1 0 000-2H5z"/></svg>' }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
var html = '';
|
||||
|
||||
// Brand
|
||||
html += '' +
|
||||
'<div class="sidebar-brand">' +
|
||||
'<div class="sidebar-brand-icon">' +
|
||||
'<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4zm0 2.18l7 3.12v4.7c0 4.83-3.23 9.36-7 10.57-3.77-1.21-7-5.74-7-10.57V6.3l7-3.12z"/><path d="M10 12.5l-2-2-1.41 1.41L10 15.32l5.41-5.41L14 8.5l-4 4z"/></svg>' +
|
||||
'</div>' +
|
||||
'<div>' +
|
||||
'<h1 class="sidebar-logo-text">NIS2 Agile</h1>' +
|
||||
'<span>Compliance Platform</span>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
// Org-switcher (popolato async per consulenti — stesso id)
|
||||
html += '<div id="sidebar-org-switcher" style="display:none;padding:8px 12px;border-bottom:1px solid var(--gray-100);"></div>';
|
||||
|
||||
// Nav — markup BI: <nav> con aria-label, link con aria-current
|
||||
html += '<nav class="sidebar-nav" aria-label="Navigazione principale">';
|
||||
navItems.forEach(function (section) {
|
||||
var sectionI18n = section.i18nKey ? ' data-i18n="' + section.i18nKey + '"' : '';
|
||||
html += '<div class="sidebar-nav-label"' + sectionI18n + '>' + section.label + '</div>';
|
||||
section.items.forEach(function (item) {
|
||||
var isActive = currentBase === item.href;
|
||||
var cls = isActive ? 'active' : '';
|
||||
var aria = isActive ? ' aria-current="page"' : '';
|
||||
var spanI18n = item.i18nKey ? ' data-i18n="' + item.i18nKey + '"' : '';
|
||||
html += '<a href="' + biHref(item.href) + '" class="' + cls + '"' + aria + '>' +
|
||||
item.icon + '<span' + spanI18n + '>' + item.name + '</span>' +
|
||||
'</a>';
|
||||
});
|
||||
});
|
||||
html += '</nav>';
|
||||
|
||||
// Footer: lang toggle + user info + version (stessi id degli helper)
|
||||
html += '' +
|
||||
'<div class="sidebar-footer">' +
|
||||
'<div class="sidebar-lang-toggle" id="sidebar-lang-toggle" role="group" aria-label="Lingua">' +
|
||||
'<button class="lang-btn ' + (_getSavedLang() === 'it' ? 'active' : '') + '" onclick="switchLang(\'it\')" title="Italiano" aria-label="Italiano">IT</button>' +
|
||||
'<button class="lang-btn ' + (_getSavedLang() === 'en' ? 'active' : '') + '" onclick="switchLang(\'en\')" title="English" aria-label="English">EN</button>' +
|
||||
'</div>' +
|
||||
'<div class="sidebar-user">' +
|
||||
'<div class="sidebar-user-avatar" id="sidebar-user-avatar" aria-hidden="true">--</div>' +
|
||||
'<div class="sidebar-user-info">' +
|
||||
'<div class="sidebar-user-name" id="sidebar-user-name">Caricamento...</div>' +
|
||||
'<div class="sidebar-user-role" id="sidebar-user-role">Utente</div>' +
|
||||
'</div>' +
|
||||
'<button class="sidebar-logout-btn" onclick="api.logout()" title="Esci" aria-label="Esci dall\'applicazione">' +
|
||||
'<svg width="18" height="18" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path fill-rule="evenodd" d="M3 3a1 1 0 00-1 1v12a1 1 0 001 1h6a1 1 0 100-2H4V5h5a1 1 0 100-2H3zm11.707 3.293a1 1 0 010 1.414L12.414 10l2.293 2.293a1 1 0 01-1.414 1.414l-3-3a1 1 0 010-1.414l3-3a1 1 0 011.414 0z" clip-rule="evenodd"/><path fill-rule="evenodd" d="M16 10a1 1 0 00-1-1H8a1 1 0 100 2h7a1 1 0 001-1z" clip-rule="evenodd"/></svg>' +
|
||||
'</button>' +
|
||||
'</div>' +
|
||||
'<div class="sidebar-version" id="sidebar-version" title="Versione applicazione" tabindex="0" role="button"' +
|
||||
' style="text-align:center; padding:6px 12px; font-size:.68rem; color:var(--gray-400); cursor:pointer; border-top:1px solid var(--gray-100); margin-top:4px;"' +
|
||||
' onclick="_showVersionChangelog()" onkeydown="if(event.key===\'Enter\'||event.key===\' \'){event.preventDefault();_showVersionChangelog();}">' +
|
||||
'v—' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
|
||||
container.innerHTML = html;
|
||||
|
||||
// Riuso integrale degli helper di common.js
|
||||
if (typeof _loadUserInfo === 'function') _loadUserInfo();
|
||||
if (typeof _setupMobileToggle === 'function') _setupMobileToggle();
|
||||
if (typeof _loadVersionFooter === 'function') _loadVersionFooter();
|
||||
if (typeof _loadFirmBranding === 'function') _loadFirmBranding();
|
||||
if (typeof _renderImpersonateBanner === 'function') _renderImpersonateBanner();
|
||||
}
|
||||
|
||||
// Override globale: le pagine -bi chiamano loadSidebar() come le originali.
|
||||
window.loadSidebar = loadSidebarBI;
|
||||
})();
|
||||
Reference in New Issue
Block a user