[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:
co-authored by
Claude Opus 4.8
parent
33790427df
commit
7a23789b0f
@@ -360,6 +360,7 @@
|
||||
<button onclick="switchTab('audit', this)">Audit Log</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('acn', this)">Requisiti ACN</button>
|
||||
</div>
|
||||
|
||||
<!-- Tab: Report Compliance -->
|
||||
@@ -412,6 +413,15 @@
|
||||
</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>
|
||||
</main>
|
||||
</div>
|
||||
@@ -479,6 +489,61 @@
|
||||
if (tabId === 'audit') { auditPage = 1; loadAuditLogs(); }
|
||||
if (tabId === 'iso') loadIsoMapping();
|
||||
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) ───────────────────────
|
||||
|
||||
Reference in New Issue
Block a user