# MAPPA-LOGICA · Formazione e Awareness ⟦ INIZIO · U-TRAINING · Videata "Formazione e Awareness" ⟧ File: public/training.html · URL: /training.html · Scopo: gestione della formazione in cybersicurezza richiesta dall'Art. 20 NIS2 (obbligo formativo per organi di gestione e dipendenti): catalogo corsi, assegnazioni personali, monitoraggio della compliance formativa per membro · Profili: tutti gli utenti autenticati dell'organizzazione (voce sidebar "Formazione"); il tab "Le Mie Assegnazioni" mostra i corsi dell'utente corrente; creazione corsi e assegnazioni soggette ai permessi backend (org_admin, compliance_manager, consultant) • U-TRAINING.1 — Intestazione pagina e azioni globali Mostra: titolo "Formazione e Awareness"; etichetta "Art. 20 NIS2"; pulsante "+ Nuovo Corso" Azioni: "+ Nuovo Corso" → modale creazione corso (U-TRAINING.3) Dati da: nessun endpoint (elementi statici) Confine: `header.content-header` (righe ~230-236); il pulsante NON ha id stabile • U-TRAINING.2 — Tab di navigazione (3 tab) Mostra: tab "Corsi" (attivo di default), "Le Mie Assegnazioni", "Compliance Formativa" Azioni: click → `switchTab(tabId, btn)` attiva il pannello corrispondente (`#tab-courses`, `#tab-assignments`, `#tab-compliance`) e carica i dati del tab Dati da: nessun endpoint diretto (ogni tab innesca il proprio caricamento) Confine: `div.tab-nav` (righe ~240-244); i bottoni tab NON hanno id stabile • U-TRAINING.3 — Tab "Corsi": catalogo corsi + modale "Nuovo Corso di Formazione" Mostra: griglia di card corso: titolo, badge "Obbligatorio" (se is_mandatory), descrizione, durata in minuti, ruolo destinatario (Tutti, Organi di Gestione, Compliance Manager, Dipendente, Tecnico), riferimento "Art. {nis2_article}", "Soglia: N%" (punteggio minimo quiz). Stato vuoto: "Nessun corso disponibile". Modale creazione con campi: Titolo* (`#course-title`), Descrizione (`#course-description`), Ruolo Destinatario (`#course-target-role`), Durata minuti (`#course-duration`), Articolo NIS2 (`#course-nis2-article`, es. "Art. 20"), Punteggio Minimo % (`#course-passing-score`, default 70), checkbox "Corso obbligatorio" (`#course-mandatory`) Azioni: le card corso sono informative (nessun click); "Crea Corso" nella modale → `createCourse()` (titolo obbligatorio) → POST e ricarica catalogo Dati da: GET /api/training/courses (`api.listCourses()`) · POST /api/training/courses (`api.post('/training/courses', data)`) Confine: pannello `#tab-courses` (riga ~247), griglia `#courses-grid` (~248); campi modale `#course-*` (righe ~415-443) dentro `#modal-overlay` • U-TRAINING.4 — Tab "Le Mie Assegnazioni" Mostra: tabella delle assegnazioni dell'utente corrente: Corso (titolo + etichetta rossa "Obbligatorio" se applicabile), Stato (badge: Assegnato, In Corso, Completato, Scaduto — "Scaduto" calcolato client-side se due_date passata e non completato), Scadenza, Punteggio Quiz (%), Azioni. Stato vuoto: "Nessuna assegnazione" Azioni: "Inizia" (stato Assegnato/Scaduto) → `updateAssignment(id, 'in_progress')`; "Completa" (stato In Corso) → `updateAssignment(id, 'completed')`; corsi completati mostrano l'etichetta "Completato" Dati da: GET /api/training/assignments (`api.getMyTraining()`) · PUT /api/training/assignments/{id} (`api.put`, body `{status}`) Confine: pannello `#tab-assignments` (riga ~257), contenuto in `#assignments-container` (~258) • U-TRAINING.5 — Tab "Compliance Formativa" Mostra: pulsante "Assegna Corso" in alto a destra; tabella dei membri dell'organizzazione: Nome, Email, Ruolo, Corsi Completati (completati/totali), "% Compliance Obbligatoria" con barra colorata (verde ≥80%, giallo ≥50%, rosso <50%). Stato vuoto: "Nessun membro trovato" Azioni: "Assegna Corso" → modale assegnazione (U-TRAINING.6) Dati da: GET /api/training/compliance-status (`api.getTrainingCompliance()`) Confine: pannello `#tab-compliance` (riga ~267), contenuto in `#compliance-container` (~271); il pulsante "Assegna Corso" NON ha id stabile • U-TRAINING.6 — Modale "Assegna Corso" Mostra: select "Corso" (`#assign-course-id`, popolata dal catalogo); campo "Scadenza" (`#assign-due-date`, date); elenco "Seleziona Utenti" con checkbox per membro (`input.assign-user-cb`, nome + ruolo), popolato dai dati del tab Compliance Azioni: "Assegna" → `doAssignCourse()` (richiede almeno un utente selezionato) → POST e ricarica compliance; se non esistono corsi, avviso "Nessun corso disponibile. Crea prima un corso." Dati da: POST /api/training/assign (`api.post('/training/assign', {course_id, user_ids, due_date})`) · catalogo corsi da GET /api/training/courses se non gia' caricato Confine: `#modal-overlay`; select `#assign-course-id` (riga ~687), data `#assign-due-date` (~691), checkbox con classe stabile `assign-user-cb` (~677) ⟦ FINE · U-TRAINING ⟧ ## ID STABILI MANCANTI (da aggiungere) | Elemento | Riga | Id proposto | |---|---|---| | Pulsante "+ Nuovo Corso" (header) | 234 | `nis2-training-btn-new-course` | | Contenitore tab bar `div.tab-nav` | 240 | `nis2-training-tabs` | | Bottone tab "Corsi" | 241 | `nis2-training-tab-btn-courses` | | Bottone tab "Le Mie Assegnazioni" | 242 | `nis2-training-tab-btn-assignments` | | Bottone tab "Compliance Formativa" | 243 | `nis2-training-tab-btn-compliance` | | Pulsante "Assegna Corso" (tab compliance) | 269 | `nis2-training-btn-assign-course` |