[FIX] ri-review guida: 2 fix guida + 2 fix prodotto UI orfane

cap-5: sei->quattro modalita + chiosa adeguatezza-ruolo sotto GV.RR-04 (non GV.PO-01)
cap-7: nomi bottoni allineati alla UI (Invia Report Finale 30gg)
assets.html: bottone Importa (openImportModal era orfana)
reports.html: tab Requisiti ACN (loadAcnRequirements -> endpoint esistente, 87/116 per entity_type)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile 2026-05-31 08:51:30 +02:00
parent 33790427df
commit 7a23789b0f
3 changed files with 72 additions and 5 deletions

View File

@ -264,6 +264,7 @@
<h2 data-i18n="assets.title">Inventario Asset</h2> <h2 data-i18n="assets.title">Inventario Asset</h2>
<div class="content-header-actions"> <div class="content-header-actions">
<span class="text-muted" style="font-size:0.8rem; margin-right:8px;">Art. 21.2.i NIS2</span> <span class="text-muted" style="font-size:0.8rem; margin-right:8px;">Art. 21.2.i NIS2</span>
<button class="btn btn-secondary" onclick="openImportModal()" title="Importa asset da CSV/CMDB">Importa</button>
<button class="btn btn-primary" onclick="showCreateAssetModal()">+ Nuovo Asset</button> <button class="btn btn-primary" onclick="showCreateAssetModal()">+ Nuovo Asset</button>
</div> </div>
</header> </header>

View File

@ -425,7 +425,7 @@
<h3>Come si fa nella piattaforma</h3> <h3>Come si fa nella piattaforma</h3>
<ol class="step-list"> <ol class="step-list">
<li>Vai su <strong>Gap Analysis</strong> e clicca "Nuovo Assessment".</li> <li>Vai su <strong>Gap Analysis</strong> e clicca "Nuovo Assessment".</li>
<li>Rispondi alle 80 domande (sei modalità: implementato / parziale / non implementato / non applicabile).</li> <li>Rispondi alle 80 domande (quattro modalità: implementato / parziale / non implementato / non applicabile).</li>
<li>Per ogni risposta, indica il <strong>livello di maturità</strong> (05).</li> <li>Per ogni risposta, indica il <strong>livello di maturità</strong> (05).</li>
<li>Salva: puoi continuare in più sessioni.</li> <li>Salva: puoi continuare in più sessioni.</li>
<li>Quando finisci, clicca <strong>"Analisi AI"</strong> per ricevere raccomandazioni prioritarie.</li> <li>Quando finisci, clicca <strong>"Analisi AI"</strong> per ricevere raccomandazioni prioritarie.</li>
@ -449,9 +449,10 @@
<div class="example-box"> <div class="example-box">
<strong>Esempio (requisiti di governance).</strong> <code>GV.RR-04</code> richiede che la cybersicurezza <strong>Esempio (requisiti di governance).</strong> <code>GV.RR-04</code> richiede che la cybersicurezza
sia integrata nella gestione delle risorse umane: chi accede ai sistemi (utenti e amministratori) deve sia integrata nella gestione delle risorse umane: chi accede ai sistemi (utenti e amministratori) deve
avere <strong>competenze e affidabilità adeguate al ruolo</strong>, valutate e documentate. avere <strong>competenze e affidabilità adeguate al ruolo</strong>, valutate e documentate — è il
"controllo di adeguatezza al ruolo" delle figure chiave dell'organigramma.
<code>GV.PO-01</code> richiede una <strong>policy di gestione del rischio cyber</strong> formalizzata, <code>GV.PO-01</code> richiede una <strong>policy di gestione del rischio cyber</strong> formalizzata,
comunicata e applicata. È il "controllo di adeguatezza al ruolo" delle figure chiave dell'organigramma. comunicata e applicata.
</div> </div>
<div class="callout-tip"> <div class="callout-tip">
<strong>Come si collega alla piattaforma.</strong> Le <strong>80 domande</strong> della Gap Analysis <strong>Come si collega alla piattaforma.</strong> Le <strong>80 domande</strong> della Gap Analysis
@ -612,8 +613,8 @@
<li>Vai su <strong>Incidenti</strong> e clicca "+ Nuovo Incidente".</li> <li>Vai su <strong>Incidenti</strong> e clicca "+ Nuovo Incidente".</li>
<li>Compila titolo, classificazione, severità, ora di rilevazione.</li> <li>Compila titolo, classificazione, severità, ora di rilevazione.</li>
<li>Il sistema calcola automaticamente le 3 scadenze (24h / 72h / 1 mese).</li> <li>Il sistema calcola automaticamente le 3 scadenze (24h / 72h / 1 mese).</li>
<li>Quando arriva il momento, usa i bottoni <strong>"Invia Early Warning"</strong>, <li>Quando arriva il momento, usa i bottoni <strong>"Invia Early Warning (24h)"</strong>,
<strong>"Invia Notifica"</strong> e <strong>"Invia Final Report"</strong>: le email partono verso <strong>"Invia Notifica (72h)"</strong> e <strong>"Invia Report Finale (30gg)"</strong>: le email partono verso
l'indirizzo CSIRT configurato.</li> l'indirizzo CSIRT configurato.</li>
<li>Aggiorna lo stato dell'incidente (analisi → contenimento → eradicazione → recovery → chiuso).</li> <li>Aggiorna lo stato dell'incidente (analisi → contenimento → eradicazione → recovery → chiuso).</li>
<li>Compila root cause e lezioni apprese alla chiusura.</li> <li>Compila root cause e lezioni apprese alla chiusura.</li>

