Files
DevEnv nis2-agileandClaude Opus 4.8 4dfab2a391 [FORMAZIONE] Avatar di prodotto — mappa-logica COMPLETA (22 viste) + SELETTORI_DEMO + tassonomia corso + fix kb.js
Deliverable formazione-first (DESIGN_AVATAR_FORMAZIONE / product-demo-complete):
- docs/mappa-logica/01..22_*.md: tutte le viste app V2 mappate (formato canonico standard §2),
  selettori VERIFICATI sul DOM reale, endpoint verificati su api.js/script, gate ruolo dai controller
- docs/mappa-logica/SELETTORI_DEMO.md: registry canonico (22 viste, 131 visualizzazione + 89 azioni)
  + worklist consolidata 132 id stabili da aggiungere (modifiche chirurgiche future)
- docs/formazione/TASSONOMIA_CORSO_NIS2.{md,json}: 10 moduli / 28 lezioni / ~7h45, prerequisiti+durate,
  esame con soglia 80%, sandbox 'prova tu' org 996002
- fix kb.js: ingest scope=ORG ora invia organization_id (prima 422 KB_ORG_REQUIRED) — bug trovato da A2

Prodotto con 4 trittici di agenti (A1 esecutore -> A2 revisore -> A3 controllore avversariale):
A2: ~14 correzioni (cross-ref, gate ruolo, comportamenti); A3 PASS: 38 selettori campionati 0 rotti,
18 endpoint reali, 0 violazioni normative (Allegati 3/4, IS-4, '1 mese', PII.* interna), 9 claim verificati.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 18:50:19 +02:00

548 lines
37 KiB
Markdown

