[FEAT] Rischi #500 fase 2 (frontend): elenco rischi = requisiti Lex&ISO

La pagina Gestione Rischi mostra come tabella primaria (read-only) i requisiti
del framework Lex&ISO come rischi, codice RSK-<misura>Com.N via /api/risks/derived,
filtrati per classe (importante All.1 / essenziale All.2). Framing normativo
(validato nis2-expert): ogni voce = "rischio derivante dal mancato/parziale
soddisfacimento del requisito", mai "requisito = rischio"; RSK e "92" NON come
codici/numeri ufficiali ACN; matrice = strumento di rappresentazione (art.24).
- Rischi CUSTOM mantenuti in sezione "Rischi aggiuntivi (all-hazard)".
- Tasti "Nuovo Rischio"/"AI Suggerisci" limitati a super_admin/org_admin (non
  rimossi): l'inserimento libero serve al multirischio (art.24 D.Lgs.138/2024).
- api.js derivedRisks(); help.js + i18n aggiornati; version 1.25.4; SW v1.25.4.
- Cache-buster api/help/i18n ?v=20260727r. Backend/DB non toccati.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-07-27 14:15:03 +02:00
co-authored by Claude Opus 4.8
parent 571e151305
commit 4e7e75582a
46 changed files with 287 additions and 117 deletions
+1 -1
View File
@@ -70,7 +70,7 @@
</script>
<!-- Stessa logica della app: api.js + common.js (helper) + common-bi.js (override BI) -->
<script src="js/api.js?v=20260627u"></script>
<script src="js/api.js?v=20260727r"></script>
<script src="js/common.js?v=20260624g"></script>
<script src="js/common-bi.js?v=20260625d"></script>
<script>
+3 -3
View File
@@ -162,12 +162,12 @@
</main>
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide → topnav-v3.js (chrome) → i18n → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -85,12 +85,12 @@
</main>
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide → topnav-v3.js (chrome) → i18n → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -92,12 +92,12 @@
</main>
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide → topnav-v3.js (chrome) → i18n → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -1122,12 +1122,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
if (!checkAuth()) throw new Error('Not authenticated');
loadSidebar(); // = loadTopnavV3()
+3 -3
View File
@@ -270,12 +270,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help. La logica
pagina (wizard GAP) resta inline: usa gli stessi id/funzioni di prima. -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -377,12 +377,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -190,12 +190,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help. La logica del
calendario (aggregatore read-only) resta inline e usa gli stessi id/funzioni. -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -139,12 +139,12 @@
</main>
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide → topnav-v3.js (chrome) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -192,12 +192,12 @@
</div>
<!-- Scripts V3. competenze.js si auto-inizializza su DOMContentLoaded (checkAuth/loadSidebar/I18n/Help). -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script src="/js/competenze.js?v=20260627"></script>
<script>
function v3Icons() { if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} } }
+3 -3
View File
@@ -98,12 +98,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script src="/js/connettori-discovery.js?v=20260627"></script>
<script>
if (typeof checkAuth === 'function' && !checkAuth()) {
+3 -3
View File
@@ -155,12 +155,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
'use strict';
/* Controlli periodici (control monitoring §9.1/A.8.16): controlli ricorrenti + esecuzioni (evidenza) + NC/AC. */
+3 -3
View File
@@ -222,12 +222,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Icone inline (Lucide stroke) usate nelle stringhe HTML generate dal JS.
// Renderizzate immediatamente (no createIcons re-run), self-host, niente sprite BI. ──
+3 -3
View File
@@ -112,12 +112,12 @@
<!-- Scripts V3: api.js (client REST, prima) → common.js (helper+FAB reali) →
Lucide self-host → topnav-v3.js (chrome, ridefinisce loadSidebar) →
i18n → help → dashboard.js -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script src="/js/dashboard.js?v=20260724a"></script>
<script>
// ── Auth check ───────────────────────────────────────────
+1 -1
View File
@@ -73,7 +73,7 @@
</script>
<!-- Stessa logica della forgot-password.html: ZERO modifiche backend -->
<script src="js/api.js?v=20260627u"></script>
<script src="js/api.js?v=20260727r"></script>
<script>
const form = document.getElementById('forgot-form');
const err = document.getElementById('err');
+3 -3
View File
@@ -1177,12 +1177,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// La guida è accessibile anche da non autenticati (utile per onboarding)
// ma se sei loggato carichi chrome + i18n normalmente. ZERO modifiche backend.
+3 -3
View File
@@ -359,12 +359,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth & Init ──────────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -931,12 +931,12 @@ curl -H <span class="str">"X-API-Key: nis2_TUA_CHIAVE"</span> \
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
if (!checkAuth()) throw new Error('Not authenticated');
loadSidebar(); // = loadTopnavV3()
+3 -3
View File
@@ -160,12 +160,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -199,12 +199,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → isms.js.
isms.js si auto-inizializza su DOMContentLoaded (checkAuth/I18n/loadSidebar/Help/render). -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script src="/js/isms.js?v=20260627v3"></script>
<script>
// Icone Lucide della chrome + crumb/page-head statici. L'init dati e il wiring
+1
View File
@@ -410,6 +410,7 @@ class NIS2API {
updateRisk(id, data) { return this.put(`/risks/${id}`, data); }
deleteRisk(id) { return this.del(`/risks/${id}`); }
getRiskMatrix() { return this.get('/risks/matrix'); }
derivedRisks() { return this.get('/risks/derived'); }
aiSuggestRisks() { return this.post('/risks/ai-suggest', {}); }
// FAIR quantitativo + KRI (P2)
computeFair(id, data) { return this.post(`/risks/${id}/fair`, data); }
+11 -1
View File
@@ -599,11 +599,21 @@ const HelpSystem = (function () {
// ─── 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. 24 del D.Lgs. 138/2024 (che recepisce l\'art. 21 della Direttiva (UE) 2022/2555). Include una matrice di rischio interattiva e strumenti per il monitoraggio continuo.',
intro: 'In questa pagina i rischi corrispondono ai requisiti del framework di sicurezza applicabili alla tua organizzazione e alla loro valutazione. I requisiti derivano dalle misure di sicurezza di base della Determinazione ACN n. 164179 del 14/4/2025 (Allegato 1 per i soggetti importanti, Allegato 2 per gli essenziali). Ogni voce rappresenta il rischio derivante dal mancato o parziale soddisfacimento del requisito, non il requisito in se\'. Tutto in conformita\' con l\'art. 24 del D.Lgs. 138/2024 (che recepisce l\'art. 21 della Direttiva (UE) 2022/2555).',
sections: [
{
heading: 'Come leggere i rischi in questa pagina',
items: [
'La <strong>prima tabella</strong> elenca i rischi <strong>derivati dai requisiti</strong> del framework di sicurezza: ogni voce e\' il <strong>rischio di non conformita\'</strong> legato al mancato o parziale soddisfacimento di un requisito, <strong>non il requisito in se\'</strong>. E\' un elenco in <strong>sola lettura</strong>.',
'Il codice <strong>RSK-‹misura›Com.‹n›</strong> e\' una <strong>codifica interna di NIS2 Agile</strong>; ‹misura› (es. GV.OC-04) e\' il codice della misura del Framework Nazionale richiamato dalla Determinazione ACN 164179/2025. <strong>Non</strong> e\' un codice o numero ufficiale ACN.',
'La colonna <strong>Conformita\'</strong> mostra lo stato di valutazione del requisito (Da valutare, Non conforme, Parziale, Conforme). I requisiti <strong>non applicabili alla tua classe</strong> (importante vs essenziale) sono mostrati in grigio.',
'La seconda tabella, <strong>Rischi aggiuntivi (approccio multirischio / all-hazard)</strong>, permette ai <strong>profili amministrativi</strong> (org_admin, super_admin) 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.'
]
},
{
heading: 'Matrice di Rischio',
items: [
'La matrice 5×5 e\' uno <strong>strumento di rappresentazione</strong>, non un obbligo normativo: l\'art. 24 D.Lgs. 138/2024 richiede un\'analisi dei rischi proporzionata e con approccio multirischio, senza imporre una metodologia specifica.',
'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>.',
+10
View File
@@ -159,6 +159,16 @@ const I18n = (function () {
'risks.accept': { it: 'Accettare', en: 'Accept' },
'risks.transfer': { it: 'Trasferire', en: 'Transfer' },
'risks.avoid': { it: 'Evitare', en: 'Avoid' },
'risks.derived_heading': { it: 'Rischi dai requisiti del framework di sicurezza', en: 'Risks from security framework requirements' },
'risks.derived_code_legend': { it: 'RSK-‹misura›Com.‹n› è una codifica interna di NIS2 Agile; ‹misura› (es. GV.OC-04) è il codice della misura del Framework Nazionale richiamato dalla Determinazione ACN 164179/2025. Elenco in sola lettura.', en: 'RSK-‹measure›Com.‹n› is an internal NIS2 Agile code; ‹measure› (e.g. GV.OC-04) is the National Framework measure code referenced by ACN Determination 164179/2025. Read-only list.' },
'risks.col_code': { it: 'Codice', en: 'Code' },
'risks.col_risk': { it: 'Rischio', en: 'Risk' },
'risks.col_measure': { it: 'Misura', en: 'Measure' },
'risks.col_conformity': { it: 'Conformità', en: 'Conformity' },
'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_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.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).' },
// ── Incidenti ──────────────────────────────────
'incidents.title': { it: 'Gestione Incidenti', en: 'Incident Management' },
+3 -3
View File
@@ -175,12 +175,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → kb.js -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script src="/js/kb.js"></script>
<script>
// Gate auth + chrome come le altre pagine V3.
+1 -1
View File
@@ -515,7 +515,7 @@ body { background: var(--bg-main); }
<div id="toast"></div>
<script src="js/api.js?v=20260627u"></script>
<script src="js/api.js?v=20260727r"></script>
<script>
// ══════════════════════════════════════════════════════
// CONFIG
+1 -1
View File
@@ -94,7 +94,7 @@
</script>
<!-- Stessa logica della login attuale: ZERO modifiche backend -->
<script src="js/api.js?v=20260627u"></script>
<script src="js/api.js?v=20260727r"></script>
<script src="js/common.js?v=20260624g"></script>
<script>
if (api.isAuthenticated()) { window.location.href = 'dashboard.html'; }
+3 -3
View File
@@ -147,12 +147,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -175,12 +175,12 @@
</div>
<!-- Scripts V3 (ordine ESATTO come dashboard.html) -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -137,12 +137,12 @@
</main>
<!-- Scripts V3 (ordine ESATTO come dashboard.html) -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth + chrome + i18n + help ──────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+1 -1
View File
@@ -494,7 +494,7 @@
</script>
<!-- Stessa logica della onboarding.html: ZERO modifiche backend -->
<script src="js/api.js?v=20260627u"></script>
<script src="js/api.js?v=20260727r"></script>
<script src="js/common.js?v=20260624g"></script>
<script>
// ── Auth check ──────────────────────────────────────────────────
+3 -3
View File
@@ -151,12 +151,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → organigramma.js.
organigramma.js si auto-inizializza su DOMContentLoaded (checkAuth/loadSidebar/I18n/Help). -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script src="/js/organigramma.js?v=20260627"></script>
<script>
// Render icone Lucide (chrome + crumb + bottoni). organigramma.js fa render albero
+3 -3
View File
@@ -340,12 +340,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -141,12 +141,12 @@
</main>
<!-- Scripts V3. raci.js si auto-inizializza su DOMContentLoaded (checkAuth/loadSidebar/I18n/Help). -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script src="/js/raci.js?v=20260627"></script>
<script>
function v3Icons() { if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} } }
+1 -1
View File
@@ -268,7 +268,7 @@
</script>
<!-- Stessa logica della register.html: ZERO modifiche backend -->
<script src="js/api.js?v=20260627u"></script>
<script src="js/api.js?v=20260727r"></script>
<script src="js/common.js?v=20260624g"></script>
<script>
if (api.isAuthenticated()) { window.location.href = 'dashboard.html'; }
+3 -3
View File
@@ -232,12 +232,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -157,12 +157,12 @@
</div>
<!-- Scripts V3 (ordine ESATTO come dashboard.html) -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
'use strict';
/*
+155 -6
View File
@@ -331,12 +331,57 @@
<button class="btn-ghost" id="btn-ai-suggest" onclick="aiSuggest()">
<i data-lucide="sparkles" class="ic"></i>AI Suggerisci
</button>
<button class="btn-primary" onclick="showRiskModal()"><i data-lucide="plus" class="ic"></i>Nuovo Rischio</button>
<button class="btn-primary" id="btn-new-risk" onclick="showRiskModal()"><i data-lucide="plus" class="ic"></i>Nuovo Rischio</button>
</div>
</div>
<div class="content-body">
<!-- Table View -->
<!-- ── Nota introduttiva (framing normativo) ───────────── -->
<div class="risk-intro-note" style="display:flex;gap:12px;align-items:flex-start;padding:16px 18px;background:var(--gray-50);border:1px solid var(--gray-200);border-left:4px solid var(--primary);border-radius:var(--border-radius);margin-bottom:24px;">
<i data-lucide="info" class="ic" style="flex-shrink:0;color:var(--primary);margin-top:2px;"></i>
<p style="font-size:0.85rem;color:var(--gray-700);line-height:1.55;margin:0;">
In questa pagina i rischi corrispondono ai requisiti del framework di sicurezza applicabili alla tua organizzazione e alla loro valutazione. I requisiti derivano dalle misure di sicurezza di base della Determinazione ACN n. 164179 del 14/4/2025 (Allegato 1 per i soggetti importanti, Allegato 2 per gli essenziali). Ogni voce rappresenta il rischio derivante dal mancato o parziale soddisfacimento del requisito, non il requisito in sé. La valutazione e la matrice 5×5 sono uno strumento di rappresentazione: la normativa (art. 24 D.Lgs. 138/2024) richiede un'analisi dei rischi proporzionata e con approccio multirischio, senza imporre una metodologia specifica.
</p>
</div>
<!-- ── SEZIONE 1: Rischi derivati dai requisiti (PRIMARIA, read-only) ── -->
<div id="view-derived" style="margin-bottom:32px;">
<div style="display:flex;justify-content:space-between;align-items:baseline;flex-wrap:wrap;gap:8px;margin-bottom:10px;">
<h2 style="font-size:1.05rem;margin:0;" data-i18n="risks.derived_heading">Rischi dai requisiti del framework di sicurezza</h2>
<span class="text-muted" style="font-size:0.78rem;" id="derived-source-note"></span>
</div>
<p class="text-muted" style="font-size:0.78rem;margin:0 0 12px;">
<span data-i18n="risks.derived_code_legend">RSK-‹misura›Com.‹n› è una codifica interna di NIS2 Agile; ‹misura› (es. GV.OC-04) è il codice della misura del Framework Nazionale richiamato dalla Determinazione ACN 164179/2025. Elenco in sola lettura.</span>
</p>
<div class="card">
<div class="table-container">
<table>
<thead>
<tr>
<th scope="col" data-i18n="risks.col_code">Codice</th>
<th scope="col" data-i18n="risks.col_risk">Rischio</th>
<th scope="col" data-i18n="risks.col_measure">Misura</th>
<th scope="col" data-i18n="risks.col_conformity">Conformità</th>
<th scope="col" data-i18n="risks.col_evaluation">Valutazione</th>
</tr>
</thead>
<tbody id="derived-table-body">
<tr>
<td colspan="5">
<div class="spinner" style="margin:40px auto;"></div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- ── SEZIONE 2: Rischi aggiuntivi (multirischio / all-hazard) ── -->
<h2 style="font-size:1.05rem;margin:0 0 6px;" data-i18n="risks.custom_heading">Rischi aggiuntivi (approccio multirischio / all-hazard)</h2>
<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>
<!-- Table View (rischi custom) -->
<div id="view-table">
<div class="filters-bar">
<select class="form-select" id="filter-category" onchange="loadRisks()" aria-label="Filtra per categoria">
@@ -426,6 +471,7 @@
</div>
</div>
</div>
<p class="text-muted" style="font-size:0.78rem;text-align:center;margin:16px auto 0;max-width:640px;line-height:1.5;" data-i18n="risks.matrix_caption">La matrice 5×5 è uno strumento di rappresentazione, non un obbligo normativo (art. 24 D.Lgs. 138/2024).</p>
</div>
</div>
</div>
@@ -437,12 +483,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth & Init ──────────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
@@ -489,11 +535,112 @@
// ── State ────────────────────────────────────────────────────
let currentView = 'table';
let risksData = [];
let derivedData = [];
let orgClass = null;
// ── Gating ruolo: creazione/AI riservate ai profili amministrativi ──
const ADMIN_ROLES = ['super_admin', 'org_admin'];
(function gateAdminActions() {
const role = (api.getUserRole && api.getUserRole()) || '';
if (!ADMIN_ROLES.includes(role)) {
['btn-ai-suggest', 'btn-new-risk'].forEach(id => {
const el = document.getElementById(id);
if (el) el.style.display = 'none';
});
}
})();
// ── Load ─────────────────────────────────────────────────────
loadDerivedRisks();
loadRisks();
loadMatrix();
// ── Derived Risks (requisiti del framework) ──────────────────
// Badge conformità per lo stato di valutazione del requisito.
const DERIVED_STATUS_BADGE = {
da_valutare: { cls: 'badge-neutral', label: 'Da valutare' },
non_applicabile: { cls: 'badge-neutral', label: 'Non applicabile' },
non_conforme: { cls: 'badge-danger', label: 'Non conforme' },
parziale: { cls: 'badge-warning', label: 'Parziale' },
conforme: { cls: 'badge-success', label: 'Conforme' },
};
function isDerivedApplicable(r) {
// important → applicabili solo i requisiti Allegato 1; essential → Allegato 2;
// classe non determinata / adesione volontaria → tutti applicabili.
if (orgClass === 'important') return r.applies_important == 1;
if (orgClass === 'essential') return r.applies_essential == 1;
return true;
}
async function loadDerivedRisks() {
try {
const result = await api.derivedRisks();
if (result && result.success && result.data) {
orgClass = result.data.org_class || null;
derivedData = result.data.risks || [];
const src = result.data.source || '';
const note = document.getElementById('derived-source-note');
if (note && src) note.textContent = 'Fonte: ' + src;
renderDerivedTable(derivedData);
} else {
renderDerivedTable([]);
}
} catch (e) {
renderDerivedTable([]);
}
}
function renderDerivedTable(rows) {
const tbody = document.getElementById('derived-table-body');
if (!rows || rows.length === 0) {
tbody.innerHTML = `
<tr><td colspan="5">
<div class="empty-state">
<svg viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M10 1.944A11.954 11.954 0 012.166 5C2.056 5.649 2 6.319 2 7c0 5.225 3.34 9.67 8 11.317C14.66 16.67 18 12.225 18 7c0-.682-.057-1.35-.166-2.001A11.954 11.954 0 0110 1.944z" clip-rule="evenodd"/></svg>
<h4>Nessun requisito applicabile</h4>
<p>Non risultano requisiti del framework di sicurezza per questa organizzazione.</p>
</div>
</td></tr>`;
return;
}
let html = '';
rows.forEach(r => {
const applicable = isDerivedApplicable(r);
const st = DERIVED_STATUS_BADGE[r.stato] || DERIVED_STATUS_BADGE.da_valutare;
const measureCode = r.misura_code || '-';
const reqCode = r.requisito_code || '';
const reqDescr = r.requisito_descr || '';
const measureDescr = r.misura_descr || '';
const evalVal = (r.valutazione_rischio !== null && r.valutazione_rischio !== undefined && r.valutazione_rischio !== '')
? escapeHtml(String(r.valutazione_rischio)) : '—';
const rowStyle = applicable ? '' : 'style="opacity:0.5;"';
const riskTooltip = `Rischio derivante dal mancato o parziale soddisfacimento del requisito ${measureCode} ${reqCode}`.trim();
// Colonna "Rischio": etichetta + requisito come OGGETTO del rischio (mai il requisito nudo).
const conformityCell = applicable
? `<span class="badge ${st.cls}">${escapeHtml(st.label)}</span>`
: `<span class="badge badge-neutral">Non applicabile alla tua classe</span>`;
html += `
<tr ${rowStyle}>
<td><code style="font-size:0.78rem;color:var(--primary);">${escapeHtml(r.risk_code || '-')}</code></td>
<td title="${escapeHtml(riskTooltip)}">
<div style="font-size:0.68rem;text-transform:uppercase;letter-spacing:0.04em;font-weight:600;color:var(--gray-500);margin-bottom:2px;">Rischio di non conformità</div>
<div style="font-size:0.86rem;color:var(--gray-800);">${escapeHtml(reqDescr)}</div>
</td>
<td>
<code style="font-size:0.75rem;color:var(--gray-600);">${escapeHtml(measureCode)}</code>
${measureDescr ? `<div style="font-size:0.78rem;color:var(--gray-500);margin-top:2px;">${escapeHtml(measureDescr)}</div>` : ''}
</td>
<td>${conformityCell}</td>
<td>${evalVal}</td>
</tr>`;
});
tbody.innerHTML = html;
}
async function loadRisks() {
const params = {};
const cat = document.getElementById('filter-category').value;
@@ -666,6 +813,7 @@
}
function renderDetail(risk) {
document.getElementById('view-derived').classList.add('hidden');
document.getElementById('view-table').classList.add('hidden');
document.getElementById('view-matrix').classList.add('hidden');
const container = document.getElementById('view-detail');
@@ -839,7 +987,8 @@
function backToList() {
document.getElementById('view-detail').classList.add('hidden');
// Tabella e matrice sono entrambe sempre visibili.
// Sezione derivati, tabella custom e matrice sono sempre visibili.
document.getElementById('view-derived').classList.remove('hidden');
document.getElementById('view-table').classList.remove('hidden');
document.getElementById('view-matrix').classList.remove('hidden');
}
+3 -3
View File
@@ -735,12 +735,12 @@
<!-- Scripts V3: api.js (client REST, prima) → common.js (helper+FAB reali) →
Lucide self-host → topnav-v3.js (chrome, ridefinisce loadSidebar) →
i18n → help → init pagina (inline, invariato). -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth check ───────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+3 -3
View File
@@ -215,12 +215,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -210,12 +210,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
'use strict';
/*
+3 -3
View File
@@ -542,12 +542,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth & Init ─────────────────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+1 -1
View File
@@ -13,7 +13,7 @@
* Il nome cache include la release: va BUMPATO ad ogni rilascio (vedi version.json),
* cosi' activate elimina automaticamente la shell vecchia.
*/
const CACHE = 'nis2-shell-v1.25.0';
const CACHE = 'nis2-shell-v1.25.4';
// Shell V3 (top-nav): niente più style.css/common-bi.js/bootstrap-italia (UI V2).
const PRECACHE = [
+3 -3
View File
@@ -190,12 +190,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali+showModal/showNotification) →
Lucide self-host → topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help.
La logica della pagina e' inline (nessun training.js): preservata invariata. -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth & Init (ordine V3) ─────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');
+1 -1
View File
@@ -1 +1 @@
{"version": "1.25.3", "build": "2026-07-27-v1.25.3", "date": "2026-07-27", "changelog": "Gestione Rischi (ticket #500 fase 2, backend): nuovo endpoint GET /api/risks/derived che deriva l'elenco dei rischi dai requisiti del framework LEX&ISO (Determinazione ACN n. 164179/2025) con codifica RSK-<misura>Com.N (es. RSK-GV.OC-04Com.1). Additivo, read-only, non tocca la tabella rischi. Prossimo passo: la pagina rischi consumera' questo elenco e verranno rimossi i bottoni 'Nuovo Rischio' e 'AI Suggerisci'."}
{"version": "1.25.4", "build": "2026-07-27-v1.25.4", "date": "2026-07-27", "changelog": "Gestione Rischi (ticket #500): l'elenco dei rischi mostra ora i requisiti del framework Lex&ISO applicabili alla tua organizzazione, con codice interno RSK-<misura>Com.N (es. RSK-GV.OC-04Com.1) e lo stato di conformita'. Ogni voce rappresenta il rischio derivante dal mancato o parziale soddisfacimento del requisito (Determinazione ACN n. 164179/2025). I 'Rischi aggiuntivi' (approccio multirischio/all-hazard) restano inseribili dai profili amministrativi. La matrice 5x5 e' uno strumento di rappresentazione (art. 24 D.Lgs. 138/2024)."}
+3 -3
View File
@@ -258,12 +258,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
if (!checkAuth()) throw new Error('Not authenticated');
loadSidebar();
+3 -3
View File
@@ -143,12 +143,12 @@
<!-- Scripts V3: api.js → common.js (helper+FAB reali) → Lucide self-host →
topnav-v3.js (chrome, ridefinisce loadSidebar) → i18n → help → init pagina. -->
<script src="/js/api.js?v=20260627u"></script>
<script src="/js/api.js?v=20260727r"></script>
<script src="/js/common.js?v=20260627t"></script>
<script src="/vendor/lucide/lucide.min.js"></script>
<script src="/js/topnav-v3.js?v=20260627t"></script>
<script src="/js/i18n.js?v=20260627p"></script>
<script src="/js/help.js?v=20260727a"></script>
<script src="/js/i18n.js?v=20260727r"></script>
<script src="/js/help.js?v=20260727r"></script>
<script>
// ── Auth + chrome + i18n + help ──────────────────────────────
if (!checkAuth()) throw new Error('Not authenticated');