[FEAT] Rischi #501 p3: sezione all-hazard mantenuta ma "Rischi trasversali" comprimibile

Compromesso approvato (Simon/Cristiano) al posto della cancellazione richiesta al
punto 3 del #501 — validato nis2-expert (all-hazards = obbligo art.21.2 Direttiva
-> art.24 D.Lgs.138/2024; rimuoverla = lacuna di conformità).
- risks.html: la sezione diventa un <details> comprimibile. Header sempre visibile
  con titolo "Rischi trasversali (approccio multirischio / all-hazards) - art. 24
  D.Lgs. 138/2024" + contatore. Apri-se-vuota (updateAllhazardHeader): vuota -> aperta
  (presidiata in audit), con voci -> chiusa (meno ingombro). Nota riformulata che la
  distingue dai "rischi di non conformità".
- i18n.js custom_heading/custom_help (IT+EN) + help.js allineati.
- Manuale + KB RISCHI_MODELLO.md aggiornati. Cache-buster help/i18n 20260731.
- Solo frontend/statico.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-08-01 10:49:10 +02:00
co-authored by Claude Opus 4.8
parent 8736d6bb4f
commit ed0b16eee2
39 changed files with 125 additions and 82 deletions
+2 -3
View File
@@ -17,7 +17,7 @@ La tabella mostra **solo i requisiti applicabili alla tua classe** (soggetto *im
Allegato 1; *essenziale* → Allegato 2). Se l'elenco è lungo, il riquadro **scorre in verticale** Allegato 1; *essenziale* → Allegato 2). Se l'elenco è lungo, il riquadro **scorre in verticale**
con l'intestazione fissa. con l'intestazione fissa.
Sotto trovi una seconda tabella, **Rischi aggiuntivi (all-hazard)**: qui i profili amministrativi Sotto trovi una seconda sezione (comprimibile), **Rischi trasversali (all-hazards) — art. 24 D.Lgs. 138/2024**: qui i profili amministrativi
aggiungono rischi non legati a un singolo requisito (minacce fisiche, fornitori, eventi naturali, aggiungono rischi non legati a un singolo requisito (minacce fisiche, fornitori, eventi naturali,
errore umano) — copre l'approccio multirischio richiesto dall'art. 24 D.Lgs. 138/2024. errore umano) — copre l'approccio multirischio richiesto dall'art. 24 D.Lgs. 138/2024.
@@ -79,8 +79,7 @@ sono due piani distinti. Questa scelta evita di far dire al sistema "sei conform
è basso", che sarebbe normativamente scorretto. è basso", che sarebbe normativamente scorretto.
## 6. La matrice 5×5 ## 6. La matrice 5×5
Ogni rischio con **inerente** valutato compare nella matrice 5×5 (sempre visibile), insieme ai Ogni rischio con **inerente** valutato compare nella matrice 5×5 (sempre visibile), insieme ai rischi trasversali. La matrice è uno **strumento di rappresentazione**, non un obbligo (art. 24
rischi aggiuntivi. La matrice è uno **strumento di rappresentazione**, non un obbligo (art. 24
D.Lgs. 138/2024). Esclude i requisiti "non applicabili". D.Lgs. 138/2024). Esclude i requisiti "non applicabili".
## 7. Chi può fare cosa ## 7. Chi può fare cosa
+2 -2
View File
@@ -32,8 +32,8 @@ I profili che valutano (super_admin/org_admin/compliance_manager) compilano Prob
- **Impatto** è una scala di severità 1-5 (non euro). La matrice 5×5 (sempre visibile) esclude i requisiti "non applicabili". - **Impatto** è una scala di severità 1-5 (non euro). La matrice 5×5 (sempre visibile) esclude i requisiti "non applicabili".
- Dall'Alert si possono creare **azioni** sul rischio (seme del futuro Modulo Azioni: registro azioni, audit, KPI, budget). - Dall'Alert si possono creare **azioni** sul rischio (seme del futuro Modulo Azioni: registro azioni, audit, KPI, budget).
## Rischi aggiuntivi (all-hazard) ## Rischi trasversali (all-hazards) — sezione comprimibile
Oltre ai rischi derivati dai requisiti, i profili **amministrativi** possono aggiungere "Rischi aggiuntivi" non coperti da un singolo requisito (minacce fisiche, catena di fornitura, eventi naturali, errore umano). Serve a coprire l'approccio **multirischio/all-hazard** richiesto dall'art. 24. Gli utenti non amministrativi non vedono i tasti di inserimento. Oltre ai rischi derivati dai requisiti, i profili **amministrativi** possono registrare **rischi trasversali** non riconducibili a un singolo requisito (ambiente fisico, catena di fornitura, eventi naturali, errore umano). Copre l'approccio **multirischio/all-hazards** richiesto dall'art. 24 D.Lgs. 138/2024 (recepisce l'art. 21 §2 Direttiva UE 2022/2555). Si distingue dai «rischi di non conformità» derivati dai requisiti. La sezione è **comprimibile** e riservata agli amministrativi; resta sempre presente e, se vuota, si apre da sola. NON è stata cancellata (ticket #501 p3): rimuoverla lascerebbe scoperto un obbligo.
## Regole per l'assistente ## Regole per l'assistente
- Indirizza alla pagina dal **menu in alto "Rischi" → "Mappa dei rischi"**. - Indirizza alla pagina dal **menu in alto "Rischi" → "Mappa dei rischi"**.
+2 -2
View File
@@ -166,8 +166,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth check ─────────────────────────────────────────── // ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -89,8 +89,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth check ─────────────────────────────────────────── // ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -96,8 +96,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth check ─────────────────────────────────────────── // ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -1126,8 +1126,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
loadSidebar(); // = loadTopnavV3() loadSidebar(); // = loadTopnavV3()
+2 -2
View File
@@ -274,8 +274,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth check ─────────────────────────────────────────── // ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -381,8 +381,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth & Init ───────────────────────────────────────── // ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -194,8 +194,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
'use strict'; 'use strict';
/* /*
+2 -2
View File
@@ -201,8 +201,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -196,8 +196,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script src="/js/competenze.js?v=20260627"></script> <script src="/js/competenze.js?v=20260627"></script>
<script> <script>
function v3Icons() { if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} } } function v3Icons() { if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} } }
+2 -2
View File
@@ -102,8 +102,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script src="/js/connettori-discovery.js?v=20260627"></script> <script src="/js/connettori-discovery.js?v=20260627"></script>
<script> <script>
if (typeof checkAuth === 'function' && !checkAuth()) { if (typeof checkAuth === 'function' && !checkAuth()) {
+2 -2
View File
@@ -159,8 +159,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
'use strict'; 'use strict';
/* Controlli periodici (control monitoring §9.1/A.8.16): controlli ricorrenti + esecuzioni (evidenza) + NC/AC. */ /* Controlli periodici (control monitoring §9.1/A.8.16): controlli ricorrenti + esecuzioni (evidenza) + NC/AC. */
+2 -2
View File
@@ -226,8 +226,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Icone inline (Lucide stroke) usate nelle stringhe HTML generate dal JS. // ── Icone inline (Lucide stroke) usate nelle stringhe HTML generate dal JS.
// Renderizzate immediatamente (no createIcons re-run), self-host, niente sprite BI. ── // Renderizzate immediatamente (no createIcons re-run), self-host, niente sprite BI. ──
+2 -2
View File
@@ -116,8 +116,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script src="/js/dashboard.js?v=20260724a"></script> <script src="/js/dashboard.js?v=20260724a"></script>
<script> <script>
// ── Auth check ─────────────────────────────────────────── // ── Auth check ───────────────────────────────────────────
+2 -2
View File
@@ -1181,8 +1181,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// La guida è accessibile anche da non autenticati (utile per onboarding) // La guida è accessibile anche da non autenticati (utile per onboarding)
// ma se sei loggato carichi chrome + i18n normalmente. ZERO modifiche backend. // ma se sei loggato carichi chrome + i18n normalmente. ZERO modifiche backend.
+2 -2
View File
@@ -363,8 +363,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth & Init ────────────────────────────────────────────── // ── Auth & Init ──────────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -935,8 +935,8 @@ curl -H <span class="str">"X-API-Key: nis2_TUA_CHIAVE"</span> \
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
loadSidebar(); // = loadTopnavV3() loadSidebar(); // = loadTopnavV3()
+2 -2
View File
@@ -164,8 +164,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
'use strict'; 'use strict';
/* /*
+2 -2
View File
@@ -203,8 +203,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script src="/js/isms.js?v=20260627v3"></script> <script src="/js/isms.js?v=20260627v3"></script>
<script> <script>
// Icone Lucide della chrome + crumb/page-head statici. L'init dati e il wiring // Icone Lucide della chrome + crumb/page-head statici. L'init dati e il wiring
+1 -1
View File
@@ -611,7 +611,7 @@ const HelpSystem = (function () {
'La colonna <strong>Alert</strong> compare in base al punteggio inerente: <em>Azione Prioritaria</em> (15–25), <em>Azione Critica</em> (9–12), <em>Azione Necessaria</em> (3–8), <em>Azione Suggerita</em> (1–2). Le soglie sono una <strong>scelta metodologica</strong> dell\'organizzazione (rif. ISO/IEC 27001 §6.1.2), <strong>non un obbligo di legge</strong>. Cliccando l\'Alert si apre l\'elenco delle azioni passate su quel rischio e si può <strong>creare una nuova azione</strong>.', 'La colonna <strong>Alert</strong> compare in base al punteggio inerente: <em>Azione Prioritaria</em> (15–25), <em>Azione Critica</em> (9–12), <em>Azione Necessaria</em> (3–8), <em>Azione Suggerita</em> (1–2). Le soglie sono una <strong>scelta metodologica</strong> dell\'organizzazione (rif. ISO/IEC 27001 §6.1.2), <strong>non un obbligo di legge</strong>. Cliccando l\'Alert si apre l\'elenco delle azioni passate su quel rischio e si può <strong>creare una nuova azione</strong>.',
'La colonna <strong>Esito residuo</strong> segnala se il rischio residuo va <strong>rivalutato</strong> (punteggio 9–25) o è <strong>adeguato</strong> (1–8). <strong>Importante</strong>: il punteggio di rischio residuo <strong>non</strong> determina automaticamente lo <em>stato di conformità</em> del requisito. La <strong>conformità</strong> dipende dall\'attuazione della misura e dalle evidenze e si aggiorna in modo esplicito dal link "stato" nella colonna Rischio Residuo. Sono due piani distinti (punteggio di rischio ≠ conformità al requisito).', 'La colonna <strong>Esito residuo</strong> segnala se il rischio residuo va <strong>rivalutato</strong> (punteggio 9–25) o è <strong>adeguato</strong> (1–8). <strong>Importante</strong>: il punteggio di rischio residuo <strong>non</strong> determina automaticamente lo <em>stato di conformità</em> del requisito. La <strong>conformità</strong> dipende dall\'attuazione della misura e dalle evidenze e si aggiorna in modo esplicito dal link "stato" nella colonna Rischio Residuo. Sono due piani distinti (punteggio di rischio ≠ conformità al requisito).',
'La tabella elenca <strong>solo i requisiti applicabili alla tua classe</strong> (importante &rarr; Allegato 1, essenziale &rarr; Allegato 2 della Determinazione ACN 164179/2025). Se l\'elenco e\' lungo, il riquadro <strong>scorre verticalmente</strong> con l\'intestazione fissa.', 'La tabella elenca <strong>solo i requisiti applicabili alla tua classe</strong> (importante &rarr; Allegato 1, essenziale &rarr; Allegato 2 della Determinazione ACN 164179/2025). Se l\'elenco e\' lungo, il riquadro <strong>scorre verticalmente</strong> con l\'intestazione fissa.',
'La seconda tabella, <strong>Rischi aggiuntivi (approccio multirischio / all-hazard)</strong>, permette ai <strong>profili amministrativi</strong> di aggiungere rischi non coperti da un singolo requisito (minacce fisiche, catena di fornitura, eventi naturali): copre l\'approccio multirischio richiesto dall\'art. 24 D.Lgs. 138/2024.' 'La seconda sezione, <strong>Rischi trasversali (approccio multirischio / all-hazards)</strong> — comprimibile, riservata ai <strong>profili amministrativi</strong> — serve a registrare le minacce non riconducibili a un singolo requisito (ambiente fisico, catena di fornitura, eventi naturali, errore umano): copre l\'approccio multirischio richiesto dall\'art. 24 D.Lgs. 138/2024 (che recepisce l\'art. 21 §2 della Direttiva (UE) 2022/2555). Si distingue dai «rischi di non conformità» derivati dai requisiti. La sezione resta sempre presente: se è vuota si apre da sola.'
] ]
}, },
{ {
+2 -2
View File
@@ -182,8 +182,8 @@ const I18n = (function () {
'risks.col_measure': { it: 'Misura', en: 'Measure' }, 'risks.col_measure': { it: 'Misura', en: 'Measure' },
'risks.col_conformity': { it: 'Conformità', en: 'Conformity' }, 'risks.col_conformity': { it: 'Conformità', en: 'Conformity' },
'risks.col_evaluation': { it: 'Valutazione', en: 'Evaluation' }, 'risks.col_evaluation': { it: 'Valutazione', en: 'Evaluation' },
'risks.custom_heading': { it: 'Rischi aggiuntivi (approccio multirischio / all-hazard)', en: 'Additional risks (multi-risk / all-hazard approach)' }, 'risks.custom_heading': { it: 'Rischi trasversali (approccio multirischio / all-hazards) — art. 24 D.Lgs. 138/2024', en: 'Cross-cutting risks (multi-risk / all-hazards approach) — art. 24 Legislative Decree 138/2024' },
'risks.custom_help': { it: 'Oltre ai rischi derivati dai requisiti, i profili amministrativi possono aggiungere rischi non coperti da un singolo requisito (es. minacce fisiche, catena di fornitura, eventi naturali): copre l\'approccio multirischio/all-hazard (art. 24 D.Lgs. 138/2024).', en: 'Beyond the requirement-derived risks, administrative profiles can add risks not covered by a single requirement (e.g. physical threats, supply chain, natural events): this covers the multi-risk/all-hazard approach (art. 24 Legislative Decree 138/2024).' }, 'risks.custom_help': { it: 'Qui si registrano le minacce non riconducibili a un singolo requisito (ambiente fisico, catena di fornitura, eventi naturali, errore umano), come richiesto dall\'approccio multirischio/all-hazards (art. 24 D.Lgs. 138/2024, che recepisce l\'art. 21 §2 della Direttiva (UE) 2022/2555). Si distinguono dai «rischi di non conformità» della tabella qui sopra, che misurano il mancato o parziale soddisfacimento dei singoli requisiti.', en: 'Register here the threats not attributable to a single requirement (physical environment, supply chain, natural events, human error), as required by the multi-risk / all-hazards approach (art. 24 Legislative Decree 138/2024, transposing art. 21(2) of Directive (EU) 2022/2555). They differ from the “non-compliance risks” in the table above, which measure the missed or partial fulfilment of individual requirements.' },
'risks.matrix_caption': { it: 'La matrice 5×5 è uno strumento di rappresentazione, non un obbligo normativo (art. 24 D.Lgs. 138/2024).', en: 'The 5×5 matrix is a representation tool, not a regulatory obligation (art. 24 Legislative Decree 138/2024).' }, 'risks.matrix_caption': { it: 'La matrice 5×5 è uno strumento di rappresentazione, non un obbligo normativo (art. 24 D.Lgs. 138/2024).', en: 'The 5×5 matrix is a representation tool, not a regulatory obligation (art. 24 Legislative Decree 138/2024).' },
'risks.matrix_subtitle': { it: 'Rischi da requisiti (valutati) + aggiuntivi — esclusi i non applicabili', en: 'Requirement-derived risks (evaluated) + additional ones — non-applicable excluded' }, 'risks.matrix_subtitle': { it: 'Rischi da requisiti (valutati) + aggiuntivi — esclusi i non applicabili', en: 'Requirement-derived risks (evaluated) + additional ones — non-applicable excluded' },
'risks.col_probability': { it: 'Probabilità', en: 'Likelihood' }, 'risks.col_probability': { it: 'Probabilità', en: 'Likelihood' },
+2 -2
View File
@@ -179,8 +179,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script src="/js/kb.js"></script> <script src="/js/kb.js"></script>
<script> <script>
// Gate auth + chrome come le altre pagine V3. // Gate auth + chrome come le altre pagine V3.
+2 -2
View File
@@ -151,8 +151,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
'use strict'; 'use strict';
/* /*
+2 -2
View File
@@ -179,8 +179,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
'use strict'; 'use strict';
/* /*
+2 -2
View File
@@ -141,8 +141,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth + chrome + i18n + help ────────────────────────── // ── Auth + chrome + i18n + help ──────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -155,8 +155,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script src="/js/organigramma.js?v=20260627"></script> <script src="/js/organigramma.js?v=20260627"></script>
<script> <script>
// Render icone Lucide (chrome + crumb + bottoni). organigramma.js fa render albero // Render icone Lucide (chrome + crumb + bottoni). organigramma.js fa render albero
+2 -2
View File
@@ -344,8 +344,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth & Init ───────────────────────────────────────── // ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -145,8 +145,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script src="/js/raci.js?v=20260627"></script> <script src="/js/raci.js?v=20260627"></script>
<script> <script>
function v3Icons() { if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} } } function v3Icons() { if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} } }
+2 -2
View File
@@ -236,8 +236,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth & Init ───────────────────────────────────────── // ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -161,8 +161,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
'use strict'; 'use strict';
/* /*
+50 -6
View File
@@ -344,6 +344,24 @@
.act-row .act-when { font-size:0.72rem; color:var(--gray-500); white-space:nowrap; min-width:74px; } .act-row .act-when { font-size:0.72rem; color:var(--gray-500); white-space:nowrap; min-width:74px; }
.act-row .act-desc { flex:1; font-size:0.82rem; color:var(--gray-800); } .act-row .act-desc { flex:1; font-size:0.82rem; color:var(--gray-800); }
.act-row .act-st { font-size:0.68rem; font-weight:700; text-transform:uppercase; color:var(--gray-500); } .act-row .act-st { font-size:0.68rem; font-weight:700; text-transform:uppercase; color:var(--gray-500); }
/* ── Sezione "Rischi trasversali (all-hazards)" comprimibile (#501 p3) ── */
details.allhazard { margin: 4px 0 8px; }
details.allhazard > summary {
list-style: none; cursor: pointer; display: flex; align-items: center; gap: 10px;
padding: 12px 14px; border: 1px solid var(--gray-200, #e4e9f1); border-radius: 10px;
background: var(--gray-50, #f7f9fc); font-weight: 700; color: var(--gray-800, #1e2e46);
font-size: 1.02rem;
}
details.allhazard > summary::-webkit-details-marker { display: none; }
details.allhazard > summary:hover { background: #eef2f8; }
details.allhazard .ah-caret { display: inline-flex; color: var(--gray-500, #7382a0); transition: transform .15s; }
details.allhazard[open] > summary .ah-caret { transform: rotate(90deg); }
details.allhazard .ah-title { flex: 1; min-width: 0; }
details.allhazard .ah-count {
display: inline-flex; align-items: center; justify-content: center; min-width: 26px; height: 22px;
padding: 0 8px; border-radius: 20px; background: #e5edfb; color: #1f529e; font-size: 0.8rem; font-weight: 800;
}
details.allhazard .ah-count.empty { background: #fdeaea; color: #c0392b; }
</style> </style>
<!-- PWA:start --> <!-- PWA:start -->
<link rel="manifest" href="/manifest.webmanifest"> <link rel="manifest" href="/manifest.webmanifest">
@@ -434,11 +452,18 @@
</div> </div>
</div> </div>
<!-- ── SEZIONE 2: Rischi aggiuntivi (multirischio / all-hazard) ── --> <!-- ── SEZIONE 2: Rischi trasversali (multirischio / all-hazards) — accordion (#501 p3) ── -->
<h2 style="font-size:1.05rem;margin:0 0 6px;" data-i18n="risks.custom_heading">Rischi aggiuntivi (approccio multirischio / all-hazard)</h2> <details id="allhazard-section" class="allhazard" open>
<p class="text-muted" style="font-size:0.82rem;margin:0 0 16px;line-height:1.5;" data-i18n="risks.custom_help">Oltre ai rischi derivati dai requisiti, i profili amministrativi possono aggiungere rischi non coperti da un singolo requisito (es. minacce fisiche, catena di fornitura, eventi naturali): copre l'approccio multirischio/all-hazard (art. 24 D.Lgs. 138/2024).</p> <summary class="allhazard-summary">
<span class="ah-caret" aria-hidden="true">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="m9 18 6-6-6-6"/></svg>
</span>
<span class="ah-title" data-i18n="risks.custom_heading">Rischi trasversali (approccio multirischio / all-hazards) — art. 24 D.Lgs. 138/2024</span>
<span class="ah-count" id="allhazard-count" aria-hidden="true"></span>
</summary>
<p class="text-muted" style="font-size:0.82rem;margin:10px 0 16px;line-height:1.5;" data-i18n="risks.custom_help">Qui si registrano le minacce non riconducibili a un singolo requisito (ambiente fisico, catena di fornitura, eventi naturali, errore umano), come richiesto dall'approccio multirischio/all-hazards (art. 24 D.Lgs. 138/2024, che recepisce l'art. 21 §2 della Direttiva (UE) 2022/2555). Si distinguono dai «rischi di non conformità» della tabella qui sopra, che misurano il mancato o parziale soddisfacimento dei singoli requisiti.</p>
<!-- Table View (rischi custom) --> <!-- Table View (rischi trasversali) -->
<div id="view-table"> <div id="view-table">
<div class="filters-bar"> <div class="filters-bar">
<select class="form-select" id="filter-category" onchange="loadRisks()" aria-label="Filtra per categoria"> <select class="form-select" id="filter-category" onchange="loadRisks()" aria-label="Filtra per categoria">
@@ -486,6 +511,7 @@
</div> </div>
</div> </div>
</div> </div>
</details>
<!-- Matrix View (sempre visibile) --> <!-- Matrix View (sempre visibile) -->
<div id="view-matrix" style="margin-top:16px;"> <div id="view-matrix" style="margin-top:16px;">
@@ -544,8 +570,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth & Init ────────────────────────────────────────────── // ── Auth & Init ──────────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
@@ -1018,6 +1044,7 @@
if (result.success) { if (result.success) {
risksData = (result.data && result.data.items) || []; risksData = (result.data && result.data.items) || [];
renderRisksTable(risksData); renderRisksTable(risksData);
updateAllhazardHeader(risksData.length);
} else { } else {
showNotification(result.message || 'Errore nel caricamento rischi', 'error'); showNotification(result.message || 'Errore nel caricamento rischi', 'error');
} }
@@ -1026,6 +1053,23 @@
} }
} }
// ── Header sezione "Rischi trasversali": contatore + apri-se-vuota (una volta).
// Normativo: la sezione resta sempre presente; se è VUOTA si apre da sola così
// in audit risulta presidiata, non nascosta (parere nis2-expert, #501 p3). ──
let _allhazardInit = false;
function updateAllhazardHeader(count) {
const badge = document.getElementById('allhazard-count');
const sec = document.getElementById('allhazard-section');
if (badge) {
badge.textContent = count;
badge.classList.toggle('empty', count === 0);
}
if (sec && !_allhazardInit) {
_allhazardInit = true;
sec.open = (count === 0); // vuota → aperta; con voci → chiusa (meno ingombro)
}
}
// ── Table Render ───────────────────────────────────────────── // ── Table Render ─────────────────────────────────────────────
function renderRisksTable(risks) { function renderRisksTable(risks) {
const tbody = document.getElementById('risks-table-body'); const tbody = document.getElementById('risks-table-body');
+2 -2
View File
@@ -739,8 +739,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth check ─────────────────────────────────────────── // ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -219,8 +219,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
'use strict'; 'use strict';
/* /*
+2 -2
View File
@@ -214,8 +214,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
'use strict'; 'use strict';
/* /*
+2 -2
View File
@@ -546,8 +546,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth & Init ───────────────────────────────────────── // ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -194,8 +194,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth & Init (ordine V3) ───────────────────────────── // ── Auth & Init (ordine V3) ─────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
+2 -2
View File
@@ -262,8 +262,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');
loadSidebar(); loadSidebar();
+2 -2
View File
@@ -147,8 +147,8 @@
<script src="/js/common.js?v=20260729b"></script> <script src="/js/common.js?v=20260729b"></script>
<script src="/vendor/lucide/lucide.min.js"></script> <script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260729"></script> <script src="/js/topnav-v3.js?v=20260729"></script>
<script src="/js/i18n.js?v=20260730b"></script> <script src="/js/i18n.js?v=20260731"></script>
<script src="/js/help.js?v=20260730b"></script> <script src="/js/help.js?v=20260731"></script>
<script> <script>
// ── Auth + chrome + i18n + help ────────────────────────────── // ── Auth + chrome + i18n + help ──────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated'); if (!checkAuth()) throw new Error('Not authenticated');