# SELETTORI_DEMO — Registry canonico NIS2 (campo → selettore CSS)
> Fonte di verità per: manifest tour, demo-selectors.js (whitelist SPA), RAG persona.
> Regola 3-way: DOM ↔ demo-selectors.js ↔ questo file devono restare allineati.
>
> Compilato dal gate A3 (2026-06-12) a partire dalle 22 mappe-logiche (sezioni Confine + Azioni).
> Tutti i selettori sono stati verificati contro gli HTML/JS reali in `public/` (campionamento avversariale ~38 selettori + 18 endpoint: 0 rotti).
> Convenzioni note: `(runtime)` = elemento generato via JS, esiste solo dopo l'apertura di modale/vista; `{id}`/`{tipo}` = porzione dinamica.
## dashboard (/dashboard.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Data corrente header | `#header-date` | Valorizzata da JS (`toLocaleDateString('it-IT')`) |
| Gauge avanzamento misure Art.21 | `#compliance-gauge` | SVG renderizzato via JS |
| Griglia 4 KPI | `#stats-grid` | |
| KPI Rischi Aperti | `#stat-risks` | |
| KPI Incidenti Attivi | `#stat-incidents` | |
| KPI Policy Approvate | `#stat-policies` | |
| KPI Formazione % | `#stat-training` | |
| Lista Prossime Scadenze (max 5) | `#deadlines-list` | Badge Urgente ≤3gg, Prossima ≤7gg |
| Lista Attività Recenti (max 8) | `#activity-list` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Azioni Rapide (3 bottoni) | `.quick-actions .quick-action-btn` | Naviga ad assessment/incidents o apre modale policy AI |
| Select categoria policy AI | `#ai-policy-category` (runtime) | In `#modal-overlay`; "Genera" → POST /api/policies/ai-generate |
## assessment (/assessment.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Schermata iniziale | `#assessment-start` | |
| Tabella assessment esistenti | `#existing-assessments` | |
| Wizard | `#assessment-wizard` | |
| Indicatore step (10 categorie) | `#steps-indicator` | Step generati via JS con classe `.step` |
| Barra avanzamento | `#progress-bar-fill` | |
| % avanzamento / contatori | `#progress-pct`, `#progress-answered`, `#progress-total` | |
| Titolo categoria / contatore domanda | `#category-title`, `#question-counter` | |
| Area domanda corrente | `#question-area` | Campi interni generati via JS |
| Contenitore risultati | `#assessment-results` | |
| Gauge punteggio complessivo | `#results-gauge` | |
| Barre punteggio per categoria | `#category-scores` | Verde ≥60%, giallo ≥40%, rosso <40% |
| Card Analisi AI | `#ai-analysis-card`, `#ai-analysis-content` | |
| Card generazione NCR | `#ncr-generation-card`, `#ncr-generation-result` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Analisi AI (header) | `#btn-ai-analyze` | POST /api/assessments/{id}/ai-analyze (nascosto fuori dai risultati) |
| Nuovo Assessment | `#btn-new-assessment` | POST /api/assessments/create + apre wizard |
| Precedente / Successiva | `#btn-prev`, `#btn-next` | Su ultima domanda `#btn-next` diventa "Completa Assessment" |
| Slider maturità | `#maturity-slider` (runtime), valore `#maturity-value` (runtime) | Dentro `#question-area` |
| Note / Evidenza | `#question-notes`, `#question-evidence` (runtime) | |
| Genera NCR dai gap | `#btn-generate-ncr` | POST /api/ncr/from-assessment |
## acn-gap (/acn-gap.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Intro normativa | `#acn-intro` | |
| Schermata START | `#acn-start` | |
| Badge livello + perimetro | `#acn-level-info` | Importanti 37/87, essenziali 43/116 |
| Lista assessment esistenti | `#acn-existing` | |
| Catalogo requisiti (card + body) | `#acn-catalog`, `#acn-catalog-body` | Cache `window._acnCatalog` |
| Wizard | `#acn-wizard` | |
| Titolo / badge livello wizard | `#acn-wizard-title`, `#acn-wizard-level` | |
| Progress bar / contatori | `#acn-bar-fill`, `#acn-answered`, `#acn-total` | |
| Hint salvataggio | `#acn-savehint` | aria-live="polite" |
| Accordion funzioni/requisiti | `#acn-functions` | |
| Messaggio wizard | `#acn-wizard-msg` | |
| Risultati | `#acn-results` | |
| Punteggio complessivo | `#acn-overall` | Verde ≥80, lime ≥60, ambra ≥40, rosso <40 |
| Punteggi per funzione GV/ID/PR/DE/RS/RC | `#acn-func-scores` | |
| Requisiti non conformi | `#acn-gaps` | |
| Card Analisi AI | `#acn-ai-card`, `#acn-ai-body` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Analisi AI dei gap (header) | `#btn-ai-analyze` | POST /api/acn-gap/{id}/aiAnalyze — NB: stesso id di assessment.html, pagina diversa |
| Nuova Gap Analysis ACN | `#btn-new-acn` | POST /api/acn-gap/create |
| Pillole risposta requisito | `.acn-opt[data-key][data-val]` (runtime) | Autosave batch debounce 700ms |
| Calcola punteggio e completa | `#btn-acn-complete` | POST /api/acn-gap/{id}/complete |
## risks (/risks.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Vista Tabella | `#view-table` | |
| Corpo tabella rischi | `#risks-table-body` | Badge score: <10 low, 10-14 medium, 15-19 high, ≥20 critical |
| Vista Matrice 5x5 | `#view-matrix`, griglia `#risk-matrix-grid` | |
| Vista FAIR | `#view-fair` | |
| Risultati FAIR | `#fair-result`; `#fair-p10`, `#fair-p50`, `#fair-p90`, `#fair-mean`, `#fair-lef`, `#fair-histogram`, `#fair-histogram-labels` | |
| ALE di portafoglio / registro | `#fair-portfolio`, `#fair-register-body` | |
| Vista KRI | `#view-kri`; riepilogo `#kri-summary`; tabella `#kri-table-body` | |
| Vista Dettaglio rischio | `#view-detail` | Contenuto generato via JS |
| Anteprima score modale | `#score-preview-value`, `#likelihood-value`, `#impact-value`, `#likelihood-detail`, `#impact-detail` (runtime) | Scala ISO 27005 |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Toggle 4 viste | `#view-toggle` (bottoni interni senza id) | `switchView()` Tabella/Matrice/FAIR/KRI |
| AI Suggerisci | `#btn-ai-suggest` | POST /api/risks/ai-suggest → modale suggerimenti |
| Filtri tabella | `#filter-category`, `#filter-status` | Ricarica GET /api/risks/list |
| Select rischio FAIR | `#fair-risk-select` | |
| Input FAIR | `#fair-tef-min`, `#fair-tef-ml`, `#fair-tef-max`, `#fair-vuln`, `#fair-lm-min`, `#fair-lm-ml`, `#fair-lm-max` | |
| Calcola ALE | `#btn-run-fair` | POST /api/risks/{id}/fair |
| Torna alla lista (dettaglio) | `.detail-back` (runtime) | |
| Campi modale rischio | `#risk-title`, `#risk-description`, `#risk-category`, `#risk-treatment`, `#risk-threat-source`, `#risk-vulnerability`, `#risk-likelihood`, `#risk-impact`, `#risk-owner`, `#risk-review-date`, `#risk-nis2-article` (runtime) | POST create / PUT {id} |
| Campi modale KRI | `#kri-name`, `#kri-desc`, `#kri-cat`, `#kri-unit`, `#kri-cur`, `#kri-target`, `#kri-warn`, `#kri-crit`, `#kri-dir` (runtime) | POST/PUT /api/risks/kri |
| Campi modale trattamento | `#treatment-description`, `#treatment-responsible`, `#treatment-due-date`, `#treatment-status`, `#treatment-notes` (runtime) | POST /api/risks/{id}/treatments |
## incidents (/incidents.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Vista Lista | `#view-list` | |
| Corpo tabella incidenti | `#incidents-table-body` | 3 dot Art.23 (verde inviato, rosso scaduto, giallo <6h, grigio in attesa) |
| Vista Dettaglio | `#view-detail` | Contenuto via JS; timeline `.nis2-timeline` / `.event-timeline` |
| Esito significatività | `#significance-result` (runtime) | Box rosso ≥1 criterio / verde 0 criteri |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Filtri lista | `#filter-classification`, `#filter-severity`, `#filter-status` | Ricarica GET /api/incidents/list |
| Campi modale registrazione | `#inc-title`, `#inc-description`, `#inc-classification`, `#inc-severity`, `#inc-detected-at`, `#inc-significant`, `#inc-affected-services`, `#inc-affected-users`, `#inc-cross-border`, `#inc-malicious` (runtime) | POST /api/incidents/create |
| Checkbox criteri Art.23 | `#crit-users`, `#crit-duration`, `#crit-crossborder`, `#crit-attack`, `#crit-financial` (runtime) | `evalSignificance()` |
| Campi modale aggiornamento | `#upd-status`, `#upd-root-cause`, `#upd-remediation`, `#upd-lessons` (runtime) | PUT /api/incidents/{id} |
| Campi modale evento timeline | `#event-type`, `#event-description` (runtime) | POST /api/incidents/{id}/timeline |
| Torna alla lista (dettaglio) | `.detail-back` (runtime) | |
## policies (/policies.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Tab Elenco | `#tab-list`; tabella `#policies-table` | |
| Vista dettaglio | `#policy-detail-view` | Banner bozza AI: `#ai-draft-warning-{id}` (runtime) |
| Tab Template | `#tab-templates`; griglia `#templates-grid` | |
| Anteprima AI | `#ai-preview-container` (runtime) | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Filtri elenco | `#filter-category`, `#filter-status` | GET /api/policies/list |
| Form policy | `#policy-form`; `#form-title`, `#form-category`, `#form-nis2-article`, `#form-version`, `#form-review-date`, `#form-content` (runtime) | POST create / PUT {id} |
| Generazione AI | `#ai-category`, `#ai-generate-btn` (runtime) | POST /api/policies/ai-generate |
## supply-chain (/supply-chain.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| KPI overview | `#risk-overview`; `#stat-total`, `#stat-critical`, `#stat-expiring`, `#stat-avg-score` | Scadenza contratti = 90 giorni, calcolo client-side |
| Grafico criticità | `#criticality-chart`; barre `#chart-bar-critical|high|medium|low`; valori `#chart-val-critical|high|medium|low` | |
| Sezione elenco | `#suppliers-list-section`; tabella `#suppliers-table` | |
| Vista dettaglio fornitore | `#supplier-detail-view` | Contenuto via JS |
| Score live valutazione | `#assessment-score-preview`, `#assessment-live-score` (runtime) | Sì=100, Parziale=50, No=0, media pesata |
| Esito import | `#importResult` (runtime) | |
| Lista categorie | `#categories-list-body` (runtime) | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Filtri elenco | `#filter-criticality`, `#filter-status` | Filtro client-side |
| Form fornitore | `#supplier-form`; `#form-name`, `#form-vat`, `#form-contact-name`, `#form-contact-email`, `#form-service-type`, `#form-criticality`, `#form-category`, `#form-service-desc`, `#form-contract-start`, `#form-contract-expiry`, `#form-notes` (runtime) | POST create / PUT {id} |
| Domande valutazione (10) | `#assessment-questions` (runtime, pill Sì/Parziale/No) | POST /api/supply-chain/{id}/assess |
| Import CSV | overlay `#importModalDyn` (runtime); `#importFile`, `#importCsv`, `#importRunBtn` | POST /api/supply-chain/import (max 1000 righe) |
| Form categoria | `#cat-name`, `#cat-desc` (runtime) | POST/PUT/DELETE /api/supply-chain/categories |
## assets (/assets.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Card statistiche | `#asset-stats`; `#stat-total`, `#stat-types`, `#stat-type-chips`, `#stat-critical`, `#stat-decommissioned` | Riflettono il filtro corrente |
| Contenitore tabella | `#assets-container` | Render via JS |
| Riepilogo scoring | `#score-preview`, `#score-total`, `#score-class` (runtime) | Critico ≥80, Alto ≥60, Medio ≥40, Basso ≥20; rilevante da 40 |
| Anteprima import | `#imp-preview` (runtime) | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Filtri | `#filter-type`, `#filter-criticality`, `#filter-status` | GET /api/assets/list con query |
| Form asset | `#asset-name`, `#asset-type`, `#asset-category`, `#asset-description`, `#asset-criticality`, `#asset-status`, `#asset-owner`, `#asset-location`, `#asset-ip`, `#asset-vendor`, `#asset-version`, `#asset-serial`, `#asset-purchase-date`, `#asset-warranty` (runtime) | POST create / PUT {id} |
| Import CSV/CMDB | `#imp-file`, `#imp-csv` (runtime) | POST /api/assets/import |
| Select criteri scoring (6) | `select.score-criterion[data-key]` (runtime) | POST /api/assets/{id}/score |
## training (/training.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Tab Corsi | `#tab-courses`; griglia `#courses-grid` | |
| Tab Assegnazioni | `#tab-assignments`; `#assignments-container` | |
| Tab Compliance | `#tab-compliance`; `#compliance-container` | Barra verde ≥80%, giallo ≥50%, rosso <50% |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Form nuovo corso | `#course-title`, `#course-description`, `#course-target-role`, `#course-duration`, `#course-nis2-article`, `#course-passing-score`, `#course-mandatory` (runtime) | POST /api/training/courses (soglia default 70) |
| Form assegnazione | `#assign-course-id`, `#assign-due-date`, checkbox `input.assign-user-cb` (runtime) | POST /api/training/assign |
## reports (/reports.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Tab Report | `#tab-report`; `#report-container` | Barra verde ≥80%, giallo ≥50%, rosso <50% |
| Tab Controlli | `#tab-controls`; `#controls-container` | |
| Tab Audit Log | `#tab-audit`; `#audit-container` | Paginato 25/pagina |
| Tab ISO 27001 | `#tab-iso`; `#iso-container` | Mapping NIS2 ↔ ISO 27001:2022 |
| Tab Monitoraggio Continuo | `#tab-monitoring`; `#monitoring-container` | Evidence Automation |
| Tab Requisiti ACN | `#tab-acn`; `#acn-container` | Determina ACN 164179/2025 |
| Valore live slider controllo | `#ctrl-impl-pct-val` (runtime) | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Campi modale controllo | `#ctrl-status`, `#ctrl-impl-pct`, `#ctrl-evidence`, `#ctrl-responsible`, `#ctrl-next-review` (runtime) | PUT /api/audit/controls/{id} |
## isms (/isms.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Hint salvataggio | `#isms-savehint` | aria-live="polite" |
| Intro / disclaimer | `.isms-intro`, `.isms-disclaimer` | Solo classi (vedi ID da aggiungere) |
| Barra step wizard | `#isms-steps`; step `#step-0` … `#step-5` (runtime, isms.js) | Classi `active`/`done` |
| Pannelli step | `#panel-0` … `#panel-5` | |
| Lista ruoli | `#roles-list` | |
| Statistiche / gruppi SoA | `#soa-stats`, `#soa-groups`; controlli `.soa-ctrl[data-code]` (runtime) | 111 controlli 27001/27017/27018 |
| Hint generazione AI / lista documenti | `#aigen-hint`, `#docs-list` | |
| Readiness | `#readiness-pct`, `#readiness-checklist` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Step 1 contesto | `#f-scope`, `#f-ctx-int`, `#f-ctx-ext`, `#f-parties`, `#f-boundaries`, `#f-exclusions`; flag cloud `#f-uses-cloud`, `#f-cloud-provider`, `#f-pii-cloud` | POST /api/isms/model |
| Step 2 ruoli | `#r-name`, `#r-resp`, `#r-raci` | POST/DELETE /api/isms/roles |
| Step 3 risk | `#f-method`, `#f-objectives` | POST /api/isms/model |
| Step 4 SoA | stati/checkbox dentro `.soa-ctrl[data-code]` (runtime) | PUT /api/isms/soa, autosave debounce 600ms; toggle Applicabile ricarica SoA dopo 400ms |
| Step 5 documenti | `#d-type`, `#btn-aigen` | POST /api/isms/documents/ai-generate |
## normative (/normative.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Statistiche | `#stats-grid` | |
| Avanzamento ACK | `#ack-pct`, `#ack-bar` | |
| Lista aggiornamenti | `#updates-container`; card `#update-{id}` (runtime) | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Filtri | `#filter-source`, `#filter-impact`, `#filter-action` | GET /api/normative/list |
| Note presa visione | `#ack-notes` (runtime) | POST /api/normative/{id}/ack (403 se non org_admin/compliance_manager) |
## whistleblowing (/whistleblowing.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Banner Art.32 | `.art32-banner` | Art. 32 D.Lgs. 138/2024 — classe, nessun id |
| Statistiche | `#stats-grid` | 403 per ruoli non gestori |
| Vista Lista | `#view-list`; `#reports-container` | |
| Vista Track | `#view-track`; risultato `#track-result` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Filtri lista | `#filter-status`, `#filter-priority`, `#filter-category` | GET /api/whistleblowing/list |
| Form segnalazione | `#view-submit`; `#is-anonymous`, `#submit-form`, `#rep-category`, `#rep-title`, `#rep-description`, `#rep-priority`, `#rep-article`, `#contact-email-group`, `#rep-email` | POST /api/whistleblowing/submit (no auth API) |
| Tracking anonimo | `#track-token` | GET /api/whistleblowing/track-anonymous?token= |
| Modale gestione | `#modal-status`, `#modal-notes` (runtime) | PUT /api/whistleblowing/{id} |
## kb (/kb.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Card form upload | `#kb-form-card` | Nascosta di default |
| Status indicizzazione | `#kb-status` | |
| Lista documenti | `#kb-doc-list` (dentro card `.kb-doc-list`) | |
| Risultati search | `#kb-search-results` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Carica documento (toggle form) | `#btn-kb-upload` | `setupScopeForUser()` |
| Campi upload | `#kb-title`, `#kb-entity-type`, `#kb-text` | POST /api/knowledgebase/ingest |
| Radio scope | `input[name="kb-scope"]`; label `[data-scope-opt="SYSTEM|FIRM|ORG"]` | Visibilità per ruolo (enforcement backend) |
| Condivisione FIRM | `#kb-share-block`, multi-select `#kb-shared-with` | GET /api/knowledgebase/firmOrgs |
| Indicizza / Annulla | `#btn-kb-submit`, `#btn-kb-cancel` | |
| Elimina documento | `[data-del-id="{id}"]` (runtime, kb.js) | DELETE /api/knowledgebase/{id} |
| Search semantica | `#kb-search-query`, `#btn-kb-search` | POST /api/knowledgebase/search (top 5) |
## cross-analysis (/cross-analysis.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Header portfolio | `#portfolio-header` | Solo consultant/super_admin |
| Tab Chat | `#tab-chat`; `#chat-messages`, `#chat-empty` | Indicatore thinking `#think-{timestamp}` (runtime) |
| Tab Storico | `#tab-history`; `#history-content` | Ultime 20 analisi, lazy |
| Tab Portfolio dati | `#tab-portfolio`; `#portfolio-content` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Tab bar | `.tab-bar[role="tablist"] .tab-btn[aria-controls="tab-chat|tab-history|tab-portfolio"]` | `switchTab()` |
| Domande rapide (6 chip) | `#quick-qs` (chip interne senza id) | Pre-compila `#chat-input` |
| Input domanda / invio | `#chat-input`, `#btn-send` | POST /api/cross-analysis/analyze |
## settings (/settings.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Nome org header | `#header-org-name` | |
| Tab Organizzazione | `#tab-org`; classificazione `#entity-classification`; piano `#subscription-plan` | |
| Tab Profilo | `#tab-profile` | |
| Indicatore forza password | `#pw-strength-bar`, `#pw-seg-1`…`#pw-seg-4`, `#pw-strength-text` | |
| Tab Preferenze | `#tab-preferences`; conferma `#pref-saved-msg` | |
| Tab Membri | `#tab-members`; `#members-container` | |
| Tab Sicurezza | `#tab-security`; `#card-sessions`, `#sessions-container`, `#audit-log-container` | |
| Tab API Keys | `#tab-apikeys`; `#apikeys-container`, `#available-scopes-container` | |
| Tab Webhook | `#tab-webhooks`; `#webhooks-container`, `#deliveries-container` | |
| Tab Branding | `#tab-branding`; conferma `#brand-saved-msg` | Solo super_admin/consultant |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Barra tab | `.settings-tabs .settings-tab` | ⚠ `switchTab()` seleziona per INDICE posizionale — non alterare ordine/numero bottoni |
| Tab Branding (unico con id) | `#tab-btn-branding` | Mostrato via /api/auth/me |
| Form organizzazione | `#org-form`; `#org-name`, `#org-sector`, `#org-vat`, `#org-fiscal`, `#org-employees`, `#org-turnover`, `#org-country`, `#org-city`, `#org-address`, `#org-website`, `#org-email`, `#org-phone`; `#btn-save-org` | PUT /api/organizations/{id} |
| Adesione volontaria | `#org-voluntary` (runtime) | PUT immediato voluntary_compliance |
| Form profilo | `#profile-form`; `#profile-email` (disabled), `#profile-name`, `#profile-phone`, `#profile-language`; `#btn-save-profile` | PUT /api/auth/profile |
| Cambio password | `#password-form`; `#pw-current`, `#pw-new`, `#pw-confirm`; `#btn-change-pw` | POST /api/auth/change-password (logout dopo successo) |
| Preferenze | `#preferences-form`; `#pref-lang`, `#pref-theme`, `#pref-tz`, `#pref-notif-email`, `#pref-notif-inapp`; `#pref-save-btn` | PUT /api/auth/preferences |
| Invito membro | `#invite-email`, `#invite-role` (runtime) | POST /api/organizations/{orgId}/invite |
| Disconnetti altre sessioni | `#revoke-all-btn` | DELETE /api/auth/sessions |
| Nuova API Key | `#apikey-name`, `#apikey-scopes-checkboxes`, `#apikey-expires` (runtime) | POST /api/webhooks/api-keys (chiave one-time) |
| Nuovo Webhook | `#wh-name`, `#wh-url` (runtime) | POST /api/webhooks/subscriptions (secret HMAC one-time) |
| Branding | `#branding-form`; `#brand-name`, `#brand-logo`, `#brand-pri`, `#brand-sec`; `#brand-save-btn` | PUT /api/branding |
## companies (/companies.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Griglia card aziende | `#companies-grid`; card `.company-card[data-org-id]` | Solo ruolo consultant |
| Elementi asincroni per card | `#score-row-{orgId}`, `#score-fill-{orgId}`, `#score-val-{orgId}`, `#stats-row-{orgId}`, `#stat-risks-{orgId}`, `#stat-incidents-{orgId}` (runtime) | |
| Stato vuoto | `#companies-empty` | |
| Sidebar | `#sidebar` | Iniettata da common-bi.js |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Ricerca azienda | `#search-input` | Filtro client-side su `data-name` |
| Modale connettori | `#conn-modal` (runtime); righe `.conn-row[data-type]`; campi `.c-en`, `.c-tenant`, `.c-client` | GET/PUT /api/organizations/{orgId}/connectors |
## onboarding (/onboarding.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Stepper 4 passi | `#stepper`; passi `.stepper-step[data-step="1..4"]`; connettori `.stepper-connector[data-after]` | |
| Pannelli step | `#step-1` … `#step-4` | |
| Risultato ricerca P.IVA / spinner | `#s1-result`, `#s1-loading` | |
| Banner fonte dati | `#data-source-banner`, `#data-source-text` | "Dati precompilati da CertiSource" |
| Risultato classificazione | `#classification-result`; `#classification-icon`, `#classification-label`, `#classification-desc` | Calcolo client-side `classifyLocally()` |
| Sezione volontaria | `#voluntary-section`, `#voluntary-card` | |
| Riepilogo dati | `#sum-company`, `#sum-vat`, `#sum-sector`, `#sum-entity-type`, `#sum-employees`, `#sum-turnover` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| P.IVA + ricerca | `#s1-vat`, `#s1-fetch-btn`, continua `#s1-continue-btn` | POST /api/onboarding/fetch-company |
| Form dati aziendali | `#company-name`, `#vat-number`, `#fiscal-code`, `#address`, `#city`, `#website`, `#company-email`, `#company-phone`, `#sector`, `#employee-count`, `#annual-turnover` | Validazione client-side |
| Form profilo | `#full-name`, `#role`, `#user-phone` | |
| Adesione volontaria | `#voluntary-checkbox` | `onVoluntaryChange()` |
| Completa onboarding | `#complete-btn` | POST /api/onboarding/complete |
## service-continuity (/service-continuity.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Header documento | `header.doc-header` | ⚠ La pagina NON contiene alcun id: solo ancore strutturali/classi |
| Blocchi resilienza L1-L7 | `.layer` (con `.layer-num`, `.layer-body`) | |
| Footer documento | `footer.doc-footer` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| — (pagina statica, nessuna azione) | — | Unico link esterno nel footer |
## admin/index (/admin/index.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Riga KPI 1 | `#stats-row-1`; `#stat-total-orgs`, `#stat-active-orgs`, `#stat-total-users`, `#stat-active-users`; etichette `#lbl-total-orgs`, `#lbl-active-orgs`, `#lbl-total-users`, `#lbl-active-users` | Solo super_admin |
| Riga KPI 2 | `#stats-row-2`; `#stat-assessments`, `#stat-incidents`, `#stat-risks`, `#stat-ai`; etichette `#lbl-assessments`, `#lbl-incidents`, `#lbl-risks`, `#lbl-ai` | |
| Grafico piani | `#plans-chart` | role="img" con aria-label |
| Sidebar | `#sidebar` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Collegamenti rapidi | `nav.quick-links-grid a.quick-link-card` | Naviga a organizations.html / users.html (senza id) |
## admin/organizations (/admin/organizations.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Contenitore tabella | `#orgs-container` | Tabella e paginazione generate via JS senza id |
| Sidebar | `#sidebar` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Paginazione | `.pagination` dentro `#orgs-container` (runtime, bottoni senza id) | `loadOrganizations(page±1)` |
## admin/users (/admin/users.html)
### Visualizzazione
| Campo/Elemento | Selettore | Note |
|---|---|---|
| Contenitore tabella | `#users-container` | aria-live="polite" |
| Sidebar | `#sidebar` | |
### Azioni
| Azione | Selettore | Effetto |
|---|---|---|
| Toggle attivo per riga | `label.active-toggle input[type=checkbox]` (runtime, senza id — solo per posizione) | PUT /api/admin/users/{id} {is_active} |
| Impersonate | `button.btn.btn-secondary.btn-sm[onclick^="impersonateUser"]` (runtime, senza id) | POST /api/auth/impersonate (1h) |
| Paginazione | `.pagination` dentro `#users-container` (runtime, senza id) | `loadUsers(page±1)` |
---
## ID STABILI DA AGGIUNGERE (consolidato)
> Worklist per modifiche chirurgiche SOLO-ATTRIBUTO (aggiunta `id`/`data-*`, zero cambi di markup/logica).
> Unione deduplicata delle sezioni "ID STABILI MANCANTI" delle 22 mappe. Nessun id proposto risulta duplicato cross-vista.
> Esclusi: kb "Elimina documento" (ha già `data-del-id`, selettore stabile) e onboarding `#sector` (id già presente).
| Vista | Elemento | Riga | Id proposto |
|---|---|---|---|
| dashboard | Bottone "Nuovo Assessment" (azioni rapide) | ~91 | `nis2-dashboard-qa-assessment` |
| dashboard | Bottone "Registra Incidente" (azioni rapide) | ~95 | `nis2-dashboard-qa-incidente` |
| dashboard | Bottone "Genera Policy AI" (azioni rapide) | ~99 | `nis2-dashboard-qa-policy-ai` |
| dashboard | Card "Azioni Rapide" | ~85 | `nis2-dashboard-quick-actions` |
| dashboard | Card "Prossime Scadenze" (wrapper) | ~110 | `nis2-dashboard-card-scadenze` |
| dashboard | Card "Attività Recenti" (wrapper) | ~120 | `nis2-dashboard-card-attivita` |
| assessment | Bottone "Salva Progresso" (footer card domanda) | ~80 | `nis2-assessment-btn-salva` |
| assessment | Bottone "Analisi AI" header card Risultati | ~97 | `nis2-assessment-btn-ai-risultati` |
| acn-gap | Bottone "Consulta il catalogo dei requisiti" | ~75 | `nis2-acn-gap-btn-catalogo` |
| acn-gap | Bottone "Torna all'inizio" (risultati) | ~134 | `nis2-acn-gap-btn-restart` |
| risks | Bottone toggle "Tabella" | ~311 | `nis2-risks-toggle-tabella` |
| risks | Bottone toggle "Matrice" | ~312 | `nis2-risks-toggle-matrice` |
| risks | Bottone toggle "Quantitativo (FAIR)" | ~313 | `nis2-risks-toggle-fair` |
| risks | Bottone toggle "KRI" | ~314 | `nis2-risks-toggle-kri` |
| risks | Bottone "+ Nuovo Rischio" (header) | ~320 | `nis2-risks-btn-nuovo` |
| risks | Bottone "+ Nuovo KRI" (vista KRI) | ~467 | `nis2-risks-btn-nuovo-kri` |
| risks | Bottone "Torna alla lista" dettaglio (`.detail-back`, JS) | script ~918 | `nis2-risks-btn-back` |
| incidents | Bottone "+ Nuovo Incidente" (header) | ~271 | `nis2-incidents-btn-nuovo` |
| incidents | Bottone "Torna alla lista" dettaglio (`.detail-back`, JS) | script ~765 | `nis2-incidents-btn-back` |
| incidents | Bottoni invio notifiche Art.23 (JS) | script ~919-921 | `nis2-incidents-btn-early-warning`, `nis2-incidents-btn-notifica-72h`, `nis2-incidents-btn-report-finale` |
| policies | Pulsante "Genera con AI" (header) | ~251 | `nis2-policies-btn-ai-generate` |
| policies | Pulsante "+ Nuova Policy" (header) | ~255 | `nis2-policies-btn-new` |
| policies | Contenitore tab bar `div.tabs` | ~261 | `nis2-policies-tabs` |
| policies | Bottone tab "Elenco Policy" | ~262 | `nis2-policies-tab-btn-list` |
| policies | Bottone tab "Template" | ~263 | `nis2-policies-tab-btn-templates` |
| policies | Pulsante "Approva" vista dettaglio (JS) | script ~551 | `nis2-policies-detail-btn-approve` |
| policies | Pulsante "Modifica" vista dettaglio (JS) | script ~555 | `nis2-policies-detail-btn-edit` |
| supply-chain | Pulsante "Categorie" (header) | ~377 | `nis2-supply-chain-btn-categories` |
| supply-chain | Pulsante "Template" (header) | ~378 | `nis2-supply-chain-btn-templates` |
| supply-chain | Pulsante "Importa" (header) | ~379 | `nis2-supply-chain-btn-import` |
| supply-chain | Pulsante "+ Nuovo Fornitore" (header) | ~380 | `nis2-supply-chain-btn-new` |
| supply-chain | Card grafico "Distribuzione Criticità" | ~406 | `nis2-supply-chain-chart-card` |
| supply-chain | Pulsanti dettaglio Valuta/Questionario/Modifica (JS) | script ~753-764 | `nis2-supply-chain-detail-btn-assess`, `nis2-supply-chain-detail-btn-send-questionnaire`, `nis2-supply-chain-detail-btn-edit` |
| assets | Pulsante "Importa" (header) | ~271 | `nis2-assets-btn-import` |
| assets | Pulsante "+ Nuovo Asset" (header) | ~272 | `nis2-assets-btn-new` |
| assets | Barra filtri `div.filters-bar` | ~319 | `nis2-assets-filters` |
| assets | Pulsanti azione riga tabella (JS) | script ~505-509 | `nis2-assets-row-btn-score-{id}`, `nis2-assets-row-btn-edit-{id}` |
| assets | Pulsanti footer modale dettaglio (JS) | script ~703-705 | `nis2-assets-detail-btn-close`, `nis2-assets-detail-btn-score`, `nis2-assets-detail-btn-edit` |
| training | Pulsante "+ Nuovo Corso" (header) | ~234 | `nis2-training-btn-new-course` |
| training | Contenitore tab bar `div.tab-nav` | ~240 | `nis2-training-tabs` |
| training | Bottone tab "Corsi" | ~241 | `nis2-training-tab-btn-courses` |
| training | Bottone tab "Le Mie Assegnazioni" | ~242 | `nis2-training-tab-btn-assignments` |
| training | Bottone tab "Compliance Formativa" | ~243 | `nis2-training-tab-btn-compliance` |
| training | Pulsante "Assegna Corso" (tab compliance) | ~269 | `nis2-training-btn-assign-course` |
| reports | Pulsante "Genera Report" (header) | ~355 | `nis2-reports-btn-generate` |
| reports | Contenitore tab bar `div.tab-nav` | ~361 | `nis2-reports-tabs` |
| reports | Bottone tab "Report Compliance" | ~362 | `nis2-reports-tab-btn-report` |
| reports | Bottone tab "Controlli" | ~363 | `nis2-reports-tab-btn-controls` |
| reports | Bottone tab "Audit Log" | ~364 | `nis2-reports-tab-btn-audit` |
| reports | Bottone tab "ISO 27001" | ~365 | `nis2-reports-tab-btn-iso` |
| reports | Bottone tab "Monitoraggio Continuo" | ~366 | `nis2-reports-tab-btn-monitoring` |
| reports | Bottone tab "Requisiti ACN" | ~367 | `nis2-reports-tab-btn-acn` |
| reports | Pulsante "Stampa Report" (JS) | script ~722 | `nis2-reports-btn-print` |
| reports | Select stato requisito ACN (JS, per riga) | script ~543 | `nis2-reports-acn-status-{id}` |
| isms | Bottone "Esporta SGSI" (header) | 59 | `nis2-isms-btn-export` |
| isms | Bottone "Salva e continua" Step 1 | 91 | `nis2-isms-btn-save-step1` |
| isms | Bottone "Aggiungi ruolo" Step 2 | 105 | `nis2-isms-btn-add-role` |
| isms | Bottone "Continua" Step 2 | 107 | `nis2-isms-btn-next-step2` |
| isms | Bottone "Salva e continua" Step 3 | 118 | `nis2-isms-btn-save-step3` |
| isms | Bottone "Ri-deriva dal NIS2" Step 4 | 128 | `nis2-isms-btn-derive-soa` |
| isms | Bottone "Continua" Step 4 | 132 | `nis2-isms-btn-next-step4` |
| isms | Bottone "Continua" Step 5 | 154 | `nis2-isms-btn-next-step5` |
| isms | Box intro | 67 | `nis2-isms-intro` |
| isms | Box disclaimer | 71 | `nis2-isms-disclaimer` |
| normative | Bottone "Solo Pendenti" (header) | 41 | `nis2-normative-btn-pending` |
| normative | Bottone "Prendi in visione" su card (JS) | script ~230 | `nis2-normative-btn-ack-{id}` (o `data-ack-id`) |
| normative | Card barra avanzamento ACK | 54 | `nis2-normative-ack-card` |
| whistleblowing | Bottone "Traccia Segnalazione Anonima" (header) | 44 | `nis2-wb-btn-track` |
| whistleblowing | Bottone "Nuova Segnalazione" (header) | 48 | `nis2-wb-btn-new` |
| whistleblowing | Banner Art.32 | 56 | `nis2-wb-art32-banner` |
| whistleblowing | Bottone "Gestisci" per riga (JS) | script ~312 | data-attribute `data-wb-view-id` |
| whistleblowing | Bottone "Verifica Stato" (vista track) | 185 | `nis2-wb-btn-verify` |
| whistleblowing | Bottone "Invia Segnalazione" | 164 | `nis2-wb-btn-submit` |
| kb | Card lista documenti (wrapper) | 110 | `nis2-kb-doc-list-card` |
| kb | Card test search (wrapper) | 122 | `nis2-kb-search-card` |
| kb | Paragrafo intro visibilità | 42 | `nis2-kb-intro` |
| cross-analysis | Tab "Chat AI" | 308 | `nis2-xa-tab-btn-chat` |
| cross-analysis | Tab "Storico" | 311 | `nis2-xa-tab-btn-history` |
| cross-analysis | Tab "Portfolio dati" | 314 | `nis2-xa-tab-btn-portfolio` |
| cross-analysis | Badge privacy header | 284 | `nis2-xa-privacy-badge` |
| cross-analysis | Badge "Solo Consulenti & Admin" | 289 | `nis2-xa-role-badge` |
| cross-analysis | Chip domande rapide (6) | 331-336 | `nis2-xa-quickq-1` … `nis2-xa-quickq-6` (o `data-quickq`) |
| settings | Tab "Organizzazione" | 215 | `nis2-settings-tab-btn-org` |
| settings | Tab "Profilo" | 216 | `nis2-settings-tab-btn-profile` |
| settings | Tab "Preferenze" | 217 | `nis2-settings-tab-btn-preferences` |
| settings | Tab "Membri" | 218 | `nis2-settings-tab-btn-members` |
| settings | Tab "Sicurezza" | 219 | `nis2-settings-tab-btn-security` |
| settings | Tab "API Keys" | 220 | `nis2-settings-tab-btn-apikeys` |
| settings | Tab "Webhook" | 221 | `nis2-settings-tab-btn-webhooks` |
| settings | Bottone "Invita Membro" | 498 | `nis2-settings-btn-invite-member` |
| settings | Bottone "Nuova API Key" | 573 | `nis2-settings-btn-new-apikey` |
| settings | Bottone "Nuovo Webhook" | 600 | `nis2-settings-btn-new-webhook` |
| settings | Bottone "Aggiorna" delivery log | 612 | `nis2-settings-btn-reload-deliveries` |
| companies | Bottone header "Aggiungi Cliente" (link) | 292 | `nis2-companies-add-btn` |
| companies | Bottone empty state "Aggiungi Primo Cliente" | 329 | `nis2-companies-add-first-btn` |
| companies | Bottone card "Connettori" (per card, JS) | script ~444 | `nis2-companies-connectors-btn-{orgId}` |
| companies | Bottone card "Gestisci" (per card, JS) | script ~447 | `nis2-companies-manage-btn-{orgId}` |
| companies | Contenitore header pagina `.companies-header` | 282 | `nis2-companies-header` |
| companies | Bottone "Salva" riga connettore (modale, JS) | script ~489 | `nis2-companies-conn-save-{type}` |
| onboarding | Bottone "Inserisci manualmente" (step 1) | 182 | `nis2-onboarding-manual-btn` |
| onboarding | Bottone "Indietro" step 2 | 298 | `nis2-onboarding-back-2` |
| onboarding | Bottone "Continua" step 2 | 303 | `nis2-onboarding-next-2` |
| onboarding | Bottone "Indietro" step 3 | 348 | `nis2-onboarding-back-3` |
| onboarding | Bottone "Continua" step 3 | 353 | `nis2-onboarding-next-3` |
| onboarding | Bottone "Indietro" step 4 | 404 | `nis2-onboarding-back-4` |
| service-continuity | Header documento | 62 | `nis2-continuity-header` |
| service-continuity | Sezione 1 Architettura (h2) | 81 | `nis2-continuity-architettura` |
| service-continuity | Sezione 2 Resilienza 7 livelli (h2) | 90 | `nis2-continuity-resilienza` |
| service-continuity | Blocchi L1-L7 | 92-149 | `nis2-continuity-layer-1` … `nis2-continuity-layer-7` |
| service-continuity | Sezione 3 Rate limiting (h2) | 151 | `nis2-continuity-rate-limiting` |
| service-continuity | Sezione 4 Manutenzione (h2) | 164 | `nis2-continuity-manutenzione` |
| service-continuity | Sezione 5 Matrice rischi (h2) | 178 | `nis2-continuity-matrice-rischi` |
| service-continuity | Sezione 6 SLA (h2) | 193 | `nis2-continuity-sla` |
| service-continuity | Sezione 7 Checklist (h2) | 206 | `nis2-continuity-checklist` |
| service-continuity | Footer documento | 222 | `nis2-continuity-footer` |
| admin/index | Header pagina con badge Super Admin | 191 | `nis2-admin-header` |
| admin/index | Nav collegamenti rapidi | 281 | `nis2-admin-quick-links` |
| admin/index | Card-link "Gestisci Organizzazioni" | 282 | `nis2-admin-link-organizations` |
| admin/index | Card-link "Gestisci Utenti" | 291 | `nis2-admin-link-users` |
| admin/index | Card "Distribuzione Piani" | 264 | `nis2-admin-plans-card` |
| admin/organizations | Header pagina | 110 | `nis2-admin-orgs-header` |
| admin/organizations | Breadcrumb | 119 | `nis2-admin-orgs-breadcrumb` |
| admin/organizations | Tabella organizzazioni (JS) | script ~228 | `nis2-admin-orgs-table` |
| admin/organizations | Bottone paginazione "Precedente" (JS) | script ~293 | `nis2-admin-orgs-prev` |
| admin/organizations | Bottone paginazione "Successivo" (JS) | script ~296 | `nis2-admin-orgs-next` |
| admin/users | Header pagina | 132 | `nis2-admin-users-header` |
| admin/users | Breadcrumb | 141 | `nis2-admin-users-breadcrumb` |
| admin/users | Tabella utenti (JS) | script ~240 | `nis2-admin-users-table` |
| admin/users | Toggle attivo per riga (JS) | script ~288 | `nis2-admin-users-toggle-{userId}` |
| admin/users | Bottone "Impersonate" per riga (JS) | script ~295 | `nis2-admin-users-impersonate-{userId}` |
| admin/users | Bottone paginazione "Precedente" (JS) | script ~321 | `nis2-admin-users-prev` |
| admin/users | Bottone paginazione "Successivo" (JS) | script ~324 | `nis2-admin-users-next` |
⚠ Vincolo settings: `switchTab()` seleziona i tab per indice posizionale (`querySelectorAll('.settings-tab')[tabMap[tab]]`) — l'aggiunta degli id NON deve cambiare ordine/numero dei bottoni tab.