[FIX] UI V3 — allineamento help/i18n/KB/ARIA al menu top-nav

Dopo il revamp V3, allineati i layer che descrivevano ancora la sidebar V2:
- topnav-v3.js: aggiunte le pagine orfane in nav (Matrice RACI → Struttura
  interna, Scadenziario → Monitoraggio, Compliance Journey → Guida) + cablato
  data-i18n su gruppi e voci del mega-menu (traduzione IT/EN come in V2).
- i18n.js: nuove chiavi nav V3 (gruppi mega-menu + connettori/whistleblowing/
  normative/cross-analysis/kb/architettura/simulazione/integrazioni/workflow/
  audit ACN/registra incidente).
- AIService.php navigationMapBlock: riscritta sul menu IN ALTO V3 (9 gruppi +
  pulsante "Registra incidente"), sync con topnav-v3.js NAV_GROUPS; ARIA non
  parla più di "barra laterale".
- help.js: corrette le 3 menzioni "sidebar" → barra/menu in alto.
- KB nis2_kb: nuovo docs/kb/V3_UI_NAVIGAZIONE.md + scripts/ingest-v3-ui-doc.php
  (ingerito host-side: 3 chunk SYSTEM, rimossi i chunk V2; ARIA RAG ora guida
  sul top-nav).
- Cache-buster ?v=20260627n sui 3 JS modificati su tutte le pagine.

