[FEAT] i18n IT/EN, Help Online contestuale, pagina Architettura
- i18n.js: sistema traduzioni IT/EN con ~150 chiavi, localStorage, data-i18n - help.js: help contestuale per 10 pagine con riferimenti NIS2 - architecture.html: descrizione architettura sistema completa - common.js: language toggle sidebar (IT/EN), link Architettura, icone - Integrato i18n + help in tutte le 14 pagine app + 3 admin Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4e3408e9f6
commit
52fd45fac9
@ -295,10 +295,12 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script>
|
||||
// ── Auth check ───────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
|
||||
// ── Admin check ──────────────────────────────────────────
|
||||
(async function checkAdmin() {
|
||||
|
||||
@ -130,10 +130,12 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script>
|
||||
// ── Auth check ───────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
|
||||
let currentPage = 1;
|
||||
|
||||
|
||||
@ -150,10 +150,12 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script>
|
||||
// ── Auth check ───────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
|
||||
let currentPage = 1;
|
||||
|
||||
|
||||
1088
public/architecture.html
Normal file
1088
public/architecture.html
Normal file
File diff suppressed because it is too large
Load Diff
@ -125,10 +125,14 @@
|
||||
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/common.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth check ───────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
// ── State ────────────────────────────────────────────────
|
||||
let currentAssessmentId = null;
|
||||
|
||||
@ -350,10 +350,14 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script src="/nis2/js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth & Init ─────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
// ── Labels ──────────────────────────────────────────────
|
||||
const typeLabels = {
|
||||
|
||||
@ -127,12 +127,16 @@
|
||||
|
||||
<script src="js/api.js"></script>
|
||||
<script src="js/common.js"></script>
|
||||
<script src="js/i18n.js"></script>
|
||||
<script src="js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth check ───────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
|
||||
// ── Init ─────────────────────────────────────────────────
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
// Data corrente nell'header
|
||||
document.getElementById('header-date').textContent = new Date().toLocaleDateString('it-IT', {
|
||||
|
||||
@ -336,10 +336,14 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script src="/nis2/js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth & Init ──────────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
// ── Labels ───────────────────────────────────────────────────
|
||||
const CLASSIFICATION_LABELS = {
|
||||
|
||||
@ -210,6 +210,7 @@ function loadSidebar() {
|
||||
label: 'Sistema',
|
||||
items: [
|
||||
{ name: 'Impostazioni', href: 'settings.html', icon: iconCog() },
|
||||
{ name: 'Architettura', href: 'architecture.html', icon: iconCubeTransparent() },
|
||||
]
|
||||
}
|
||||
];
|
||||
@ -240,9 +241,13 @@ function loadSidebar() {
|
||||
}
|
||||
navHTML += '</nav>';
|
||||
|
||||
// Footer with user info
|
||||
// Footer with language toggle + user info
|
||||
navHTML += `
|
||||
<div class="sidebar-footer">
|
||||
<div class="sidebar-lang-toggle" id="sidebar-lang-toggle">
|
||||
<button class="lang-btn ${_getSavedLang() === 'it' ? 'active' : ''}" onclick="switchLang('it')" title="Italiano">IT</button>
|
||||
<button class="lang-btn ${_getSavedLang() === 'en' ? 'active' : ''}" onclick="switchLang('en')" title="English">EN</button>
|
||||
</div>
|
||||
<div class="sidebar-user">
|
||||
<div class="sidebar-user-avatar" id="sidebar-user-avatar">--</div>
|
||||
<div class="sidebar-user-info">
|
||||
@ -472,6 +477,14 @@ function iconChartBar() {
|
||||
return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M2 11a1 1 0 011-1h2a1 1 0 011 1v5a1 1 0 01-1 1H3a1 1 0 01-1-1v-5zm6-4a1 1 0 011-1h2a1 1 0 011 1v9a1 1 0 01-1 1H9a1 1 0 01-1-1V7zm6-3a1 1 0 011-1h2a1 1 0 011 1v12a1 1 0 01-1 1h-2a1 1 0 01-1-1V4z"/></svg>';
|
||||
}
|
||||
|
||||
function iconCubeTransparent() {
|
||||
return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M10 3.5a1 1 0 00-.5.134l-5 2.857A1 1 0 004 7.382v5.236a1 1 0 00.5.866l5 2.857a1 1 0 001 0l5-2.857a1 1 0 00.5-.866V7.382a1 1 0 00-.5-.891l-5-2.857A1 1 0 0010 3.5zM6 8.382l4 2.286 4-2.286v3.236l-4 2.286-4-2.286V8.382z"/></svg>';
|
||||
}
|
||||
|
||||
function iconQuestionMarkCircle() {
|
||||
return '<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-8-3a1 1 0 00-.867.5 1 1 0 11-1.731-1A3 3 0 0113 8a3.001 3.001 0 01-2 2.83V11a1 1 0 11-2 0v-1a1 1 0 011-1 1 1 0 100-2zm0 8a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"/></svg>';
|
||||
}
|
||||
|
||||
function iconCog() {
|
||||
return '<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M11.49 3.17c-.38-1.56-2.6-1.56-2.98 0a1.532 1.532 0 01-2.286.948c-1.372-.836-2.942.734-2.106 2.106.54.886.061 2.042-.947 2.287-1.561.379-1.561 2.6 0 2.978a1.532 1.532 0 01.947 2.287c-.836 1.372.734 2.942 2.106 2.106a1.532 1.532 0 012.287.947c.379 1.561 2.6 1.561 2.978 0a1.533 1.533 0 012.287-.947c1.372.836 2.942-.734 2.106-2.106a1.533 1.533 0 01.947-2.287c1.561-.379 1.561-2.6 0-2.978a1.532 1.532 0 01-.947-2.287c.836-1.372-.734-2.942-2.106-2.106a1.532 1.532 0 01-2.287-.947zM10 13a3 3 0 100-6 3 3 0 000 6z" clip-rule="evenodd"/></svg>';
|
||||
}
|
||||
@ -504,3 +517,60 @@ function iconPlus() {
|
||||
function iconSparkles() {
|
||||
return '<svg viewBox="0 0 20 20" fill="currentColor"><path d="M5 2a1 1 0 011 1v1h1a1 1 0 010 2H6v1a1 1 0 01-2 0V6H3a1 1 0 010-2h1V3a1 1 0 011-1zm0 10a1 1 0 011 1v1h1a1 1 0 110 2H6v1a1 1 0 11-2 0v-1H3a1 1 0 110-2h1v-1a1 1 0 011-1zm7-10a1 1 0 01.967.744L14.146 7.2 17.5 8.512a1 1 0 010 1.836l-3.354 1.311-1.18 4.456a1 1 0 01-1.932 0L9.854 11.66 6.5 10.348a1 1 0 010-1.836l3.354-1.311 1.18-4.456A1 1 0 0112 2z"/></svg>';
|
||||
}
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
// Language Switcher
|
||||
// ═══════════════════════════════════════════════════════════════════
|
||||
|
||||
function _getSavedLang() {
|
||||
return localStorage.getItem('nis2_lang') || 'it';
|
||||
}
|
||||
|
||||
function switchLang(lang) {
|
||||
if (typeof I18n !== 'undefined') {
|
||||
I18n.setLang(lang);
|
||||
}
|
||||
localStorage.setItem('nis2_lang', lang);
|
||||
// Aggiorna bottoni
|
||||
document.querySelectorAll('.lang-btn').forEach(btn => {
|
||||
btn.classList.toggle('active', btn.textContent.trim().toLowerCase() === lang);
|
||||
});
|
||||
}
|
||||
|
||||
// Inject language toggle CSS
|
||||
(function _injectLangCSS() {
|
||||
if (document.getElementById('lang-toggle-css')) return;
|
||||
const style = document.createElement('style');
|
||||
style.id = 'lang-toggle-css';
|
||||
style.textContent = `
|
||||
.sidebar-lang-toggle {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 8px 16px 4px;
|
||||
}
|
||||
.lang-btn {
|
||||
padding: 4px 14px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
border: 1px solid var(--gray-300);
|
||||
background: var(--gray-50);
|
||||
color: var(--gray-500);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all var(--transition-fast);
|
||||
font-family: inherit;
|
||||
}
|
||||
.lang-btn:hover {
|
||||
background: var(--gray-100);
|
||||
color: var(--gray-700);
|
||||
}
|
||||
.lang-btn.active {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
})();
|
||||
|
||||
881
public/js/help.js
Normal file
881
public/js/help.js
Normal file
@ -0,0 +1,881 @@
|
||||
/**
|
||||
* NIS2 Agile - Contextual Help System
|
||||
*
|
||||
* Sistema di guida contestuale per pagina.
|
||||
* - Rileva automaticamente la pagina corrente dall'URL
|
||||
* - Mostra help contestuale tramite showModal()
|
||||
* - Inietta un pulsante "?" nell'header della pagina
|
||||
* - Fornisce tooltip inline riutilizzabili
|
||||
*/
|
||||
|
||||
const HelpSystem = (function () {
|
||||
'use strict';
|
||||
|
||||
// ── Icona SVG del punto interrogativo ────────────────────────────
|
||||
var HELP_ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>';
|
||||
|
||||
var HELP_ICON_SMALL = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>';
|
||||
|
||||
// ── Contenuti di help per ogni pagina (in italiano) ──────────────
|
||||
|
||||
var _helpContent = {
|
||||
|
||||
// ─── Dashboard ───────────────────────────────────────────────
|
||||
'dashboard': {
|
||||
title: 'Guida - Dashboard',
|
||||
intro: 'La Dashboard fornisce una panoramica in tempo reale dello stato di conformita\' NIS2 della tua organizzazione. Da qui puoi monitorare i principali indicatori, le attivita\' recenti e le scadenze imminenti.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Punteggio di Compliance',
|
||||
items: [
|
||||
'Il <strong>gauge circolare</strong> mostra il punteggio complessivo di conformita\' NIS2 (0-100%), calcolato sulla base dell\'ultimo assessment completato.',
|
||||
'I colori indicano il livello: <span style="color:var(--danger)">rosso</span> (critico, <20%), <span style="color:#f97316">arancione</span> (basso, 20-39%), <span style="color:var(--warning)">giallo</span> (medio, 40-59%), <span style="color:#84cc16">verde chiaro</span> (buono, 60-79%), <span style="color:var(--secondary)">verde</span> (eccellente, ≥80%).',
|
||||
'Il punteggio si aggiorna automaticamente ad ogni nuovo assessment.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Statistiche Principali',
|
||||
items: [
|
||||
'<strong>Rischi Aperti</strong> - numero di rischi attivi nel registro dei rischi.',
|
||||
'<strong>Incidenti Attivi</strong> - incidenti di sicurezza attualmente in gestione.',
|
||||
'<strong>Scadenze Superate</strong> - attivita\' o task il cui termine e\' gia\' passato.',
|
||||
'Clicca su ciascuna card per accedere direttamente alla sezione corrispondente.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Attivita\' Recente e Scadenze',
|
||||
items: [
|
||||
'Il <strong>feed Attivita\' Recente</strong> mostra le ultime operazioni effettuate sulla piattaforma (assessment, rischi, incidenti, policy).',
|
||||
'Le <strong>Prossime Scadenze</strong> elencano i task e le attivita\' in scadenza, evidenziando in rosso quelle urgenti.',
|
||||
'La <strong>Mappa Rischi</strong> visualizza la distribuzione dei rischi per livello di gravita\'.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Azioni Rapide',
|
||||
items: [
|
||||
'Utilizza le <strong>Azioni Rapide</strong> per avviare operazioni comuni senza navigare nel menu.',
|
||||
'La funzione <strong>Genera Policy con AI</strong> permette di creare bozze di policy conformi NIS2 tramite intelligenza artificiale.',
|
||||
'Puoi avviare un nuovo assessment, registrare un incidente o aggiungere un rischio con un solo clic.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Direttiva (UE) 2022/2555 (NIS2) - Considerando generale sulla gestione del rischio',
|
||||
'Art. 21 - Misure di gestione dei rischi di cibersicurezza',
|
||||
'Art. 23 - Obblighi di segnalazione'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Assessment / Gap Analysis ───────────────────────────────
|
||||
'assessment': {
|
||||
title: 'Guida - Gap Analysis NIS2',
|
||||
intro: 'La Gap Analysis NIS2 ti permette di valutare il livello di conformita\' della tua organizzazione rispetto ai requisiti dell\'Art. 21 della Direttiva NIS2. Il wizard guida l\'utente attraverso 10 categorie di misure di sicurezza.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Wizard di Assessment',
|
||||
items: [
|
||||
'L\'assessment e\' strutturato in <strong>10 categorie</strong> derivate dall\'Art. 21.2 della Direttiva NIS2.',
|
||||
'Ogni categoria contiene domande specifiche sulle misure di sicurezza implementate.',
|
||||
'Puoi compilare l\'assessment in piu\' sessioni: il progresso viene salvato automaticamente.',
|
||||
'Utilizza la barra di avanzamento in alto per monitorare il completamento.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Sistema di Valutazione',
|
||||
items: [
|
||||
'<strong>Non Implementato</strong> (0%) - la misura non e\' presente.',
|
||||
'<strong>Parzialmente Implementato</strong> (50%) - la misura e\' presente ma incompleta o non formalizzata.',
|
||||
'<strong>Implementato</strong> (100%) - la misura e\' completamente implementata e documentata.',
|
||||
'<strong>Non Applicabile</strong> - la misura non si applica al contesto dell\'organizzazione.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Analisi AI',
|
||||
items: [
|
||||
'Al completamento dell\'assessment, puoi richiedere un\'<strong>analisi basata su AI</strong> dei risultati.',
|
||||
'L\'AI identifica le aree critiche, suggerisce priorita\' di intervento e fornisce raccomandazioni specifiche.',
|
||||
'L\'analisi tiene conto del settore e della classificazione NIS2 dell\'organizzazione.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Generazione Non Conformita\' (NCR)',
|
||||
items: [
|
||||
'Dalla pagina dei risultati, puoi <strong>generare automaticamente NCR</strong> (Non Conformity Reports) dai gap individuati.',
|
||||
'Ogni NCR viene creata con priorita\', descrizione e suggerimenti di rimedio.',
|
||||
'Le NCR generate possono essere esportate o collegate al registro dei rischi.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 21.1 - Obbligo di adottare misure tecniche, operative e organizzative adeguate e proporzionate',
|
||||
'Art. 21.2 (a-j) - Le 10 categorie di misure di sicurezza minime',
|
||||
'Art. 21.3 - Valutazione della proporzionalita\' delle misure',
|
||||
'Art. 21.4 - Conformita\' con norme europee e internazionali'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Risk Management ─────────────────────────────────────────
|
||||
'risks': {
|
||||
title: 'Guida - Gestione Rischi',
|
||||
intro: 'Il modulo di Gestione Rischi consente di identificare, valutare e trattare i rischi di sicurezza informatica in conformita\' con l\'Art. 21 della Direttiva NIS2. Include una matrice di rischio interattiva e strumenti per il monitoraggio continuo.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Matrice di Rischio',
|
||||
items: [
|
||||
'La <strong>matrice Probabilita\' x Impatto</strong> (5x5) classifica automaticamente i rischi in base alla loro gravita\'.',
|
||||
'I livelli di probabilita\' vanno da <em>Molto Bassa</em> a <em>Molto Alta</em>.',
|
||||
'I livelli di impatto vanno da <em>Trascurabile</em> a <em>Critico</em>.',
|
||||
'Il punteggio di rischio risultante determina il colore e la priorita\' del rischio.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Livelli di Gravita\'',
|
||||
items: [
|
||||
'<span style="color:var(--secondary)"><strong>Basso</strong></span> - rischio accettabile, monitoraggio periodico.',
|
||||
'<span style="color:var(--warning)"><strong>Medio</strong></span> - richiede attenzione, pianificare azioni di mitigazione.',
|
||||
'<span style="color:#f97316"><strong>Alto</strong></span> - richiede intervento prioritario.',
|
||||
'<span style="color:var(--danger)"><strong>Critico</strong></span> - richiede intervento immediato, escalation al management.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Opzioni di Trattamento',
|
||||
items: [
|
||||
'<strong>Mitigare</strong> - implementare controlli per ridurre probabilita\' o impatto.',
|
||||
'<strong>Accettare</strong> - accettare consapevolmente il rischio (documentando la decisione).',
|
||||
'<strong>Trasferire</strong> - trasferire il rischio a terzi (es. assicurazione, outsourcing).',
|
||||
'<strong>Evitare</strong> - eliminare l\'attivita\' o la condizione che genera il rischio.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Registro e Monitoraggio',
|
||||
items: [
|
||||
'Ogni rischio registrato include: descrizione, categoria, asset coinvolti, owner e piano di trattamento.',
|
||||
'Lo stato di ogni rischio e\' tracciato nel tempo con lo storico delle modifiche.',
|
||||
'I rischi possono essere collegati ad asset, incidenti e controlli di compliance.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 21.1 - Approccio basato sulla valutazione dei rischi (risk-based approach)',
|
||||
'Art. 21.2 (a) - Politiche di analisi dei rischi e di sicurezza dei sistemi informatici',
|
||||
'Art. 21.2 (f) - Politiche e procedure per valutare l\'efficacia delle misure',
|
||||
'Considerando 79 - Proporzionalita\' delle misure rispetto ai rischi'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Incident Management ─────────────────────────────────────
|
||||
'incidents': {
|
||||
title: 'Guida - Gestione Incidenti',
|
||||
intro: 'Il modulo di Gestione Incidenti supporta l\'intero ciclo di vita degli incidenti di sicurezza, dalla rilevazione alla chiusura, nel rispetto dei tempi di notifica previsti dall\'Art. 23 della Direttiva NIS2.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Timeline di Notifica NIS2',
|
||||
items: [
|
||||
'<strong>Early Warning (24 ore)</strong> - preallarme al CSIRT/autorita\' competente entro 24 ore dalla conoscenza dell\'incidente significativo.',
|
||||
'<strong>Notifica (72 ore)</strong> - notifica completa entro 72 ore con valutazione iniziale, gravita\', impatto e indicatori di compromissione.',
|
||||
'<strong>Report Finale (30 giorni)</strong> - relazione finale dettagliata entro un mese dalla notifica, con descrizione, causa, misure adottate e impatto transfrontaliero.',
|
||||
'La piattaforma traccia automaticamente le scadenze e invia promemoria per ciascuna fase.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Livelli di Gravita\'',
|
||||
items: [
|
||||
'<strong>Bassa</strong> - impatto limitato, nessuna interruzione di servizio significativa.',
|
||||
'<strong>Media</strong> - impatto moderato su operazioni o dati, richiede intervento pianificato.',
|
||||
'<strong>Alta</strong> - impatto significativo su servizi essenziali o dati sensibili.',
|
||||
'<strong>Critica</strong> - interruzione grave di servizi essenziali, compromissione massiva di dati, impatto transfrontaliero.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Ciclo di Vita dell\'Incidente',
|
||||
items: [
|
||||
'<strong>Rilevazione</strong> - identificazione e registrazione iniziale dell\'incidente.',
|
||||
'<strong>Analisi</strong> - valutazione di gravita\', impatto e perimetro.',
|
||||
'<strong>Contenimento</strong> - azioni immediate per limitare il danno.',
|
||||
'<strong>Risoluzione</strong> - eliminazione della causa e ripristino dei servizi.',
|
||||
'<strong>Chiusura</strong> - documentazione delle lezioni apprese e miglioramenti.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Funzionalita\' Avanzate',
|
||||
items: [
|
||||
'Collegamento automatico con il registro dei rischi e gli asset coinvolti.',
|
||||
'Timeline visuale con tutti gli aggiornamenti e le comunicazioni.',
|
||||
'Esportazione del report per la notifica alle autorita\' competenti.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 23.1 - Obbligo di notifica degli incidenti significativi',
|
||||
'Art. 23.4 (a) - Early warning entro 24 ore',
|
||||
'Art. 23.4 (b) - Notifica entro 72 ore',
|
||||
'Art. 23.4 (d) - Relazione finale entro un mese',
|
||||
'Art. 23.3 - Definizione di incidente significativo'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Policy Management ───────────────────────────────────────
|
||||
'policies': {
|
||||
title: 'Guida - Gestione Policy',
|
||||
intro: 'Il modulo di Gestione Policy consente di creare, gestire e mantenere aggiornate le policy di sicurezza informatica richieste dalla Direttiva NIS2. Include un generatore di policy basato su intelligenza artificiale.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Ciclo di Vita delle Policy',
|
||||
items: [
|
||||
'<strong>Bozza</strong> - la policy e\' in fase di stesura, modificabile liberamente.',
|
||||
'<strong>In Revisione</strong> - la policy e\' stata sottoposta a revisione da parte dei responsabili.',
|
||||
'<strong>Approvata</strong> - la policy e\' stata approvata dal management.',
|
||||
'<strong>Pubblicata</strong> - la policy e\' attiva e comunicata a tutto il personale.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Generazione con AI',
|
||||
items: [
|
||||
'La funzione <strong>Genera con AI</strong> crea automaticamente bozze di policy conformi NIS2.',
|
||||
'L\'AI utilizza il contesto della tua organizzazione (settore, dimensione, classificazione) per personalizzare il contenuto.',
|
||||
'Le policy generate includono: obiettivo, ambito di applicazione, responsabilita\', procedure e riferimenti normativi.',
|
||||
'Le bozze generate devono essere revisionate e personalizzate prima dell\'approvazione.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Framework di Compliance NIS2',
|
||||
items: [
|
||||
'Ogni policy e\' collegata a una o piu\' categorie dell\'Art. 21.2 della Direttiva NIS2.',
|
||||
'Il sistema tiene traccia della copertura delle policy rispetto ai requisiti normativi.',
|
||||
'Le policy scadute o non aggiornate vengono evidenziate automaticamente.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Gestione Versioni',
|
||||
items: [
|
||||
'Ogni modifica crea una nuova versione della policy con data e autore.',
|
||||
'E\' possibile confrontare versioni e ripristinare versioni precedenti.',
|
||||
'Lo storico completo e\' disponibile per finalita\' di audit.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 21.2 (a) - Politiche di analisi dei rischi e di sicurezza dei sistemi informatici',
|
||||
'Art. 21.2 (e) - Sicurezza nell\'acquisizione, sviluppo e manutenzione dei sistemi',
|
||||
'Art. 21.2 (j) - Uso di crittografia e cifratura',
|
||||
'Art. 20 - Governance - Responsabilita\' degli organi di gestione'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Supply Chain Security ───────────────────────────────────
|
||||
'supply-chain': {
|
||||
title: 'Guida - Sicurezza Supply Chain',
|
||||
intro: 'Il modulo di Sicurezza della Supply Chain consente di gestire e valutare i rischi legati a fornitori e terze parti, in conformita\' con l\'Art. 21.2 (d) della Direttiva NIS2.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Registro Fornitori',
|
||||
items: [
|
||||
'Il <strong>registro fornitori</strong> centralizza tutte le informazioni sui fornitori critici.',
|
||||
'Per ogni fornitore sono registrati: servizi forniti, dati di contatto, contratti attivi e livello di criticita\'.',
|
||||
'I fornitori possono essere classificati per tipologia (ICT, servizi, consulenza, etc.).'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Valutazione del Rischio Fornitore',
|
||||
items: [
|
||||
'Ogni fornitore puo\' essere sottoposto a una <strong>valutazione del rischio</strong> strutturata.',
|
||||
'La valutazione copre: sicurezza informatica, continuita\' operativa, conformita\' normativa e dipendenza.',
|
||||
'Il <strong>punteggio di rischio</strong> viene calcolato automaticamente sulla base delle risposte.',
|
||||
'I fornitori ad alto rischio vengono evidenziati per un monitoraggio rafforzato.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Monitoraggio Continuo',
|
||||
items: [
|
||||
'La piattaforma traccia scadenze contrattuali e revisioni periodiche.',
|
||||
'Le variazioni di rischio vengono segnalate automaticamente.',
|
||||
'E\' possibile associare requisiti di sicurezza specifici a ciascun contratto.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Panoramica Rischi',
|
||||
items: [
|
||||
'La <strong>Panoramica Rischi</strong> mostra la distribuzione dei fornitori per livello di rischio.',
|
||||
'Statistiche aggregate su: numero di fornitori, valutazioni scadute, fornitori ad alto rischio.',
|
||||
'Esportazione dei dati per reporting e audit.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 21.2 (d) - Sicurezza della catena di approvvigionamento',
|
||||
'Art. 21.3 - Valutazione dei rischi specifici per ciascun fornitore diretto',
|
||||
'Considerando 85 - Qualita\' e resilienza dei prodotti e servizi ICT',
|
||||
'Considerando 86 - Gestione dei rischi della supply chain'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Training & Awareness ────────────────────────────────────
|
||||
'training': {
|
||||
title: 'Guida - Formazione e Awareness',
|
||||
intro: 'Il modulo di Formazione gestisce i programmi di sensibilizzazione e formazione sulla sicurezza informatica, come richiesto dall\'Art. 20.2 della Direttiva NIS2 che impone la formazione obbligatoria per gli organi di gestione.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Formazione Obbligatoria NIS2',
|
||||
items: [
|
||||
'L\'Art. 20.2 della Direttiva NIS2 richiede che i <strong>membri degli organi di gestione</strong> seguano una formazione specifica.',
|
||||
'La formazione deve consentire di individuare i rischi e valutare le pratiche di gestione del rischio.',
|
||||
'La piattaforma traccia la conformita\' a questo obbligo per ciascun membro del management.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Gestione Corsi',
|
||||
items: [
|
||||
'Crea e gestisci <strong>corsi di formazione</strong> su tematiche NIS2 e sicurezza informatica.',
|
||||
'Ogni corso include: titolo, descrizione, durata stimata, contenuti e materiali.',
|
||||
'I corsi possono essere categorizzati per argomento e livello di difficolta\'.',
|
||||
'Supporto per corsi interni ed esterni con tracciamento unificato.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Assegnazione e Tracciamento',
|
||||
items: [
|
||||
'Assegna corsi a singoli utenti o gruppi con <strong>scadenze personalizzate</strong>.',
|
||||
'La dashboard mostra lo stato di completamento per ciascun assegnatario.',
|
||||
'I corsi scaduti vengono evidenziati e possono generare notifiche automatiche.',
|
||||
'Lo storico di completamento e\' disponibile per audit e compliance.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Report di Compliance',
|
||||
items: [
|
||||
'Visualizza il tasso di completamento complessivo della formazione.',
|
||||
'Identifica gli utenti con formazione scaduta o mancante.',
|
||||
'Genera report per dimostrare la conformita\' all\'Art. 20.2 in sede di audit.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 20.2 - Formazione obbligatoria per gli organi di gestione',
|
||||
'Art. 20.1 - Responsabilita\' degli organi di gestione nell\'approvazione delle misure',
|
||||
'Art. 21.2 (g) - Pratiche di igiene informatica di base e formazione in materia di cibersicurezza',
|
||||
'Considerando 89 - Formazione regolare per dipendenti e management'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Asset Inventory ─────────────────────────────────────────
|
||||
'assets': {
|
||||
title: 'Guida - Inventario Asset',
|
||||
intro: 'Il modulo Inventario Asset consente di catalogare e gestire tutti gli asset ICT dell\'organizzazione, fondamentale per la gestione dei rischi e la sicurezza delle reti e dei sistemi informativi ai sensi dell\'Art. 21 della Direttiva NIS2.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Categorizzazione degli Asset',
|
||||
items: [
|
||||
'<strong>Hardware</strong> - server, workstation, dispositivi di rete, dispositivi mobili, apparati di sicurezza.',
|
||||
'<strong>Software</strong> - applicativi, sistemi operativi, database, middleware, servizi cloud.',
|
||||
'<strong>Rete</strong> - reti LAN/WAN, collegamenti internet, VPN, segmenti di rete.',
|
||||
'<strong>Dati</strong> - archivi, database, backup, dati personali, dati critici per il business.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Livelli di Criticita\'',
|
||||
items: [
|
||||
'Ogni asset ha un <strong>livello di criticita\'</strong> che ne determina la priorita\' nella gestione del rischio.',
|
||||
'La criticita\' viene valutata in base a: impatto sulla continuita\' operativa, dati trattati, esposizione esterna.',
|
||||
'Gli asset critici sono soggetti a controlli di sicurezza rafforzati.',
|
||||
'Il livello di criticita\' influenza la valutazione dei rischi associati.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Mappa delle Dipendenze',
|
||||
items: [
|
||||
'La <strong>Mappa Dipendenze</strong> visualizza le relazioni tra gli asset.',
|
||||
'Identifica le dipendenze critiche e i singoli punti di guasto (SPOF).',
|
||||
'Utile per la valutazione dell\'impatto in caso di incidente o indisponibilita\'.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Gestione del Ciclo di Vita',
|
||||
items: [
|
||||
'Tracciamento dello stato dell\'asset: attivo, in manutenzione, dismesso.',
|
||||
'Registrazione di proprietario, ubicazione, fornitore e contratti di supporto.',
|
||||
'Alert automatici per asset con supporto in scadenza o fine vita.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 21.2 (i) - Sicurezza delle risorse umane, politiche di controllo dell\'accesso e gestione degli attivi',
|
||||
'Art. 21.2 (a) - Politiche di analisi dei rischi e di sicurezza dei sistemi informatici',
|
||||
'Art. 21.2 (c) - Continuita\' operativa, gestione dei backup e ripristino in caso di disastro',
|
||||
'Considerando 79 - Adeguatezza delle misure rispetto ai rischi per le reti e i sistemi informativi'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Reports / Audit ─────────────────────────────────────────
|
||||
'reports': {
|
||||
title: 'Guida - Audit & Report',
|
||||
intro: 'Il modulo Audit & Report fornisce strumenti per monitorare lo stato dei controlli di compliance, gestire le evidenze e generare report per le autorita\' competenti e per gli audit interni.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Controlli di Compliance',
|
||||
items: [
|
||||
'Ogni controllo rappresenta un requisito specifico della Direttiva NIS2.',
|
||||
'Lo stato di ciascun controllo e\' tracciato: <strong>Conforme</strong>, <strong>Parzialmente Conforme</strong>, <strong>Non Conforme</strong>, <strong>Non Valutato</strong>.',
|
||||
'I controlli sono organizzati per categoria secondo l\'Art. 21.2.',
|
||||
'La percentuale di conformita\' complessiva e\' calcolata automaticamente.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Gestione Evidenze',
|
||||
items: [
|
||||
'Per ogni controllo e\' possibile allegare <strong>evidenze</strong> (documenti, screenshot, log).',
|
||||
'Le evidenze supportano la dimostrazione di conformita\' in sede di audit.',
|
||||
'Il sistema traccia la data di caricamento e la validita\' di ogni evidenza.',
|
||||
'Le evidenze scadute o mancanti vengono segnalate automaticamente.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Log di Audit',
|
||||
items: [
|
||||
'Il <strong>log di audit</strong> registra automaticamente tutte le operazioni effettuate sulla piattaforma.',
|
||||
'Ogni voce include: utente, azione, data/ora, oggetto e dettagli.',
|
||||
'I log sono immutabili e disponibili per verifiche ispettive.',
|
||||
'Supporto per filtri avanzati e ricerca nei log.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Esportazione Report',
|
||||
items: [
|
||||
'Genera report completi in formato esportabile per audit e autorita\'.',
|
||||
'Report disponibili: stato di compliance, registro rischi, incidenti, formazione.',
|
||||
'I report possono essere personalizzati per periodo e ambito.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 21 - Misure di gestione dei rischi di cibersicurezza',
|
||||
'Art. 32 - Misure di vigilanza e di esecuzione per i soggetti essenziali',
|
||||
'Art. 33 - Misure di vigilanza e di esecuzione per i soggetti importanti',
|
||||
'Art. 34 - Condizioni generali per l\'imposizione di sanzioni amministrative pecuniarie'
|
||||
]
|
||||
},
|
||||
|
||||
// ─── Settings ────────────────────────────────────────────────
|
||||
'settings': {
|
||||
title: 'Guida - Impostazioni',
|
||||
intro: 'La sezione Impostazioni consente di configurare il profilo dell\'organizzazione, la classificazione NIS2, gestire il team e le impostazioni di sicurezza dell\'account.',
|
||||
sections: [
|
||||
{
|
||||
heading: 'Profilo Organizzazione',
|
||||
items: [
|
||||
'Configura i dati della tua organizzazione: nome, settore, dimensione, codice fiscale/P.IVA.',
|
||||
'Questi dati vengono utilizzati per personalizzare i report e le analisi AI.',
|
||||
'Il settore e la dimensione influenzano la classificazione NIS2.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Classificazione NIS2',
|
||||
items: [
|
||||
'<strong>Soggetto Essenziale</strong> - organizzazione che rientra nell\'Allegato I della Direttiva e supera le soglie dimensionali (grandi imprese). Soggetta a vigilanza proattiva.',
|
||||
'<strong>Soggetto Importante</strong> - organizzazione che rientra nell\'Allegato I o II ma non raggiunge le soglie per "essenziale" (medie imprese). Soggetta a vigilanza reattiva.',
|
||||
'<strong>Non Applicabile</strong> - organizzazione che non rientra nell\'ambito di applicazione della Direttiva.',
|
||||
'<strong>Adesione Volontaria</strong> - organizzazione non obbligata che sceglie volontariamente di adottare il framework NIS2.'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Gestione Team',
|
||||
items: [
|
||||
'Aggiungi e gestisci i <strong>membri del team</strong> con ruoli e permessi differenziati.',
|
||||
'Ruoli disponibili: Amministratore (accesso completo) e Utente (accesso limitato).',
|
||||
'Ogni membro puo\' essere associato a specifiche responsabilita\' (risk owner, incident manager, etc.).'
|
||||
]
|
||||
},
|
||||
{
|
||||
heading: 'Sicurezza',
|
||||
items: [
|
||||
'Modifica la password del tuo account.',
|
||||
'Visualizza le sessioni attive e disconnetti dispositivi remoti.',
|
||||
'Configura le impostazioni di notifica per scadenze e alert.'
|
||||
]
|
||||
}
|
||||
],
|
||||
references: [
|
||||
'Art. 2 - Ambito di applicazione',
|
||||
'Art. 3 - Soggetti essenziali e soggetti importanti',
|
||||
'Allegato I - Settori ad alta criticita\'',
|
||||
'Allegato II - Altri settori critici',
|
||||
'Art. 20 - Governance'
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
// ── Mappa tra filename nell'URL e chiave di help ─────────────────
|
||||
|
||||
var _pageMap = {
|
||||
'dashboard.html': 'dashboard',
|
||||
'dashboard': 'dashboard',
|
||||
'assessment.html': 'assessment',
|
||||
'assessment': 'assessment',
|
||||
'risks.html': 'risks',
|
||||
'risks': 'risks',
|
||||
'incidents.html': 'incidents',
|
||||
'incidents': 'incidents',
|
||||
'policies.html': 'policies',
|
||||
'policies': 'policies',
|
||||
'supply-chain.html': 'supply-chain',
|
||||
'supply-chain': 'supply-chain',
|
||||
'training.html': 'training',
|
||||
'training': 'training',
|
||||
'assets.html': 'assets',
|
||||
'assets': 'assets',
|
||||
'reports.html': 'reports',
|
||||
'audit.html': 'reports',
|
||||
'reports': 'reports',
|
||||
'audit': 'reports',
|
||||
'settings.html': 'settings',
|
||||
'settings': 'settings'
|
||||
};
|
||||
|
||||
// ── Stili CSS (iniettati una sola volta) ─────────────────────────
|
||||
|
||||
var _stylesInjected = false;
|
||||
|
||||
function _injectStyles() {
|
||||
if (_stylesInjected) return;
|
||||
_stylesInjected = true;
|
||||
|
||||
var style = document.createElement('style');
|
||||
style.textContent = [
|
||||
/* Pulsante help nell'header */
|
||||
'.help-btn {',
|
||||
' display: inline-flex;',
|
||||
' align-items: center;',
|
||||
' justify-content: center;',
|
||||
' width: 36px;',
|
||||
' height: 36px;',
|
||||
' padding: 0;',
|
||||
' background: var(--gray-100);',
|
||||
' border: 1px solid var(--gray-300);',
|
||||
' border-radius: 50%;',
|
||||
' cursor: pointer;',
|
||||
' color: var(--gray-500);',
|
||||
' transition: all var(--transition-fast);',
|
||||
'}',
|
||||
'.help-btn:hover {',
|
||||
' background: var(--primary-bg);',
|
||||
' border-color: var(--primary);',
|
||||
' color: var(--primary);',
|
||||
'}',
|
||||
'.help-btn:focus-visible {',
|
||||
' box-shadow: 0 0 0 3px rgba(26, 115, 232, 0.3);',
|
||||
' outline: none;',
|
||||
'}',
|
||||
'.help-btn svg {',
|
||||
' width: 18px;',
|
||||
' height: 18px;',
|
||||
'}',
|
||||
|
||||
/* Contenuto del modale help */
|
||||
'.help-content {',
|
||||
' font-size: 0.875rem;',
|
||||
' color: var(--gray-700);',
|
||||
' line-height: 1.7;',
|
||||
'}',
|
||||
'.help-content .help-intro {',
|
||||
' margin-bottom: 20px;',
|
||||
' padding: 14px 18px;',
|
||||
' background: var(--primary-bg);',
|
||||
' border-radius: var(--border-radius);',
|
||||
' border-left: 4px solid var(--primary);',
|
||||
' color: var(--gray-700);',
|
||||
'}',
|
||||
'.help-content h4 {',
|
||||
' font-size: 0.9375rem;',
|
||||
' font-weight: 700;',
|
||||
' color: var(--gray-900);',
|
||||
' margin: 20px 0 10px 0;',
|
||||
' padding-bottom: 6px;',
|
||||
' border-bottom: 1px solid var(--gray-100);',
|
||||
'}',
|
||||
'.help-content h4:first-of-type {',
|
||||
' margin-top: 0;',
|
||||
'}',
|
||||
'.help-content ul {',
|
||||
' list-style: none;',
|
||||
' padding: 0;',
|
||||
' margin: 0 0 8px 0;',
|
||||
'}',
|
||||
'.help-content ul li {',
|
||||
' position: relative;',
|
||||
' padding: 5px 0 5px 20px;',
|
||||
' line-height: 1.6;',
|
||||
'}',
|
||||
'.help-content ul li::before {',
|
||||
' content: "";',
|
||||
' position: absolute;',
|
||||
' left: 0;',
|
||||
' top: 13px;',
|
||||
' width: 6px;',
|
||||
' height: 6px;',
|
||||
' background: var(--primary);',
|
||||
' border-radius: 50%;',
|
||||
'}',
|
||||
'.help-content .help-references {',
|
||||
' margin-top: 24px;',
|
||||
' padding: 14px 18px;',
|
||||
' background: var(--gray-50);',
|
||||
' border-radius: var(--border-radius);',
|
||||
' border: 1px solid var(--gray-200);',
|
||||
'}',
|
||||
'.help-content .help-references h4 {',
|
||||
' font-size: 0.8125rem;',
|
||||
' color: var(--gray-600);',
|
||||
' text-transform: uppercase;',
|
||||
' letter-spacing: 0.04em;',
|
||||
' margin: 0 0 10px 0;',
|
||||
' padding-bottom: 6px;',
|
||||
' border-bottom: 1px solid var(--gray-200);',
|
||||
'}',
|
||||
'.help-content .help-references ul li {',
|
||||
' font-size: 0.8125rem;',
|
||||
' color: var(--gray-600);',
|
||||
' padding: 3px 0 3px 20px;',
|
||||
'}',
|
||||
'.help-content .help-references ul li::before {',
|
||||
' width: 5px;',
|
||||
' height: 5px;',
|
||||
' background: var(--gray-400);',
|
||||
' top: 11px;',
|
||||
'}',
|
||||
|
||||
/* Inline help tooltip */
|
||||
'.inline-help {',
|
||||
' display: inline-flex;',
|
||||
' align-items: center;',
|
||||
' justify-content: center;',
|
||||
' width: 16px;',
|
||||
' height: 16px;',
|
||||
' margin-left: 4px;',
|
||||
' vertical-align: middle;',
|
||||
' color: var(--gray-400);',
|
||||
' cursor: help;',
|
||||
' position: relative;',
|
||||
'}',
|
||||
'.inline-help:hover {',
|
||||
' color: var(--primary);',
|
||||
'}',
|
||||
'.inline-help .inline-help-text {',
|
||||
' position: absolute;',
|
||||
' bottom: calc(100% + 8px);',
|
||||
' left: 50%;',
|
||||
' transform: translateX(-50%) scale(0.95);',
|
||||
' background: var(--gray-800);',
|
||||
' color: #fff;',
|
||||
' padding: 8px 12px;',
|
||||
' border-radius: var(--border-radius-sm);',
|
||||
' font-size: 0.75rem;',
|
||||
' font-weight: 500;',
|
||||
' line-height: 1.5;',
|
||||
' white-space: normal;',
|
||||
' width: max-content;',
|
||||
' max-width: 260px;',
|
||||
' pointer-events: none;',
|
||||
' opacity: 0;',
|
||||
' transition: opacity var(--transition-fast), transform var(--transition-fast);',
|
||||
' z-index: 60;',
|
||||
' box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);',
|
||||
'}',
|
||||
'.inline-help .inline-help-text::after {',
|
||||
' content: "";',
|
||||
' position: absolute;',
|
||||
' top: 100%;',
|
||||
' left: 50%;',
|
||||
' transform: translateX(-50%);',
|
||||
' border: 5px solid transparent;',
|
||||
' border-top-color: var(--gray-800);',
|
||||
'}',
|
||||
'.inline-help:hover .inline-help-text {',
|
||||
' opacity: 1;',
|
||||
' transform: translateX(-50%) scale(1);',
|
||||
'}'
|
||||
].join('\n');
|
||||
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
// ── Rilevamento pagina corrente dall'URL ─────────────────────────
|
||||
|
||||
function _detectPage() {
|
||||
var pathname = window.location.pathname;
|
||||
var filename = pathname.split('/').pop() || '';
|
||||
// Rimuovi eventuale query string
|
||||
filename = filename.split('?')[0];
|
||||
// Rimuovi eventuale hash
|
||||
filename = filename.split('#')[0];
|
||||
|
||||
// Prova match diretto
|
||||
if (_pageMap[filename]) {
|
||||
return _pageMap[filename];
|
||||
}
|
||||
|
||||
// Prova senza estensione
|
||||
var withoutExt = filename.replace(/\.html?$/, '');
|
||||
if (_pageMap[withoutExt]) {
|
||||
return _pageMap[withoutExt];
|
||||
}
|
||||
|
||||
// Fallback: prova a cercare nel pathname
|
||||
var segments = pathname.replace(/\.html?$/, '').split('/').filter(Boolean);
|
||||
for (var i = segments.length - 1; i >= 0; i--) {
|
||||
if (_pageMap[segments[i]]) {
|
||||
return _pageMap[segments[i]];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// ── Rendering del contenuto HTML di help ─────────────────────────
|
||||
|
||||
function _renderHelpContent(pageId) {
|
||||
var data = _helpContent[pageId];
|
||||
if (!data) return null;
|
||||
|
||||
var html = '<div class="help-content">';
|
||||
|
||||
// Intro
|
||||
html += '<div class="help-intro">' + data.intro + '</div>';
|
||||
|
||||
// Sezioni
|
||||
for (var i = 0; i < data.sections.length; i++) {
|
||||
var section = data.sections[i];
|
||||
html += '<h4>' + section.heading + '</h4>';
|
||||
html += '<ul>';
|
||||
for (var j = 0; j < section.items.length; j++) {
|
||||
html += '<li>' + section.items[j] + '</li>';
|
||||
}
|
||||
html += '</ul>';
|
||||
}
|
||||
|
||||
// Riferimenti NIS2
|
||||
if (data.references && data.references.length > 0) {
|
||||
html += '<div class="help-references">';
|
||||
html += '<h4>Riferimenti NIS2</h4>';
|
||||
html += '<ul>';
|
||||
for (var k = 0; k < data.references.length; k++) {
|
||||
html += '<li>' + data.references[k] + '</li>';
|
||||
}
|
||||
html += '</ul>';
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return { title: data.title, content: html };
|
||||
}
|
||||
|
||||
// ── API Pubblica ─────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Mostra il modale di help per una pagina specifica.
|
||||
* Se pageId non e' fornito, rileva automaticamente la pagina corrente.
|
||||
* @param {string} [pageId] - Identificativo della pagina (es. 'dashboard', 'risks')
|
||||
*/
|
||||
function showHelp(pageId) {
|
||||
_injectStyles();
|
||||
|
||||
if (!pageId) {
|
||||
pageId = _detectPage();
|
||||
}
|
||||
|
||||
if (!pageId || !_helpContent[pageId]) {
|
||||
if (typeof showNotification === 'function') {
|
||||
showNotification('Nessuna guida disponibile per questa pagina.', 'info');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var rendered = _renderHelpContent(pageId);
|
||||
if (!rendered) return;
|
||||
|
||||
var footer = '<button class="btn btn-primary" onclick="closeModal()">Ho capito</button>';
|
||||
|
||||
showModal(rendered.title, rendered.content, {
|
||||
footer: footer,
|
||||
size: 'lg'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Inietta un pulsante "?" nell'header della pagina (.content-header-actions).
|
||||
* Se l'header non esiste o il pulsante e' gia' presente, non fa nulla.
|
||||
*/
|
||||
function addHelpButton() {
|
||||
_injectStyles();
|
||||
|
||||
var pageId = _detectPage();
|
||||
if (!pageId || !_helpContent[pageId]) return;
|
||||
|
||||
var container = document.querySelector('.content-header-actions');
|
||||
if (!container) return;
|
||||
|
||||
// Evita duplicati
|
||||
if (container.querySelector('.help-btn')) return;
|
||||
|
||||
var btn = document.createElement('button');
|
||||
btn.className = 'help-btn';
|
||||
btn.setAttribute('type', 'button');
|
||||
btn.setAttribute('aria-label', 'Apri guida contestuale');
|
||||
btn.setAttribute('title', 'Guida');
|
||||
btn.innerHTML = HELP_ICON;
|
||||
|
||||
btn.addEventListener('click', function () {
|
||||
showHelp(pageId);
|
||||
});
|
||||
|
||||
container.appendChild(btn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restituisce una stringa HTML per un'icona "?" con tooltip inline.
|
||||
* Puo' essere inserita accanto a qualsiasi elemento.
|
||||
* @param {string} text - Testo del tooltip
|
||||
* @returns {string} HTML string
|
||||
*/
|
||||
function inlineHelp(text) {
|
||||
_injectStyles();
|
||||
|
||||
// Escape delle virgolette nel testo per sicurezza nell'attributo
|
||||
var safeText = text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
|
||||
return '<span class="inline-help" tabindex="0" role="button" aria-label="Informazione">' +
|
||||
HELP_ICON_SMALL +
|
||||
'<span class="inline-help-text">' + safeText + '</span>' +
|
||||
'</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Inizializza il sistema di help: inietta gli stili e aggiunge il pulsante help
|
||||
* nell'header della pagina corrente (se applicabile).
|
||||
*/
|
||||
function init() {
|
||||
_injectStyles();
|
||||
addHelpButton();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restituisce la lista degli ID di pagina supportati dal sistema di help.
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function getAvailablePages() {
|
||||
return Object.keys(_helpContent);
|
||||
}
|
||||
|
||||
// ── Esposizione API ──────────────────────────────────────────────
|
||||
|
||||
return {
|
||||
init: init,
|
||||
showHelp: showHelp,
|
||||
addHelpButton: addHelpButton,
|
||||
inlineHelp: inlineHelp,
|
||||
getAvailablePages: getAvailablePages
|
||||
};
|
||||
|
||||
})();
|
||||
257
public/js/i18n.js
Normal file
257
public/js/i18n.js
Normal file
@ -0,0 +1,257 @@
|
||||
/**
|
||||
* NIS2 Agile - i18n (Internationalization)
|
||||
*
|
||||
* Sistema di traduzione leggero IT/EN.
|
||||
* - Lingua di default: 'it'
|
||||
* - Salva preferenza in localStorage
|
||||
* - Applica traduzioni via attributo data-i18n
|
||||
* - Funzione t('chiave') per traduzione programmatica
|
||||
*/
|
||||
|
||||
const I18n = (function () {
|
||||
'use strict';
|
||||
|
||||
let _lang = 'it';
|
||||
|
||||
// ── Dizionario traduzioni ─────────────────────────────────────────
|
||||
const _dict = {
|
||||
// ── Navigazione / Sidebar ──────────────────────
|
||||
'nav.main': { it: 'Principale', en: 'Main' },
|
||||
'nav.dashboard': { it: 'Dashboard', en: 'Dashboard' },
|
||||
'nav.gap_analysis': { it: 'Gap Analysis', en: 'Gap Analysis' },
|
||||
'nav.management': { it: 'Gestione', en: 'Management' },
|
||||
'nav.risks': { it: 'Rischi', en: 'Risks' },
|
||||
'nav.incidents': { it: 'Incidenti', en: 'Incidents' },
|
||||
'nav.policies': { it: 'Policy', en: 'Policies' },
|
||||
'nav.supply_chain': { it: 'Supply Chain', en: 'Supply Chain' },
|
||||
'nav.operations': { it: 'Operativo', en: 'Operations' },
|
||||
'nav.training': { it: 'Formazione', en: 'Training' },
|
||||
'nav.assets': { it: 'Asset', en: 'Assets' },
|
||||
'nav.audit': { it: 'Audit & Report', en: 'Audit & Reports' },
|
||||
'nav.system': { it: 'Sistema', en: 'System' },
|
||||
'nav.settings': { it: 'Impostazioni', en: 'Settings' },
|
||||
'nav.help': { it: 'Guida', en: 'Help' },
|
||||
'nav.logout': { it: 'Esci', en: 'Logout' },
|
||||
|
||||
// ── Dashboard ──────────────────────────────────
|
||||
'dashboard.title': { it: 'Dashboard', en: 'Dashboard' },
|
||||
'dashboard.compliance_score': { it: 'Punteggio Compliance', en: 'Compliance Score' },
|
||||
'dashboard.open_risks': { it: 'Rischi Aperti', en: 'Open Risks' },
|
||||
'dashboard.active_incidents': { it: 'Incidenti Attivi', en: 'Active Incidents' },
|
||||
'dashboard.overdue_tasks': { it: 'Scadenze Superate', en: 'Overdue Tasks' },
|
||||
'dashboard.recent_activity': { it: 'Attivita\' Recente', en: 'Recent Activity' },
|
||||
'dashboard.upcoming_deadlines': { it: 'Prossime Scadenze', en: 'Upcoming Deadlines' },
|
||||
'dashboard.risk_heatmap': { it: 'Mappa Rischi', en: 'Risk Heatmap' },
|
||||
'dashboard.quick_actions': { it: 'Azioni Rapide', en: 'Quick Actions' },
|
||||
|
||||
// ── Assessment ─────────────────────────────────
|
||||
'assessment.title': { it: 'Gap Analysis NIS2', en: 'NIS2 Gap Analysis' },
|
||||
'assessment.new': { it: 'Nuovo Assessment', en: 'New Assessment' },
|
||||
'assessment.start': { it: 'Inizia Assessment', en: 'Start Assessment' },
|
||||
'assessment.complete': { it: 'Completa', en: 'Complete' },
|
||||
'assessment.results': { it: 'Risultati Assessment', en: 'Assessment Results' },
|
||||
'assessment.ai_analysis': { it: 'Analisi AI', en: 'AI Analysis' },
|
||||
'assessment.generate_ncr': { it: 'Genera Non Conformita\' dai Gap', en: 'Generate NCRs from Gaps' },
|
||||
'assessment.categories': { it: 'Categorie', en: 'Categories' },
|
||||
'assessment.progress': { it: 'Avanzamento', en: 'Progress' },
|
||||
'assessment.not_implemented': { it: 'Non Implementato', en: 'Not Implemented' },
|
||||
'assessment.partial': { it: 'Parzialmente Implementato', en: 'Partially Implemented' },
|
||||
'assessment.implemented': { it: 'Implementato', en: 'Implemented' },
|
||||
'assessment.not_applicable': { it: 'Non Applicabile', en: 'Not Applicable' },
|
||||
|
||||
// ── Rischi ─────────────────────────────────────
|
||||
'risks.title': { it: 'Gestione Rischi', en: 'Risk Management' },
|
||||
'risks.new': { it: 'Nuovo Rischio', en: 'New Risk' },
|
||||
'risks.matrix': { it: 'Matrice Rischi', en: 'Risk Matrix' },
|
||||
'risks.likelihood': { it: 'Probabilita\'', en: 'Likelihood' },
|
||||
'risks.impact': { it: 'Impatto', en: 'Impact' },
|
||||
'risks.treatment': { it: 'Trattamento', en: 'Treatment' },
|
||||
'risks.mitigate': { it: 'Mitigare', en: 'Mitigate' },
|
||||
'risks.accept': { it: 'Accettare', en: 'Accept' },
|
||||
'risks.transfer': { it: 'Trasferire', en: 'Transfer' },
|
||||
'risks.avoid': { it: 'Evitare', en: 'Avoid' },
|
||||
|
||||
// ── Incidenti ──────────────────────────────────
|
||||
'incidents.title': { it: 'Gestione Incidenti', en: 'Incident Management' },
|
||||
'incidents.new': { it: 'Nuovo Incidente', en: 'New Incident' },
|
||||
'incidents.early_warning': { it: 'Early Warning (24h)', en: 'Early Warning (24h)' },
|
||||
'incidents.notification': { it: 'Notifica (72h)', en: 'Notification (72h)' },
|
||||
'incidents.final_report': { it: 'Report Finale (30gg)', en: 'Final Report (30d)' },
|
||||
'incidents.timeline': { it: 'Cronologia', en: 'Timeline' },
|
||||
'incidents.severity': { it: 'Gravita\'', en: 'Severity' },
|
||||
'incidents.low': { it: 'Bassa', en: 'Low' },
|
||||
'incidents.medium': { it: 'Media', en: 'Medium' },
|
||||
'incidents.high': { it: 'Alta', en: 'High' },
|
||||
'incidents.critical': { it: 'Critica', en: 'Critical' },
|
||||
|
||||
// ── Policy ─────────────────────────────────────
|
||||
'policies.title': { it: 'Gestione Policy', en: 'Policy Management' },
|
||||
'policies.new': { it: 'Nuova Policy', en: 'New Policy' },
|
||||
'policies.ai_generate': { it: 'Genera con AI', en: 'Generate with AI' },
|
||||
'policies.approve': { it: 'Approva', en: 'Approve' },
|
||||
'policies.draft': { it: 'Bozza', en: 'Draft' },
|
||||
'policies.review': { it: 'In Revisione', en: 'Under Review' },
|
||||
'policies.approved': { it: 'Approvata', en: 'Approved' },
|
||||
'policies.published': { it: 'Pubblicata', en: 'Published' },
|
||||
|
||||
// ── Supply Chain ───────────────────────────────
|
||||
'supply_chain.title': { it: 'Sicurezza Supply Chain', en: 'Supply Chain Security' },
|
||||
'supply_chain.new_supplier': { it: 'Nuovo Fornitore', en: 'New Supplier' },
|
||||
'supply_chain.risk_overview': { it: 'Panoramica Rischi', en: 'Risk Overview' },
|
||||
'supply_chain.assess': { it: 'Valuta Fornitore', en: 'Assess Supplier' },
|
||||
|
||||
// ── Formazione ─────────────────────────────────
|
||||
'training.title': { it: 'Formazione e Awareness', en: 'Training & Awareness' },
|
||||
'training.courses': { it: 'Corsi', en: 'Courses' },
|
||||
'training.my_assignments': { it: 'I Miei Corsi', en: 'My Assignments' },
|
||||
'training.assign': { it: 'Assegna Corso', en: 'Assign Course' },
|
||||
'training.completed': { it: 'Completato', en: 'Completed' },
|
||||
'training.overdue': { it: 'Scaduto', en: 'Overdue' },
|
||||
|
||||
// ── Asset ──────────────────────────────────────
|
||||
'assets.title': { it: 'Inventario Asset', en: 'Asset Inventory' },
|
||||
'assets.new': { it: 'Nuovo Asset', en: 'New Asset' },
|
||||
'assets.dependency_map': { it: 'Mappa Dipendenze', en: 'Dependency Map' },
|
||||
'assets.hardware': { it: 'Hardware', en: 'Hardware' },
|
||||
'assets.software': { it: 'Software', en: 'Software' },
|
||||
'assets.network': { it: 'Rete', en: 'Network' },
|
||||
'assets.data': { it: 'Dati', en: 'Data' },
|
||||
|
||||
// ── Audit ──────────────────────────────────────
|
||||
'audit.title': { it: 'Audit & Report', en: 'Audit & Reports' },
|
||||
'audit.controls': { it: 'Controlli di Compliance', en: 'Compliance Controls' },
|
||||
'audit.evidence': { it: 'Evidenze', en: 'Evidence' },
|
||||
'audit.logs': { it: 'Log di Audit', en: 'Audit Logs' },
|
||||
'audit.export': { it: 'Esporta Report', en: 'Export Report' },
|
||||
|
||||
// ── Impostazioni ───────────────────────────────
|
||||
'settings.title': { it: 'Impostazioni', en: 'Settings' },
|
||||
'settings.organization': { it: 'Organizzazione', en: 'Organization' },
|
||||
'settings.profile': { it: 'Profilo Utente', en: 'User Profile' },
|
||||
'settings.members': { it: 'Membri Team', en: 'Team Members' },
|
||||
'settings.security': { it: 'Sicurezza', en: 'Security' },
|
||||
|
||||
// ── Classificazione NIS2 ───────────────────────
|
||||
'classification.essential': { it: 'Soggetto Essenziale', en: 'Essential Entity' },
|
||||
'classification.important': { it: 'Soggetto Importante', en: 'Important Entity' },
|
||||
'classification.not_applicable': { it: 'Non Applicabile', en: 'Not Applicable' },
|
||||
'classification.voluntary': { it: 'Adesione Volontaria', en: 'Voluntary Compliance' },
|
||||
|
||||
// ── Azioni comuni ──────────────────────────────
|
||||
'action.save': { it: 'Salva', en: 'Save' },
|
||||
'action.cancel': { it: 'Annulla', en: 'Cancel' },
|
||||
'action.delete': { it: 'Elimina', en: 'Delete' },
|
||||
'action.edit': { it: 'Modifica', en: 'Edit' },
|
||||
'action.create': { it: 'Crea', en: 'Create' },
|
||||
'action.close': { it: 'Chiudi', en: 'Close' },
|
||||
'action.back': { it: 'Indietro', en: 'Back' },
|
||||
'action.next': { it: 'Avanti', en: 'Next' },
|
||||
'action.confirm': { it: 'Conferma', en: 'Confirm' },
|
||||
'action.search': { it: 'Cerca', en: 'Search' },
|
||||
'action.filter': { it: 'Filtra', en: 'Filter' },
|
||||
'action.export': { it: 'Esporta', en: 'Export' },
|
||||
'action.loading': { it: 'Caricamento...', en: 'Loading...' },
|
||||
|
||||
// ── Stato ──────────────────────────────────────
|
||||
'status.draft': { it: 'Bozza', en: 'Draft' },
|
||||
'status.in_progress': { it: 'In Corso', en: 'In Progress' },
|
||||
'status.completed': { it: 'Completato', en: 'Completed' },
|
||||
'status.open': { it: 'Aperto', en: 'Open' },
|
||||
'status.closed': { it: 'Chiuso', en: 'Closed' },
|
||||
'status.active': { it: 'Attivo', en: 'Active' },
|
||||
'status.archived': { it: 'Archiviato', en: 'Archived' },
|
||||
|
||||
// ── Messaggi ───────────────────────────────────
|
||||
'msg.save_success': { it: 'Salvato con successo!', en: 'Saved successfully!' },
|
||||
'msg.delete_confirm': { it: 'Sei sicuro di voler eliminare?', en: 'Are you sure you want to delete?' },
|
||||
'msg.error_generic': { it: 'Si e\' verificato un errore.', en: 'An error occurred.' },
|
||||
'msg.error_connection': { it: 'Errore di connessione al server.', en: 'Server connection error.' },
|
||||
'msg.no_data': { it: 'Nessun dato disponibile.', en: 'No data available.' },
|
||||
|
||||
// ── Tempo relativo ─────────────────────────────
|
||||
'time.now': { it: 'Adesso', en: 'Just now' },
|
||||
'time.min_ago': { it: 'min fa', en: 'min ago' },
|
||||
'time.hours_ago': { it: 'ore fa', en: 'hours ago' },
|
||||
'time.yesterday': { it: 'Ieri', en: 'Yesterday' },
|
||||
'time.days_ago': { it: 'giorni fa', en: 'days ago' },
|
||||
};
|
||||
|
||||
// ── API Pubblica ──────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Inizializza i18n: carica lingua da localStorage o parametro.
|
||||
*/
|
||||
function init(defaultLang) {
|
||||
_lang = localStorage.getItem('nis2_lang') || defaultLang || 'it';
|
||||
applyTranslations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ritorna la lingua corrente.
|
||||
*/
|
||||
function getLang() {
|
||||
return _lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cambia lingua e ri-applica traduzioni.
|
||||
*/
|
||||
function setLang(lang) {
|
||||
if (lang !== 'it' && lang !== 'en') return;
|
||||
_lang = lang;
|
||||
localStorage.setItem('nis2_lang', lang);
|
||||
applyTranslations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Traduce una chiave.
|
||||
* @param {string} key - Chiave di traduzione (es. 'nav.dashboard')
|
||||
* @param {object} [params] - Parametri per interpolazione {name: 'valore'}
|
||||
* @returns {string}
|
||||
*/
|
||||
function t(key, params) {
|
||||
const entry = _dict[key];
|
||||
if (!entry) return key;
|
||||
let text = entry[_lang] || entry['it'] || key;
|
||||
if (params) {
|
||||
Object.keys(params).forEach(k => {
|
||||
text = text.replace(new RegExp('\\{' + k + '\\}', 'g'), params[k]);
|
||||
});
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applica traduzioni a tutti gli elementi con data-i18n.
|
||||
* Attributo data-i18n="chiave" → traduce textContent
|
||||
* Attributo data-i18n-placeholder="chiave" → traduce placeholder
|
||||
* Attributo data-i18n-title="chiave" → traduce title
|
||||
*/
|
||||
function applyTranslations() {
|
||||
document.querySelectorAll('[data-i18n]').forEach(el => {
|
||||
const key = el.getAttribute('data-i18n');
|
||||
el.textContent = t(key);
|
||||
});
|
||||
document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {
|
||||
el.placeholder = t(el.getAttribute('data-i18n-placeholder'));
|
||||
});
|
||||
document.querySelectorAll('[data-i18n-title]').forEach(el => {
|
||||
el.title = t(el.getAttribute('data-i18n-title'));
|
||||
});
|
||||
document.querySelectorAll('[data-i18n-html]').forEach(el => {
|
||||
el.innerHTML = t(el.getAttribute('data-i18n-html'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Registra nuove traduzioni (merge).
|
||||
*/
|
||||
function addTranslations(entries) {
|
||||
Object.assign(_dict, entries);
|
||||
}
|
||||
|
||||
return { init, getLang, setLang, t, applyTranslations, addTranslations };
|
||||
})();
|
||||
|
||||
// Shortcut globale
|
||||
function t(key, params) { return I18n.t(key, params); }
|
||||
@ -318,10 +318,14 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script src="/nis2/js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth & Init ─────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
// ── Labels ──────────────────────────────────────────────
|
||||
const CATEGORY_LABELS = {
|
||||
|
||||
@ -407,10 +407,14 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script src="/nis2/js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth & Init ─────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
// ── Labels ──────────────────────────────────────────────
|
||||
const controlStatusLabels = {
|
||||
|
||||
@ -418,10 +418,14 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script src="/nis2/js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth & Init ──────────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
// ── Labels ───────────────────────────────────────────────────
|
||||
const CATEGORY_LABELS = {
|
||||
|
||||
@ -485,12 +485,16 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script src="/nis2/js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth check ───────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
|
||||
// ── Init ─────────────────────────────────────────────────
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
let currentOrg = null;
|
||||
let currentUser = null;
|
||||
|
||||
@ -459,10 +459,14 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script src="/nis2/js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth & Init ─────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
// ── Labels ──────────────────────────────────────────────
|
||||
const CRITICALITY_LABELS = {
|
||||
|
||||
@ -277,10 +277,14 @@
|
||||
|
||||
<script src="/nis2/js/api.js"></script>
|
||||
<script src="/nis2/js/common.js"></script>
|
||||
<script src="/nis2/js/i18n.js"></script>
|
||||
<script src="/nis2/js/help.js"></script>
|
||||
<script>
|
||||
// ── Auth & Init ─────────────────────────────────────────
|
||||
if (!checkAuth()) throw new Error('Not authenticated');
|
||||
loadSidebar();
|
||||
I18n.init();
|
||||
HelpSystem.init();
|
||||
|
||||
// ── Labels ──────────────────────────────────────────────
|
||||
const roleLabels = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user