View File

@ -360,6 +360,7 @@
<button onclick="switchTab('audit', this)">Audit Log</button> <button onclick="switchTab('audit', this)">Audit Log</button>
<button onclick="switchTab('iso', this)">ISO 27001</button> <button onclick="switchTab('iso', this)">ISO 27001</button>
<button onclick="switchTab('monitoring', this)" data-i18n="audit.monitoring_tab">Monitoraggio Continuo</button> <button onclick="switchTab('monitoring', this)" data-i18n="audit.monitoring_tab">Monitoraggio Continuo</button>
<button onclick="switchTab('acn', this)">Requisiti ACN</button>
</div> </div>
<!-- Tab: Report Compliance --> <!-- Tab: Report Compliance -->
@ -412,6 +413,15 @@
</div> </div>
</div> </div>
</div> </div>
<div class="tab-panel" id="tab-acn">
<div id="acn-container">
<div class="loading-state">
<div class="spinner"></div>
<p>Caricamento requisiti ACN...</p>
</div>
</div>
</div>
</div> </div>
</main> </main>
</div> </div>
@ -479,6 +489,61 @@
if (tabId === 'audit') { auditPage = 1; loadAuditLogs(); } if (tabId === 'audit') { auditPage = 1; loadAuditLogs(); }
if (tabId === 'iso') loadIsoMapping(); if (tabId === 'iso') loadIsoMapping();
if (tabId === 'monitoring') loadMonitoring(); if (tabId === 'monitoring') loadMonitoring();
if (tabId === 'acn') loadAcnRequirements();
}
// ── Requisiti ACN (specifiche di base, Determina 164179/2025) ──────────
const ACN_ST = {
not_started: ['Da iniziare', '#9ca3af'],
in_progress: ['In corso', '#eab308'],
implemented: ['Implementato', '#22c55e'],
not_applicable:['Non applicabile', '#64748b']
};
async function loadAcnRequirements() {
const c = document.getElementById('acn-container');
if (!c) return;
c.innerHTML = '<div class="loading-state"><div class="spinner"></div><p>Caricamento...</p></div>';
let res;
try { res = await api.getAcnRequirements(); }
catch (e) { c.innerHTML = '<p style="color:#ef4444;padding:16px;">Errore di connessione</p>'; return; }
if (!res.success) { c.innerHTML = '<p style="color:#ef4444;padding:16px;">' + escapeHtml(res.message || 'Errore') + '</p>'; return; }
const d = res.data;
const s = d.summary || {};
const cards = ['implemented','in_progress','not_started','not_applicable'].map(k =>
`<div style="border-left:4px solid ${ACN_ST[k][1]};padding:8px 14px;background:var(--gray-50,#f8fafc);border-radius:6px;min-width:110px;">
<div style="font-size:1.4rem;font-weight:700;">${s[k]||0}</div><div style="font-size:.75rem;color:#64748b;">${ACN_ST[k][0]}</div></div>`).join('');
const groups = {};
(d.requirements||[]).forEach(r => { (groups[r.function_name] = groups[r.function_name] || []).push(r); });
let body = '';
Object.keys(groups).forEach(fn => {
const reqs = groups[fn];
body += `<h3 style="margin:18px 0 8px;">${escapeHtml(fn)} <span style="font-weight:400;color:#64748b;font-size:.85rem;">(${reqs.length})</span></h3>`;
body += '<table class="data-table" style="width:100%;"><thead><tr><th style="width:90px;">Sottocat.</th><th>Requisito</th><th style="width:175px;">Stato</th></tr></thead><tbody>';
reqs.forEach(r => {
const st = ACN_ST[r.status] || ACN_ST.not_started;
const opts = Object.keys(ACN_ST).map(k => `<option value="${k}"${k===r.status?' selected':''}>${ACN_ST[k][0]}</option>`).join('');
body += `<tr>
<td><code title="${escapeHtml(r.subcategory_text||'')}">${escapeHtml(r.subcategory)}</code></td>
<td>${escapeHtml(r.requirement)}</td>
<td><span style="display:inline-block;width:10px;height:10px;border-radius:50%;background:${st[1]};margin-right:6px;"></span>
<select onchange="updateAcn(${r.id}, this.value)" style="padding:4px;border:1px solid #cbd5e1;border-radius:5px;">${opts}</select></td></tr>`;
});
body += '</tbody></table>';
});
c.innerHTML = `
<p style="font-size:.85rem;color:#475569;margin-bottom:12px;">
Requisiti operativi <strong>specifiche di base ACN</strong> (Determina 164179/2025, Framework Nazionale 2025)
per questa organizzazione (<strong>${escapeHtml(d.entity)}</strong>: ${d.total} requisiti).
Compliance: <strong>${d.compliance_percent}%</strong>. <em>${escapeHtml(d.source||'')}</em></p>
<div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:16px;">${cards}</div>
${body}`;
}
async function updateAcn(id, status) {
try {
const res = await api.updateAcnRequirement(id, status, null);
if (res.success) { if (typeof showNotification==='function') showNotification('Stato aggiornato', 'success'); loadAcnRequirements(); }
else if (typeof showNotification==='function') showNotification(res.message || 'Errore', 'error');
} catch (e) { if (typeof showNotification==='function') showNotification('Errore di connessione', 'error'); }
} }
// ── Continuous Control Monitoring (P1) ─────────────────────── // ── Continuous Control Monitoring (P1) ───────────────────────