Deploy: JS/HTML statici live via bind-mount; php-fpm reload (host + nis2-app)
per AIService.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-27 16:38:07 +02:00
co-authored by Claude Opus 4.8
parent a4150b5389
commit 58b4bdbe5e
41 changed files with 379 additions and 123 deletions
+3 -3
View File
@@ -165,9 +165,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -88,9 +88,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -95,9 +95,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -1125,9 +1125,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
if (!checkAuth()) throw new Error('Not authenticated');
loadSidebar(); // = loadTopnavV3()
+3 -3
View File
@@ -273,9 +273,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -380,9 +380,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -193,9 +193,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -142,9 +142,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -195,9 +195,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script src="/js/competenze.js?v=20260627"></script>
<script>
function v3Icons() { if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} } }
+3 -3
View File
@@ -101,9 +101,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script src="/js/connettori-discovery.js?v=20260627"></script>
<script>
if (typeof checkAuth === 'function' && !checkAuth()) {
+3 -3
View File
@@ -158,9 +158,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
'use strict';
/* Controlli periodici (control monitoring §9.1/A.8.16): controlli ricorrenti + esecuzioni (evidenza) + NC/AC. */
+3 -3
View File
@@ -225,9 +225,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Icone inline (Lucide stroke) usate nelle stringhe HTML generate dal JS.
// Renderizzate immediatamente (no createIcons re-run), self-host, niente sprite BI. ──
+3 -3
View File
@@ -115,9 +115,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script src="/js/dashboard.js?v=20260627"></script>
<script>
// ── Auth check ───────────────────────────────────────────
+3 -3
View File
@@ -1180,9 +1180,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// La guida è accessibile anche da non autenticati (utile per onboarding)
// ma se sei loggato carichi chrome + i18n normalmente. ZERO modifiche backend.
+3 -3
View File
@@ -362,9 +362,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth & Init ──────────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -934,9 +934,9 @@ curl -H <span class="str">"X-API-Key: nis2_TUA_CHIAVE"</span> \
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
if (!checkAuth()) throw new Error('Not authenticated');
loadSidebar(); // = loadTopnavV3()
+3 -3
View File
@@ -163,9 +163,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -202,9 +202,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script src="/js/isms.js?v=20260627v3"></script>
<script>
// Icone Lucide della chrome + crumb/page-head statici. L'init dati e il wiring
+3 -3
View File
@@ -1134,7 +1134,7 @@ const HelpSystem = (function () {
'<strong>Tema</strong>: chiaro, scuro o automatico (segue le impostazioni del sistema operativo).',
'<strong>Timezone</strong>: usata per visualizzare date e ore. Default Europe/Rome (CET/CEST). Cambia se lavori da un altro fuso.',
'<strong>Notifiche email</strong>: ricevi via email gli avvisi per incidenti, scadenze e training.',
'<strong>Notifiche in-app</strong>: mostra badge contatori nella sidebar per nuove notifiche.'
'<strong>Notifiche in-app</strong>: mostra un badge contatore per le nuove notifiche.'
]
},
{
@@ -1150,7 +1150,7 @@ const HelpSystem = (function () {
{
heading: 'Cambio organizzazione (multi-tenant)',
items: [
'Se sei membro di piu\' organizzazioni (es. consulente), nella sidebar appare un <strong>dropdown azienda</strong> in alto.',
'Se sei membro di piu\' organizzazioni (es. consulente), nella barra in alto appare un <strong>selettore azienda</strong>.',
'Click sul dropdown per vedere tutte le organizzazioni a cui hai accesso e quale è quella attualmente attiva (✓).',
'Selezionando un\'altra organizzazione il sistema emette un nuovo token con il contesto aggiornato e ricarica la pagina.',
'Il cambio è registrato nell\'audit log come <em>context_switch</em>.'
@@ -1161,7 +1161,7 @@ const HelpSystem = (function () {
items: [
'Il tab <strong>Branding</strong> consente a uno studio di consulenza di personalizzare l\'interfaccia che vedranno i suoi clienti.',
'<strong>Nome prodotto custom</strong>: sostituisce "NIS2 Agile" con il branding dello studio (es. "Studio Rossi NIS2 Suite").',
'<strong>URL Logo</strong>: link assoluto al logo dello studio (es. https://www.tuosito.it/logo.svg). Verra\' mostrato nella sidebar e nelle pagine di login.',
'<strong>URL Logo</strong>: link assoluto al logo dello studio (es. https://www.tuosito.it/logo.svg). Verra\' mostrato nell\'intestazione e nelle pagine di login.',
'<strong>Colori</strong>: il primario sostituisce il blu di default (intestazioni, pulsanti principali); il secondario sostituisce il ciano (badge, accenti).',
'Le modifiche vengono applicate automaticamente a tutti gli utenti del firm al ricaricamento della pagina.'
]
+20
View File
@@ -96,6 +96,26 @@ const I18n = (function () {
'nav.settings': { it: 'Impostazioni', en: 'Settings' },
'nav.help': { it: 'Guida', en: 'Help' },
'nav.logout': { it: 'Esci', en: 'Logout' },
// ── Voci nav UI V3 (top-nav mega-menu) ──
'nav.register_incident': { it: 'Registra incidente', en: 'Report incident' },
'nav.connectors': { it: 'Connettori Discovery', en: 'Discovery Connectors' },
'nav.whistleblowing': { it: 'Segnalazioni ricevute', en: 'Reports received' },
'nav.normative': { it: 'Normative', en: 'Regulations' },
'nav.cross_analysis': { it: 'AI Cross-Analysis', en: 'AI Cross-Analysis' },
'nav.kb': { it: 'Knowledge Base', en: 'Knowledge Base' },
'nav.architecture': { it: 'Architettura', en: 'Architecture' },
'nav.simulate': { it: 'Simulazione Demo', en: 'Demo Simulation' },
'nav.integrations': { it: 'Integrazioni Esterne', en: 'External Integrations' },
'nav.workflow': { it: 'Compliance Journey', en: 'Compliance Journey' },
'nav.audit_acn': { it: 'Audit ACN', en: 'ACN Audit' },
// ── Etichette gruppi mega-menu V3 ──
'nav.grp_stakeholders': { it: 'Stakeholders', en: 'Stakeholders' },
'nav.grp_internal': { it: 'Struttura interna', en: 'Internal structure' },
'nav.grp_risks': { it: 'Rischi', en: 'Risks' },
'nav.grp_monitoring': { it: 'Monitoraggio', en: 'Monitoring' },
'nav.grp_lexiso': { it: 'Lex e ISO', en: 'Law & ISO' },
'nav.grp_ai': { it: 'AI', en: 'AI' },
'nav.grp_guide': { it: 'Guida', en: 'Guide' },
// ── Dashboard ──────────────────────────────────
'dashboard.title': { it: 'Dashboard', en: 'Dashboard' },
+24 -4
View File
@@ -38,6 +38,7 @@
{ b: 'Organigramma', s: 'Ruoli, deleghe e responsabilità', href: 'organigramma.html', ic: 'network' },
{ b: 'Competenze', s: 'Skill matrix e copertura dei ruoli', href: 'competenze.html', ic: 'award' },
{ b: 'GAP Analysis', s: 'Scostamento tra stato attuale e atteso', href: 'assessment.html', ic: 'git-compare' },
{ b: 'Matrice RACI', s: 'Responsabilità per attività (R/A/C/I)', href: 'raci.html', ic: 'grid-3x3' },
{ b: 'Formazione', s: 'Piani formativi e completamento', href: 'training.html', ic: 'graduation-cap' }
]}]
},
@@ -57,7 +58,8 @@
{ b: 'Audit interni', s: 'Programma e esiti degli audit', href: 'internal-audits.html', ic: 'clipboard-check' },
{ b: 'Audit ACN', s: 'Verifiche e ispezioni dell’autorità', href: 'reports.html', ic: 'file-check' },
{ b: 'Controlli periodici', s: 'Verifiche ricorrenti e check-list', href: 'controlli-periodici.html', ic: 'list-checks' },
{ b: 'Riesame di Direzione', s: 'Esiti del riesame §9.3', href: 'management-review.html', ic: 'gauge' }
{ b: 'Riesame di Direzione', s: 'Esiti del riesame §9.3', href: 'management-review.html', ic: 'gauge' },
{ b: 'Scadenziario', s: 'Revisioni e adempimenti ricorrenti', href: 'review-schedule.html', ic: 'calendar-clock' }
]}]
},
{
@@ -80,6 +82,7 @@
cols: [
{ head: 'GUIDA', links: [
{ b: 'Guida all’uso', s: 'Manuale e istruzioni della piattaforma', href: 'guida.html', ic: 'life-buoy' },
{ b: 'Compliance Journey', s: 'Percorso guidato di conformità', href: 'workflow.html', ic: 'route' },
{ b: 'Integrazioni Esterne', s: 'Connettori e servizi collegati', href: 'integrazioniext.html', ic: 'plug' }
]},
{ head: 'SISTEMA', links: [
@@ -91,6 +94,23 @@
}
];
// ── i18n: traduzione voci nav (IT/EN) come nel V2. Chiave per href/gruppo. ──
var I18N_BY_HREF = {
'dashboard.html': 'nav.dashboard', 'calendario.html': 'nav.calendar',
'stakeholders.html': 'nav.stakeholders', 'supply-chain.html': 'nav.supply_chain', 'stakeholder-activities.html': 'nav.stk_activities',
'organigramma.html': 'nav.org_chart', 'competenze.html': 'nav.competences', 'assessment.html': 'nav.gap_analysis', 'raci.html': 'nav.raci', 'training.html': 'nav.training',
'risks.html': 'nav.risks', 'assets.html': 'nav.assets', 'policies.html': 'nav.policies', 'connettori-discovery.html': 'nav.connectors',
'whistleblowing.html': 'nav.whistleblowing', 'internal-audits.html': 'nav.internal_audits', 'reports.html': 'nav.audit_acn', 'controlli-periodici.html': 'nav.periodic_controls', 'management-review.html': 'nav.management_review', 'review-schedule.html': 'nav.review_schedule',
'normative.html': 'nav.normative', 'isms.html': 'nav.isms', 'misure-requisiti.html': 'nav.framework',
'cross-analysis.html': 'nav.cross_analysis', 'kb.html': 'nav.kb',
'guida.html': 'nav.help', 'workflow.html': 'nav.workflow', 'integrazioniext.html': 'nav.integrations', 'settings.html': 'nav.settings', 'architecture.html': 'nav.architecture', 'simulate.html': 'nav.simulate'
};
var GROUP_I18N = {
'Stakeholders': 'nav.grp_stakeholders', 'Struttura interna': 'nav.grp_internal', 'Rischi': 'nav.grp_risks',
'Monitoraggio': 'nav.grp_monitoring', 'Lex e ISO': 'nav.grp_lexiso', 'AI': 'nav.grp_ai', 'Guida': 'nav.grp_guide'
};
function i18nAttr(key) { return key ? ' data-i18n="' + key + '"' : ''; }
function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c];
@@ -118,7 +138,7 @@
var links = col.links.map(function (l) {
return '<a class="mega-link" href="' + esc(l.href) + '">' +
'<span class="mega-ic"><i data-lucide="' + esc(l.ic) + '" class="ic"></i></span>' +
'<span><b>' + esc(l.b) + '</b><span>' + esc(l.s) + '</span></span></a>';
'<span><b' + i18nAttr(I18N_BY_HREF[l.href]) + '>' + esc(l.b) + '</b><span>' + esc(l.s) + '</span></span></a>';
}).join('');
return '<div class="mega-col"><p class="mega-head">' + esc(col.head) + '</p>' + links + '</div>';
}).join('');
@@ -129,7 +149,7 @@
var active = isGroupActive(g, page);
var cls = 'nav-link' + (active ? ' active' : '');
var aria = active ? ' aria-current="page"' : '';
var i18n = g.i18n ? ' data-i18n="' + g.i18n + '"' : '';
var i18n = i18nAttr(g.i18n || GROUP_I18N[g.label]);
if (g.href && !g.cols) {
// Voce diretta (Dashboard, Calendario)
@@ -183,7 +203,7 @@
'<button class="lang-btn ' + (lang === 'it' ? 'active' : '') + '" onclick="switchLang(\'it\')" title="Italiano" aria-label="Italiano">IT</button>' +
'<button class="lang-btn ' + (lang === 'en' ? 'active' : '') + '" onclick="switchLang(\'en\')" title="English" aria-label="English">EN</button>' +
'</span>' +
'<a class="btn-incident" href="incidents.html"><i data-lucide="siren" class="ic"></i><span>Registra incidente</span></a>' +
'<a class="btn-incident" href="incidents.html"><i data-lucide="siren" class="ic"></i><span data-i18n="nav.register_incident">Registra incidente</span></a>' +
// User menu (avatar+nome+ruolo via _loadUserInfo; click = menu)
'<span class="user" id="topnav-user" tabindex="0" role="button" aria-haspopup="true" aria-expanded="false">' +
'<span class="avatar" id="sidebar-user-avatar">--</span>' +
+3 -3
View File
@@ -178,9 +178,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script src="/js/kb.js"></script>
<script>
// Gate auth + chrome come le altre pagine V3.
+3 -3
View File
@@ -150,9 +150,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -178,9 +178,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -140,9 +140,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth + chrome + i18n + help ──────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -154,9 +154,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script src="/js/organigramma.js?v=20260627"></script>
<script>
// Render icone Lucide (chrome + crumb + bottoni). organigramma.js fa render albero
+3 -3
View File
@@ -343,9 +343,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -144,9 +144,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script src="/js/raci.js?v=20260627"></script>
<script>
function v3Icons() { if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} } }
+3 -3
View File
@@ -235,9 +235,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -160,9 +160,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -505,9 +505,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth & Init ──────────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -738,9 +738,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -218,9 +218,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -213,9 +213,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -545,9 +545,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -193,9 +193,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth & Init (ordine V3) ─────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -261,9 +261,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
if (!checkAuth()) throw new Error('Not authenticated');
loadSidebar();
+3 -3
View File
@@ -146,9 +146,9 @@
<script src="/js/api.js?v=20260625d"></script>
<script src="/js/common.js?v=20260627v3"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627"></script>
<script src="/js/i18n.js?v=20260631"></script>
<script src="/js/help.js?v=20260625d"></script>
<script src="/js/topnav-v3.js?v=20260627n"></script>
<script src="/js/i18n.js?v=20260627n"></script>
<script src="/js/help.js?v=20260627n"></script>
<script>
// ── Auth + chrome + i18n + help ──────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');