[FEAT] UI V3 — flotta: chrome top-nav + design system su 32 pagine app
Migrazione UI dal V2 (Bootstrap Italia, sidebar) al V3 (top-nav mega-menu navy/oro, Inter+Lucide self-host) fedele ai mockup docs/mockup Ui/. - 32 pagine app convertite (incl. admin/*): head Inter+v3.css, chrome topnav-v3, contenuti in card V3, dati REALI preservati (id/data-* intatti), i18n/help/FAB reali invariati, nessun CDN esterno. - v3.css: aggiunti widget condivisi (modal/toast/form/btn-variant/spinner/ progress/tag/badge) + utility (.btn base, .text-muted, .sr-only) + alias variabili V2->token V3, così modali/form/toast di common.js funzionano ovunque senza style.css. - maintenance.html: finestra estesa a ~12:15. - docs/UI_V3_FLEET_BRIEF.md: ricetta operativa della flotta. 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
b0fc190704
commit
a4150b5389
+112
-114
@@ -3,35 +3,35 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Misure e Requisiti - NIS2 Agile</title>
|
||||
<!-- Bootstrap Italia v2.18.1 self-hostato (regola: MAI CDN). Caricato PRIMA di style.css. -->
|
||||
<link rel="stylesheet" href="/vendor/bootstrap-italia/dist/css/bootstrap-italia.min.css">
|
||||
<link rel="stylesheet" href="/css/style.css?v=20260618">
|
||||
<title>Misure e Requisiti · NIS2 Agile</title>
|
||||
<!-- UI V3: Inter self-host + design system v3.css (NO Google Fonts / NO unpkg / NO Bootstrap Italia). -->
|
||||
<link rel="stylesheet" href="/vendor/inter/inter.css?v=20260627">
|
||||
<link rel="stylesheet" href="/css/v3.css?v=20260627">
|
||||
<style>
|
||||
.mr-intro { background:#eff6ff; border-left:4px solid var(--primary,#2563eb); padding:14px 18px; border-radius:8px; margin-bottom:14px; font-size:.92rem; line-height:1.6; }
|
||||
.mr-note { font-size:.82rem; color:#92400e; background:#fffbeb; border:1px solid #fde68a; border-radius:8px; padding:10px 12px; margin:0 0 16px; }
|
||||
.mr-ro { font-size:.8rem; color:#3730a3; background:#eef2ff; border:1px solid #c7d2fe; border-radius:8px; padding:8px 12px; margin:0 0 16px; display:flex; gap:8px; align-items:center; }
|
||||
.mr-stats { display:flex; flex-wrap:wrap; gap:10px; margin-bottom:16px; }
|
||||
.mr-stat { flex:1 1 140px; min-width:120px; background:#fff; border:1px solid var(--gray-200,#e5e7eb); border-radius:10px; padding:12px 14px; }
|
||||
.mr-stat .n { font-size:1.5rem; font-weight:800; line-height:1; }
|
||||
.mr-stat .l { font-size:.76rem; color:var(--gray-500,#6b7280); margin-top:4px; }
|
||||
.mr-toolbar { display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-bottom:14px; }
|
||||
.mr-toolbar label { font-weight:600; font-size:.82rem; }
|
||||
.mr-toolbar select, .mr-toolbar input[type=search] { padding:8px 10px; border:1px solid var(--gray-200,#e5e7eb); border-radius:8px; font-size:.88rem; }
|
||||
/* V3 — specifico pagina Misure/Requisiti (non coperto da v3.css) */
|
||||
.mr-intro { background:#EAF1FA; border-left:4px solid var(--c-blue); padding:14px 18px; border-radius:10px; margin-bottom:14px; font-size:13.5px; line-height:1.6; color:var(--ink); }
|
||||
.mr-note { font-size:12.5px; color:#92400e; background:#FBF1E2; border:1px solid #fde68a; border-radius:10px; padding:10px 12px; margin:0 0 16px; }
|
||||
.mr-ro { font-size:12.5px; color:#3730a3; background:#eef2ff; border:1px solid #c7d2fe; border-radius:10px; padding:8px 12px; margin:0 0 16px; display:flex; gap:8px; align-items:center; }
|
||||
.mr-stats { display:flex; flex-wrap:wrap; gap:12px; margin-bottom:16px; }
|
||||
.mr-stat { flex:1 1 140px; min-width:120px; background:#fff; border:1px solid var(--line); border-radius:11px; padding:14px 16px; box-shadow:var(--shadow); }
|
||||
.mr-stat .n { font-size:27px; font-weight:800; line-height:1; color:var(--c-blue); }
|
||||
.mr-stat .l { font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase; color:#7a8aa0; margin-top:6px; }
|
||||
.mr-toolbar { display:flex; gap:10px; flex-wrap:wrap; align-items:center; margin-bottom:14px; }
|
||||
.mr-toolbar label { font-weight:600; font-size:13px; color:var(--ink); }
|
||||
.mr-toolbar select, .mr-toolbar input[type=search] { padding:8px 11px; border:1px solid var(--line); border-radius:8px; font:inherit; font-size:13.5px; background:#fff; color:var(--ink); }
|
||||
.mr-toolbar .grow { flex:1 1 auto; }
|
||||
.mr-group { border:1px solid var(--gray-200,#e5e7eb); border-radius:10px; margin-bottom:14px; overflow:hidden; }
|
||||
.mr-group-head { background:#f8fafc; padding:11px 14px; border-bottom:1px solid var(--gray-100,#f3f4f6); }
|
||||
.mr-group-head .code { font-weight:800; color:#1e3a8a; font-size:.95rem; }
|
||||
.mr-group-head .area { font-size:.78rem; color:var(--gray-500,#6b7280); }
|
||||
.mr-group-head .descr { font-size:.86rem; color:#334155; margin-top:3px; line-height:1.5; }
|
||||
.mr-tag { display:inline-block; font-size:.66rem; font-weight:800; padding:2px 8px; border-radius:20px; margin-left:6px; vertical-align:middle; }
|
||||
.mr-tag.ess { background:#fef3c7; color:#92400e; }
|
||||
.mr-table { width:100%; border-collapse:collapse; font-size:.85rem; }
|
||||
.mr-table th, .mr-table td { text-align:left; padding:9px 11px; border-bottom:1px solid var(--gray-100,#f3f4f6); vertical-align:top; }
|
||||
.mr-table th { font-size:.72rem; text-transform:uppercase; letter-spacing:.04em; color:var(--gray-500,#6b7280); background:#fff; position:sticky; top:0; }
|
||||
.mr-group { border:1px solid var(--line); border-radius:11px; margin-bottom:14px; overflow:hidden; }
|
||||
.mr-group-head { background:#FAFCFE; padding:12px 16px; border-bottom:1px solid var(--line); }
|
||||
.mr-group-head .code { font-weight:800; color:#1e3a8a; font-size:14px; }
|
||||
.mr-group-head .area { font-size:12px; color:var(--muted); }
|
||||
.mr-group-head .descr { font-size:13px; color:#334155; margin-top:3px; line-height:1.5; }
|
||||
.mr-tag { display:inline-block; font-size:10px; font-weight:800; padding:2px 8px; border-radius:20px; margin-left:6px; vertical-align:middle; }
|
||||
.mr-tag.ess { background:#FBF1E2; color:#92400e; }
|
||||
.mr-table { width:100%; border-collapse:collapse; font-size:13px; }
|
||||
.mr-table th, .mr-table td { text-align:left; padding:9px 12px; border-bottom:1px solid var(--line); vertical-align:top; }
|
||||
.mr-table th { font-size:10.5px; text-transform:uppercase; letter-spacing:.04em; color:#7a8aa0; background:#fff; position:sticky; top:0; }
|
||||
.mr-req-code { white-space:nowrap; font-weight:700; color:#475569; }
|
||||
.mr-code-pill { display:inline-block; font-size:.7rem; font-weight:700; color:#3730a3; background:#eef2ff; border-radius:6px; padding:1px 6px; }
|
||||
/* Ticket #384 p.3 — codici Policy come link attivi verso il menu POLICY */
|
||||
.mr-code-pill { display:inline-block; font-size:11px; font-weight:700; color:#3730a3; background:#eef2ff; border-radius:6px; padding:1px 6px; }
|
||||
a.mr-code-link { text-decoration:none; cursor:pointer; margin-right:3px; }
|
||||
a.mr-code-link:hover { background:#e0e7ff; text-decoration:underline; }
|
||||
a.mr-code-link:focus-visible { outline:2px solid #3730a3; outline-offset:1px; }
|
||||
@@ -41,31 +41,32 @@
|
||||
.mr-yn.n { color:#cbd5e1; }
|
||||
.mr-row.spento { opacity:.4; }
|
||||
.mr-row.spento td { font-style:italic; }
|
||||
.mr-empty { text-align:center; padding:34px 16px; color:var(--gray-500,#6b7280); }
|
||||
.sv-badge { display:inline-block; font-size:.72rem; font-weight:800; padding:3px 10px; border-radius:20px; border:none; cursor:pointer; }
|
||||
.sv-badge.sv-todo { background:#f1f5f9; color:#475569; }
|
||||
.mr-empty { text-align:center; padding:34px 16px; color:var(--muted); }
|
||||
.savehint { font-size:12.5px; color:var(--muted); min-height:16px; }
|
||||
.sv-badge { display:inline-block; font-size:11px; font-weight:800; padding:3px 10px; border-radius:20px; border:none; cursor:pointer; }
|
||||
.sv-badge.sv-todo { background:#EEF2F7; color:#475569; }
|
||||
.sv-badge.sv-na { background:#e5e7eb; color:#6b7280; }
|
||||
.sv-badge.sv-ko { background:#fee2e2; color:#991b1b; }
|
||||
.sv-badge.sv-mid { background:#fef3c7; color:#92400e; }
|
||||
.sv-badge.sv-ok { background:#dcfce7; color:#166534; }
|
||||
.sv-val { font-size:.76rem; color:#64748b; margin-top:4px; max-width:300px; }
|
||||
/* Modale valutazione */
|
||||
.rm-overlay { display:none; position:fixed; inset:0; background:rgba(15,23,42,.5); z-index:1050; align-items:flex-start; justify-content:center; overflow:auto; padding:40px 16px; }
|
||||
.sv-badge.sv-ko { background:#FBE9E9; color:#991b1b; }
|
||||
.sv-badge.sv-mid { background:#FBF1E2; color:#92400e; }
|
||||
.sv-badge.sv-ok { background:#E6F4EC; color:#166534; }
|
||||
.sv-val { font-size:11.5px; color:#64748b; margin-top:4px; max-width:300px; }
|
||||
/* Modale valutazione (page-owned) */
|
||||
.rm-overlay { display:none; position:fixed; inset:0; background:rgba(15,27,46,.5); z-index:1050; align-items:flex-start; justify-content:center; overflow:auto; padding:40px 16px; }
|
||||
.rm-overlay.open { display:flex; }
|
||||
.rm-dialog { background:#fff; border-radius:12px; width:100%; max-width:600px; box-shadow:0 20px 50px rgba(0,0,0,.25); }
|
||||
.rm-head { display:flex; justify-content:space-between; align-items:flex-start; padding:16px 20px; border-bottom:1px solid var(--gray-100,#f3f4f6); }
|
||||
.rm-head h3 { margin:0; font-size:1.02rem; }
|
||||
.rm-head .sub { font-size:.8rem; color:var(--gray-500,#6b7280); margin-top:4px; }
|
||||
.rm-dialog { background:#fff; border-radius:12px; width:100%; max-width:600px; box-shadow:0 20px 50px rgba(11,40,80,.25); }
|
||||
.rm-head { display:flex; justify-content:space-between; align-items:flex-start; padding:16px 20px; border-bottom:1px solid var(--line); }
|
||||
.rm-head h3 { margin:0; font-size:16px; }
|
||||
.rm-head .sub { font-size:12.5px; color:var(--muted); margin-top:4px; }
|
||||
.rm-body { padding:18px 20px; }
|
||||
.rm-foot { padding:14px 20px; border-top:1px solid var(--gray-100,#f3f4f6); display:flex; justify-content:flex-end; gap:10px; }
|
||||
.rm-foot { padding:14px 20px; border-top:1px solid var(--line); display:flex; justify-content:flex-end; gap:10px; }
|
||||
.rm-field { margin-bottom:14px; }
|
||||
.rm-field label { display:block; font-weight:600; font-size:.86rem; margin-bottom:4px; }
|
||||
.rm-field select, .rm-field textarea { width:100%; padding:9px 10px; border:1px solid var(--gray-200,#e5e7eb); border-radius:8px; font-size:.9rem; }
|
||||
.rm-field label { display:block; font-weight:600; font-size:13px; margin-bottom:4px; }
|
||||
.rm-field select, .rm-field textarea { width:100%; padding:9px 10px; border:1px solid var(--line); border-radius:8px; font:inherit; font-size:14px; }
|
||||
.rm-field textarea { min-height:80px; resize:vertical; }
|
||||
.rm-ctx { background:#f8fafc; border:1px solid var(--gray-100,#f3f4f6); border-radius:8px; padding:10px 12px; font-size:.83rem; margin-bottom:14px; line-height:1.5; }
|
||||
.rm-ctx .pill { display:inline-block; font-size:.7rem; font-weight:700; color:#3730a3; background:#eef2ff; border-radius:6px; padding:1px 6px; margin-right:4px; }
|
||||
.rm-err { color:#b91c1c; font-size:.82rem; min-height:18px; }
|
||||
.rm-close { background:none; border:none; font-size:1.4rem; line-height:1; cursor:pointer; color:var(--gray-400,#9ca3af); }
|
||||
.rm-ctx { background:#FAFCFE; border:1px solid var(--line); border-radius:8px; padding:10px 12px; font-size:13px; margin-bottom:14px; line-height:1.5; }
|
||||
.rm-ctx .pill { display:inline-block; font-size:11px; font-weight:700; color:#3730a3; background:#eef2ff; border-radius:6px; padding:1px 6px; margin-right:4px; }
|
||||
.rm-err { color:#b91c1c; font-size:12.5px; min-height:18px; }
|
||||
.rm-close { background:none; border:none; font-size:1.4rem; line-height:1; cursor:pointer; color:var(--muted); }
|
||||
</style>
|
||||
<!-- PWA:start -->
|
||||
<link rel="manifest" href="/manifest.webmanifest">
|
||||
@@ -82,56 +83,62 @@
|
||||
<!-- PWA:end -->
|
||||
</head>
|
||||
<body>
|
||||
<div class="app-layout">
|
||||
<aside class="sidebar" id="sidebar"></aside>
|
||||
<main class="main-content">
|
||||
<header class="content-header">
|
||||
<h2 data-i18n="mr.title">Misure e Requisiti</h2>
|
||||
<div class="content-header-actions">
|
||||
<span class="savehint" id="mr-hint" role="status" aria-live="polite"></span>
|
||||
</div>
|
||||
</header>
|
||||
<!-- Chrome V3 (appbar + mega-menu) iniettata da topnav-v3.js -->
|
||||
<div id="topnav"></div>
|
||||
|
||||
<div class="content-body">
|
||||
<div class="mr-intro">
|
||||
<strong>L'elenco canonico delle misure di sicurezza di base e dei relativi requisiti.</strong>
|
||||
Ogni requisito riporta la <strong>policy di default</strong> che lo soddisfa e il <strong>rischio di default</strong> associato. È la base su cui si costruiscono policy, rischi e attività: le righe si "spengono" quando non sono applicabili alla classe del tuo soggetto.
|
||||
</div>
|
||||
<div class="mr-ro">
|
||||
🔒 <span>Elenco di riferimento, <strong>sola lettura</strong>: la codifica (misure, requisiti, ordine) non è modificabile. Policy e rischi qui elencati vengono proposti come <em>default</em> nei rispettivi moduli, dove potrai personalizzarli.</span>
|
||||
</div>
|
||||
<div class="mr-note">
|
||||
Fonte: <strong>Determinazione ACN n. 164179 del 14 aprile 2025</strong> (misure di base, Allegati 1–2). Le misure applicabili dipendono dalla classe del soggetto: <strong>importanti</strong> 37 misure / 92 requisiti, <strong>essenziali</strong> 43 / 116. Strumento di supporto, non un parere legale.
|
||||
</div>
|
||||
|
||||
<div class="mr-stats" id="mr-stats" style="display:none;">
|
||||
<div class="mr-stat"><div class="n" id="mr-stat-mis">0</div><div class="l">Misure</div></div>
|
||||
<div class="mr-stat"><div class="n" id="mr-stat-req">0</div><div class="l">Requisiti</div></div>
|
||||
<div class="mr-stat"><div class="n" id="mr-stat-active">0</div><div class="l">Applicabili al soggetto</div></div>
|
||||
</div>
|
||||
|
||||
<div class="mr-toolbar">
|
||||
<label for="mr-view">Vista:</label>
|
||||
<select id="mr-view" onchange="mrRender()">
|
||||
<option value="misura">Per misura</option>
|
||||
<option value="area">Per area politica</option>
|
||||
</select>
|
||||
<label for="mr-class">Soggetto:</label>
|
||||
<select id="mr-class" onchange="mrRender()">
|
||||
<option value="auto">Automatico (dalla mia org)</option>
|
||||
<option value="important">Importante</option>
|
||||
<option value="essential">Essenziale</option>
|
||||
<option value="all">Tutti (mostra tutto)</option>
|
||||
</select>
|
||||
<label style="font-weight:500;"><input type="checkbox" id="mr-hide" onchange="mrRender()"> nascondi non applicabili</label>
|
||||
<span class="grow"></span>
|
||||
<input type="search" id="mr-q" placeholder="Cerca codice o testo…" oninput="mrRender()" style="min-width:200px;">
|
||||
</div>
|
||||
|
||||
<div id="mr-list" aria-live="polite"><div class="mr-empty">Caricamento…</div></div>
|
||||
<main class="wrap">
|
||||
<nav class="crumb" aria-label="breadcrumb">
|
||||
<i data-lucide="home" class="ic"></i><a href="dashboard.html">Home</a>
|
||||
<i data-lucide="chevron-right" class="ic"></i><span>Lex e ISO</span>
|
||||
<i data-lucide="chevron-right" class="ic"></i><span>Misure e requisiti</span>
|
||||
</nav>
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1 data-i18n="mr.title">Misure e Requisiti</h1>
|
||||
<p class="sub">Elenco canonico delle misure di base ACN e dei relativi requisiti</p>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<span class="savehint" id="mr-hint" role="status" aria-live="polite"></span>
|
||||
</div>
|
||||
|
||||
<section class="card card-pad">
|
||||
<div class="mr-intro">
|
||||
<strong>L'elenco canonico delle misure di sicurezza di base e dei relativi requisiti.</strong>
|
||||
Ogni requisito riporta la <strong>policy di default</strong> che lo soddisfa e il <strong>rischio di default</strong> associato. È la base su cui si costruiscono policy, rischi e attività: le righe si "spengono" quando non sono applicabili alla classe del tuo soggetto.
|
||||
</div>
|
||||
<div class="mr-ro">
|
||||
🔒 <span>Elenco di riferimento, <strong>sola lettura</strong>: la codifica (misure, requisiti, ordine) non è modificabile. Policy e rischi qui elencati vengono proposti come <em>default</em> nei rispettivi moduli, dove potrai personalizzarli.</span>
|
||||
</div>
|
||||
<div class="mr-note">
|
||||
Fonte: <strong>Determinazione ACN n. 164179 del 14 aprile 2025</strong> (misure di base, Allegati 1–2). Le misure applicabili dipendono dalla classe del soggetto: <strong>importanti</strong> 37 misure / 92 requisiti, <strong>essenziali</strong> 43 / 116. Strumento di supporto, non un parere legale.
|
||||
</div>
|
||||
|
||||
<div class="mr-stats" id="mr-stats" style="display:none;">
|
||||
<div class="mr-stat"><div class="n" id="mr-stat-mis">0</div><div class="l">Misure</div></div>
|
||||
<div class="mr-stat"><div class="n" id="mr-stat-req">0</div><div class="l">Requisiti</div></div>
|
||||
<div class="mr-stat"><div class="n" id="mr-stat-active">0</div><div class="l">Applicabili al soggetto</div></div>
|
||||
</div>
|
||||
|
||||
<div class="mr-toolbar">
|
||||
<label for="mr-view">Vista:</label>
|
||||
<select id="mr-view" onchange="mrRender()">
|
||||
<option value="misura">Per misura</option>
|
||||
<option value="area">Per area politica</option>
|
||||
</select>
|
||||
<label for="mr-class">Soggetto:</label>
|
||||
<select id="mr-class" onchange="mrRender()">
|
||||
<option value="auto">Automatico (dalla mia org)</option>
|
||||
<option value="important">Importante</option>
|
||||
<option value="essential">Essenziale</option>
|
||||
<option value="all">Tutti (mostra tutto)</option>
|
||||
</select>
|
||||
<label style="font-weight:500;"><input type="checkbox" id="mr-hide" onchange="mrRender()"> nascondi non applicabili</label>
|
||||
<span class="grow"></span>
|
||||
<input type="search" id="mr-q" placeholder="Cerca codice o testo…" oninput="mrRender()" style="min-width:200px;">
|
||||
</div>
|
||||
|
||||
<div id="mr-list" aria-live="polite"><div class="mr-empty">Caricamento…</div></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Modale valutazione del requisito (org-scoped) -->
|
||||
<div class="rm-overlay" id="mr-modal" role="dialog" aria-modal="true" aria-labelledby="mr-modal-title">
|
||||
@@ -161,16 +168,17 @@
|
||||
<div class="rm-err" id="mr-eval-err" role="alert"></div>
|
||||
</div>
|
||||
<div class="rm-foot">
|
||||
<button class="btn btn-outline" type="button" onclick="mrCloseEval()">Annulla</button>
|
||||
<button class="btn btn-primary" type="button" id="mr-eval-save" onclick="mrSaveEval()">Salva</button>
|
||||
<button class="btn-ghost" type="button" onclick="mrCloseEval()">Annulla</button>
|
||||
<button class="btn-primary" type="button" id="mr-eval-save" onclick="mrSaveEval()">Salva</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Scripts V3 (ordine ESATTO come dashboard.html) -->
|
||||
<script src="/js/api.js?v=20260625d"></script>
|
||||
<script src="/js/common.js?v=20260624g"></script>
|
||||
<script src="/vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
|
||||
<script src="/js/common-bi.js?v=20260625d"></script>
|
||||
<script src="/js/common.js?v=20260627v3"></script>
|
||||
<script src="/vendor/lucide/lucide.min.js"></script>
|
||||
<script src="/js/topnav-v3.js?v=20260627"></script>
|
||||
<script src="/js/i18n.js?v=20260631"></script>
|
||||
<script src="/js/help.js?v=20260625d"></script>
|
||||
<script>
|
||||
@@ -185,12 +193,9 @@
|
||||
function el(id) { return document.getElementById(id); }
|
||||
function esc(s) { const d = document.createElement('div'); d.textContent = (s == null ? '' : String(s)); return d.innerHTML; }
|
||||
// Ticket #384 p.2.1 — i codici procedura/policy si presentano con prefisso "Policy." (Proc.NN -> Policy.NN).
|
||||
// Il codice canonico nel catalogo resta proc_code (chiave di join requisiti<->procedure): qui si rietichetta solo per la UI.
|
||||
function policyCode(code) { return code ? String(code).replace(/^Proc\./i, 'Policy.') : code; }
|
||||
|
||||
// Ticket #384 p.2.2/p.3 — colonna "Policy (default)" multi-valore con link attivi al menu POLICY.
|
||||
// Usa le policy d'organizzazione collegate (req.policies dal backend); in assenza degrada
|
||||
// al codice di default del catalogo (proc_code), com'era prima.
|
||||
function renderPolicyLinks(req) {
|
||||
const pols = req.policies || [];
|
||||
if (pols.length) {
|
||||
@@ -220,6 +225,7 @@
|
||||
if (window.I18n && I18n.init) I18n.init('it');
|
||||
if (typeof loadSidebar === 'function') loadSidebar();
|
||||
if (window.HelpSystem && HelpSystem.init) HelpSystem.init();
|
||||
if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} }
|
||||
await mrLoad();
|
||||
});
|
||||
|
||||
@@ -230,17 +236,14 @@
|
||||
MR.org_class = (data && data.org_class) || null;
|
||||
MR.has_org = !!(data && data.has_org);
|
||||
MR.stati = (data && data.stati) || Object.keys(STATO_META);
|
||||
// indicizza i requisiti per id (per la modale)
|
||||
MR.byId = {};
|
||||
MR.measures.forEach(function (m) { (m.requisiti || []).forEach(function (q) { MR.byId[q.id] = q; }); });
|
||||
// se conosco la classe dell'org, parto in modalità "auto"; altrimenti mostro tutto
|
||||
if (!MR.org_class) el('mr-class').value = 'all';
|
||||
el('mr-stat-mis').textContent = (data && data.totals && data.totals.misure) || MR.measures.length;
|
||||
el('mr-stat-req').textContent = (data && data.totals && data.totals.requisiti) || 0;
|
||||
el('mr-stats').style.display = 'flex';
|
||||
// Ticket #384 p.3 — deep-link dal menu POLICY: ?requisito=ID → mostra ed evidenzia il requisito.
|
||||
const focusReq = parseInt(new URLSearchParams(location.search).get('requisito') || '', 10);
|
||||
if (focusReq) el('mr-class').value = 'all'; // evita che la riga sia filtrata via
|
||||
if (focusReq) el('mr-class').value = 'all';
|
||||
mrRender();
|
||||
if (focusReq) mrFocusRequisito(focusReq);
|
||||
} catch (e) {
|
||||
@@ -248,7 +251,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Ticket #384 p.3 — scorre alla riga del requisito e la evidenzia (arrivo da link Policy).
|
||||
function mrFocusRequisito(reqId) {
|
||||
const row = document.getElementById('req-' + reqId);
|
||||
if (!row) return;
|
||||
@@ -257,14 +259,12 @@
|
||||
setTimeout(function () { row.classList.remove('mr-row-focus'); }, 4000);
|
||||
}
|
||||
|
||||
// classe effettiva selezionata (risolve "auto" sulla classe dell'org)
|
||||
function mrEffectiveClass() {
|
||||
const sel = el('mr-class').value;
|
||||
if (sel === 'auto') return MR.org_class; // può essere null => tutto attivo
|
||||
if (sel === 'auto') return MR.org_class;
|
||||
if (sel === 'all') return null;
|
||||
return sel; // important | essential
|
||||
return sel;
|
||||
}
|
||||
// un requisito è "attivo" per la classe effettiva?
|
||||
function reqActive(req, cls) {
|
||||
if (!cls) return true;
|
||||
return cls === 'important' ? !!req.applies_important : !!req.applies_essential;
|
||||
@@ -283,9 +283,8 @@
|
||||
const q = (el('mr-q').value || '').trim();
|
||||
const view = el('mr-view').value;
|
||||
|
||||
// costruisco le righe filtrate (misura -> requisiti visibili)
|
||||
let activeCount = 0;
|
||||
const groupsByKey = {}; // key -> {label, descr, area, code, essOnly, rows:[]}
|
||||
const groupsByKey = {};
|
||||
|
||||
MR.measures.forEach(function (m) {
|
||||
const essOnly = !m.applies_important;
|
||||
@@ -398,7 +397,6 @@
|
||||
(function () {
|
||||
const ov = el('mr-modal'); if (!ov) return;
|
||||
ov.addEventListener('click', function (e) { if (e.target === ov) mrCloseEval(); });
|
||||
// focus-trap: Tab/Shift+Tab restano dentro la modale aperta
|
||||
ov.addEventListener('keydown', function (e) {
|
||||
if (e.key !== 'Tab' || !ov.classList.contains('open')) return;
|
||||
const nodes = ov.querySelectorAll('select, textarea, button');
|
||||
|
||||
Reference in New Issue
Block a user