[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
+80
-53
@@ -3,50 +3,50 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||||
<title>Organigramma - 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>Organigramma · NIS2 Agile</title>
|
||||
<!-- UI V3: Inter self-host + design system v3.css. No external CDN, no Bootstrap Italia. -->
|
||||
<link rel="stylesheet" href="/vendor/inter/inter.css?v=20260627">
|
||||
<link rel="stylesheet" href="/css/v3.css?v=20260627">
|
||||
<style>
|
||||
.org-intro { background:#eff6ff; border-left:4px solid var(--primary,#2563eb); padding:14px 18px; border-radius:8px; margin-bottom:18px; font-size:.92rem; line-height:1.6; }
|
||||
.org-gov-note { font-size:.82rem; color:#92400e; background:#fffbeb; border:1px solid #fde68a; border-radius:8px; padding:10px 12px; margin:12px 0 18px; }
|
||||
.org-stats { display:flex; flex-wrap:wrap; gap:10px; margin-bottom:18px; }
|
||||
.org-stat { flex:1 1 150px; min-width:130px; background:#fff; border:1px solid var(--gray-200,#e5e7eb); border-radius:10px; padding:12px 14px; }
|
||||
.org-stat .n { font-size:1.5rem; font-weight:800; color:var(--primary,#2563eb); line-height:1; }
|
||||
.org-stat .l { font-size:.78rem; color:var(--gray-500,#6b7280); margin-top:4px; }
|
||||
/* V3+ page-specific (componenti non coperti da v3.css): albero + modale ruolo. Token V3. */
|
||||
.org-intro { background:#EFF4FB; 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); }
|
||||
.org-gov-note { font-size:12.5px; color:#9A6418; background:#FBF1E2; border:1px solid #f3d9a8; border-radius:10px; padding:10px 12px; margin:0 0 18px; }
|
||||
.org-stats { display:flex; flex-wrap:wrap; gap:12px; margin-bottom:18px; }
|
||||
.org-stat { flex:1 1 150px; min-width:130px; background:#fff; border:1px solid var(--line); border-radius:11px; padding:13px 15px; }
|
||||
.org-stat .n { font-size:26px; font-weight:800; color:var(--c-blue); line-height:1; }
|
||||
.org-stat .l { font-size:11.5px; color:var(--muted); margin-top:5px; }
|
||||
/* Albero */
|
||||
.org-tree { margin-top:6px; }
|
||||
.org-children { margin-left:26px; border-left:2px solid var(--gray-100,#eef0f3); padding-left:16px; }
|
||||
.org-children { margin-left:26px; border-left:2px solid var(--line); padding-left:16px; }
|
||||
.org-node { margin:8px 0; }
|
||||
.org-card { display:flex; align-items:flex-start; gap:12px; background:#fff; border:1px solid var(--gray-200,#e5e7eb); border-radius:10px; padding:12px 14px; }
|
||||
.org-card.gov { border-color:#b45309; background:#fffdf7; }
|
||||
.org-card { display:flex; align-items:flex-start; gap:12px; background:#fff; border:1px solid var(--line); border-radius:11px; padding:13px 15px; }
|
||||
.org-card.gov { border-color:#e0b25a; background:#FFFDF7; }
|
||||
.org-card-main { flex:1 1 auto; min-width:0; }
|
||||
.org-role-name { font-weight:700; font-size:.98rem; }
|
||||
.org-holder { font-size:.85rem; color:var(--gray-600,#4b5563); margin-top:2px; }
|
||||
.org-holder.vacant { color:#9ca3af; font-style:italic; }
|
||||
.org-desc { font-size:.82rem; color:var(--gray-500,#6b7280); margin-top:6px; line-height:1.5; }
|
||||
.org-badge { display:inline-block; font-size:.68rem; font-weight:700; padding:2px 8px; border-radius:6px; margin-left:6px; vertical-align:middle; }
|
||||
.org-badge.gov { background:#fef3c7; color:#92400e; }
|
||||
.org-role-name { font-weight:700; font-size:14px; }
|
||||
.org-holder { font-size:12.5px; color:var(--muted); margin-top:3px; }
|
||||
.org-holder.vacant { color:#9aa8ba; font-style:italic; }
|
||||
.org-desc { font-size:12.5px; color:var(--muted); margin-top:6px; line-height:1.5; }
|
||||
.org-badge { display:inline-block; font-size:10px; font-weight:700; padding:2px 8px; border-radius:6px; margin-left:6px; vertical-align:middle; }
|
||||
.org-badge.gov { background:#FBF1E2; color:#9A6418; }
|
||||
.org-actions { display:flex; gap:6px; flex-wrap:wrap; flex-shrink:0; }
|
||||
.org-actions .btn { padding:4px 8px; font-size:.74rem; }
|
||||
.org-empty { text-align:center; padding:48px 16px; color:var(--gray-500,#6b7280); }
|
||||
.org-empty .big { font-size:1.05rem; font-weight:700; color:var(--gray-700,#374151); margin-bottom:6px; }
|
||||
.org-empty { text-align:center; padding:48px 16px; color:var(--muted); }
|
||||
.org-empty .big { font-size:16px; font-weight:700; color:var(--ink); margin-bottom:6px; }
|
||||
/* Modale */
|
||||
.om-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; }
|
||||
.om-overlay { display:none; position:fixed; inset:0; background:rgba(15,38,60,.5); z-index:1050; align-items:flex-start; justify-content:center; overflow:auto; padding:40px 16px; }
|
||||
.om-overlay.open { display:flex; }
|
||||
.om-dialog { background:#fff; border-radius:12px; width:100%; max-width:560px; box-shadow:0 20px 50px rgba(0,0,0,.25); }
|
||||
.om-head { display:flex; justify-content:space-between; align-items:center; padding:16px 20px; border-bottom:1px solid var(--gray-100,#f3f4f6); }
|
||||
.om-head h3 { margin:0; font-size:1.05rem; }
|
||||
.om-dialog { background:#fff; border-radius:14px; width:100%; max-width:560px; box-shadow:0 20px 50px rgba(11,40,80,.25); }
|
||||
.om-head { display:flex; justify-content:space-between; align-items:center; padding:16px 20px; border-bottom:1px solid var(--line); }
|
||||
.om-head h3 { margin:0; font-size:17px; font-weight:800; }
|
||||
.om-body { padding:18px 20px; }
|
||||
.om-foot { padding:14px 20px; border-top:1px solid var(--gray-100,#f3f4f6); display:flex; justify-content:flex-end; gap:10px; }
|
||||
.om-foot { padding:14px 20px; border-top:1px solid var(--line); display:flex; justify-content:flex-end; gap:10px; }
|
||||
.om-field { margin-bottom:14px; }
|
||||
.om-field label { display:block; font-weight:600; font-size:.86rem; margin-bottom:4px; }
|
||||
.om-field input[type=text], .om-field select, .om-field textarea { width:100%; padding:9px 10px; border:1px solid var(--gray-200,#e5e7eb); border-radius:8px; font-size:.9rem; }
|
||||
.om-field label { display:block; font-weight:600; font-size:12.5px; margin-bottom:5px; }
|
||||
.om-field input[type=text], .om-field select, .om-field textarea { width:100%; padding:9px 11px; border:1px solid var(--line); border-radius:8px; font:inherit; font-size:13.5px; background:#fff; color:var(--ink); }
|
||||
.om-field textarea { min-height:70px; resize:vertical; }
|
||||
.om-check { display:flex; align-items:flex-start; gap:8px; background:#fffbeb; border:1px solid #fde68a; border-radius:8px; padding:10px 12px; font-size:.85rem; }
|
||||
.om-check { display:flex; align-items:flex-start; gap:8px; background:#FBF1E2; border:1px solid #f3d9a8; border-radius:8px; padding:10px 12px; font-size:12.5px; color:var(--ink); }
|
||||
.om-check input { margin-top:3px; }
|
||||
.om-err { color:#b91c1c; font-size:.82rem; min-height:18px; margin-top:2px; }
|
||||
.om-close { background:none; border:none; font-size:1.4rem; line-height:1; cursor:pointer; color:var(--gray-400,#9ca3af); }
|
||||
.om-err { color:var(--c-red); font-size:12.5px; min-height:18px; margin-top:2px; }
|
||||
.om-close { background:none; border:none; font-size:22px; line-height:1; cursor:pointer; color:#9aa8ba; }
|
||||
</style>
|
||||
<!-- PWA:start -->
|
||||
<link rel="manifest" href="/manifest.webmanifest">
|
||||
@@ -63,17 +63,27 @@
|
||||
<!-- 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="org.title">Organigramma</h2>
|
||||
<div class="content-header-actions">
|
||||
<button class="btn btn-primary btn-sm" id="org-btn-new" onclick="orgOpenModal()" data-i18n="org.new_role">Nuovo ruolo</button>
|
||||
</div>
|
||||
</header>
|
||||
<!-- Chrome V3 (appbar + mega-menu) iniettata da topnav-v3.js in questo host -->
|
||||
<div id="topnav"></div>
|
||||
|
||||
<div class="content-body">
|
||||
<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>Struttura interna</span>
|
||||
<i data-lucide="chevron-right" class="ic"></i><span data-i18n="org.title">Organigramma</span>
|
||||
</nav>
|
||||
<div class="page-head">
|
||||
<div>
|
||||
<h1 data-i18n="org.title">Organigramma</h1>
|
||||
<p class="sub">Ruoli, gerarchia e titolari — base del modello relazionale NIS2</p>
|
||||
</div>
|
||||
<button class="btn-primary" id="org-btn-new" onclick="orgOpenModal()">
|
||||
<i data-lucide="plus" class="ic"></i><span data-i18n="org.new_role">Nuovo ruolo</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<section class="card card-pad">
|
||||
<div class="org-intro">
|
||||
<strong>Chi è responsabile di cosa.</strong>
|
||||
L'organigramma mappa i ruoli dell'organizzazione, la loro gerarchia e il titolare di ciascuno. È la base del modello relazionale NIS2: nelle fasi successive i ruoli si collegheranno a competenze, procedure, inventario e rischi.
|
||||
@@ -93,12 +103,12 @@
|
||||
<div id="org-empty" class="org-empty" style="display:none;">
|
||||
<div class="big">Nessun ruolo definito</div>
|
||||
<div style="margin-bottom:16px;">Inizia a costruire l'organigramma della tua organizzazione.</div>
|
||||
<button class="btn btn-primary" onclick="orgOpenModal()">Crea il primo ruolo</button>
|
||||
<button class="btn btn-outline" onclick="orgScaffold()" style="margin-left:8px;">Crea struttura di base</button>
|
||||
<button class="btn-primary" onclick="orgOpenModal()"><i data-lucide="plus" class="ic"></i>Crea il primo ruolo</button>
|
||||
<button class="btn-ghost" onclick="orgScaffold()" style="margin-left:8px;"><i data-lucide="network" class="ic"></i>Crea struttura di base</button>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Modale crea/modifica ruolo -->
|
||||
<div class="om-overlay" id="org-modal" role="dialog" aria-modal="true" aria-labelledby="org-modal-title">
|
||||
@@ -132,18 +142,35 @@
|
||||
<div class="om-err" id="org-f-err" role="alert"></div>
|
||||
</div>
|
||||
<div class="om-foot">
|
||||
<button class="btn btn-outline" type="button" onclick="orgCloseModal()">Annulla</button>
|
||||
<button class="btn btn-primary" type="button" id="org-f-save" onclick="orgSaveRole()">Salva</button>
|
||||
<button class="btn-ghost" type="button" onclick="orgCloseModal()">Annulla</button>
|
||||
<button class="btn-primary" type="button" id="org-f-save" onclick="orgSaveRole()">Salva</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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=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 src="/js/organigramma.js?v=20260617"></script>
|
||||
<script src="/js/organigramma.js?v=20260627"></script>
|
||||
<script>
|
||||
// Render icone Lucide (chrome + crumb + bottoni). organigramma.js fa render albero
|
||||
// dopo il fetch dati: re-render icone su mutazioni del DOM dell'app.
|
||||
function v3Icons() { if (window.lucide && lucide.createIcons) { try { lucide.createIcons(); } catch (e) {} } }
|
||||
v3Icons();
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
v3Icons();
|
||||
var root = document.querySelector('main.wrap');
|
||||
if (root && window.MutationObserver) {
|
||||
var mo = new MutationObserver(function () { v3Icons(); });
|
||||
mo.observe(root, { childList: true, subtree: true });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user