Files
nis2-agile/docs/mappa-logica/07_supply-chain.md
T
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

83 lines
11 KiB
Markdown

# MAPPA-LOGICA · Sicurezza Supply Chain
⟦ INIZIO · U-SUPPLYCHAIN · Videata "Sicurezza Supply Chain" ⟧
File: public/supply-chain.html · URL: /supply-chain.html · Scopo: anagrafica e valutazione di sicurezza dei fornitori (Art. 21.2.d NIS2 — sicurezza della catena di approvvigionamento): KPI di rischio, valutazione interna a punteggio, invio questionario self-assessment al fornitore, import CSV, gestione categorie e template questionari · Profili: tutti gli utenti autenticati dell'organizzazione (voce sidebar "Supply Chain"); azioni di scrittura soggette ai permessi backend (org_admin, compliance_manager, consultant)
• U-SUPPLYCHAIN.1 — Intestazione pagina e azioni globali
Mostra: titolo "Sicurezza Supply Chain"; 4 pulsanti: "Categorie", "Template", "Importa", "+ Nuovo Fornitore"
Azioni: "Categorie" → modale gestione categorie (U-SUPPLYCHAIN.9); "Template" → modale template questionari read-only (U-SUPPLYCHAIN.10); "Importa" → modale import CSV (U-SUPPLYCHAIN.8); "+ Nuovo Fornitore" → modale creazione (U-SUPPLYCHAIN.5)
Dati da: nessun endpoint (statici); "Categorie"/"+ Nuovo Fornitore" precaricano GET /api/supply-chain/categories
Confine: `header.content-header` (righe ~374-382); i 4 pulsanti NON hanno id stabile
• U-SUPPLYCHAIN.2 — KPI Risk Overview (4 card)
Mostra: 4 card numeriche: "Totale Fornitori" (`#stat-total`), "Critici Non Conformi" (`#stat-critical`, rosso: fornitori con criticita' critical/high e requisiti sicurezza non soddisfatti), "Contratti in Scadenza" (`#stat-expiring`, giallo: contratti attivi che scadono entro 90 giorni), "Score Rischio Medio" (`#stat-avg-score`, verde, formato N/100)
Azioni: nessuna (sola lettura, calcolo client-side in `updateRiskOverview()`)
Dati da: GET /api/supply-chain/list (`api.listSuppliers()`) — i KPI sono calcolati lato client sull'elenco completo
Confine: `#risk-overview` (riga ~386); valori `#stat-total` (~388), `#stat-critical` (~392), `#stat-expiring` (~396), `#stat-avg-score` (~400)
• U-SUPPLYCHAIN.3 — Grafico "Distribuzione Criticita'"
Mostra: card con istogramma a 4 barre: Critico (rosso), Alto (arancione), Medio (giallo), Basso (verde), con conteggio sopra ogni barra
Azioni: nessuna (sola lettura, `updateCriticalityChart()`)
Dati da: GET /api/supply-chain/list (stesso payload della lista, aggregato client-side)
Confine: `#criticality-chart` (riga ~411); barre `#chart-bar-critical|high|medium|low` e valori `#chart-val-critical|high|medium|low` (righe ~413-426)
• U-SUPPLYCHAIN.4 — Filtri ed elenco fornitori
Mostra: 2 select filtro: "Tutte le Criticita'" (`#filter-criticality`: Critico, Alto, Medio, Basso) e "Tutti gli Stati" (`#filter-status`: Attivo, In Revisione, Sospeso, Terminato). Tabella con colonne: Nome (link), Tipo Servizio, Criticita' (badge), Score Rischio (barra + valore colorato 0-100), Stato (badge), Ultima Valutazione, Azioni (occhio=visualizza, matita=modifica, checklist blu=valutazione interna, busta=invia questionario al fornitore). Stato vuoto: "Nessun fornitore trovato"
Azioni: filtri → `applyFilters()` (filtro client-side, nessuna nuova chiamata); click nome/occhio → dettaglio (U-SUPPLYCHAIN.6); matita → modale modifica; checklist → modale valutazione (U-SUPPLYCHAIN.7); busta → invio questionario (U-SUPPLYCHAIN.11)
Dati da: GET /api/supply-chain/list (`api.listSuppliers()`)
Confine: `#suppliers-list-section` (riga ~433); filtri `#filter-criticality` (~435) e `#filter-status` (~442); tabella renderizzata in `#suppliers-table` (~452)
• U-SUPPLYCHAIN.5 — Modale "Nuovo Fornitore" / "Modifica Fornitore"
Mostra: form `#supplier-form` con campi: Nome Fornitore* (`#form-name`), Partita IVA (`#form-vat`), Referente (`#form-contact-name`), Email Contatto (`#form-contact-email`), Tipo Servizio* (`#form-service-type`), Criticita' (`#form-criticality`: Basso/Medio/Alto/Critico), Categoria fornitore (`#form-category`, select con categorie preset + personalizzate), Descrizione Servizio (`#form-service-desc`), Inizio Contratto (`#form-contract-start`), Scadenza Contratto (`#form-contract-expiry`), Note (`#form-notes`)
Azioni: "Annulla" → chiude; "Aggiungi Fornitore"/"Salva Modifiche" → `saveSupplier(id)` (validazione: nome e tipo servizio obbligatori)
Dati da: POST /api/supply-chain/create (`api.createSupplier`) · PUT /api/supply-chain/{id} (`api.updateSupplier`) · categorie da GET /api/supply-chain/categories (`api.getSupplierCategories`) · in modifica precarico via GET /api/supply-chain/{id}
Confine: `#modal-overlay` (modale common.js); form `#supplier-form` (riga ~934)
• U-SUPPLYCHAIN.6 — Vista dettaglio fornitore
Mostra: link "← Torna all'elenco"; nome fornitore; badge criticita', stato e Conforme/Non Conforme; pulsanti "Valuta Fornitore", "Invia questionario", "Modifica"; card "Informazioni Fornitore" (Nome, Partita IVA, Referente, Email mailto, Tipo Servizio, Criticita', Inizio/Scadenza Contratto); card opzionali "Descrizione Servizio" e "Note"; gauge circolare "Score Rischio" su 100 con testo "Requisiti di sicurezza soddisfatti" (score ≥70) / "non soddisfatti" / "Nessuna valutazione effettuata"; card "Valutazione" (Ultima Valutazione, Prossima Valutazione, Requisiti Sicurezza Soddisfatti/Non Soddisfatti, Creato il)
Azioni: "Valuta Fornitore" → U-SUPPLYCHAIN.7; "Invia questionario" → U-SUPPLYCHAIN.11; "Modifica" → U-SUPPLYCHAIN.5; "Torna all'elenco" → `backToList()`
Dati da: GET /api/supply-chain/{id} (`api.getSupplier(id)`)
Confine: `#supplier-detail-view` (riga ~462, contenuto renderizzato via `renderSupplierDetail()`, righe ~720-880)
• U-SUPPLYCHAIN.7 — Modale "Valutazione Fornitore" (assessment interno a 10 domande)
Mostra: 10 domande pesate con opzioni Si'/Parziale/No (pill cliccabili): certificazioni ISO 27001/SOC2 (peso 2), SLA sicurezza (1.5), piano incident response (2), crittografia dati in transito e a riposo (2), audit periodici (1.5), controlli di accesso (1), backup e disaster recovery (1.5), conformita' GDPR (1), formazione sicurezza del personale (1), gestione vulnerabilita' (1.5); box "Score Rischio Calcolato" aggiornato live (`#assessment-live-score`, Si'=100, Parziale=50, No=0, media pesata)
Azioni: click opzione → `selectAnswer()` + ricalcolo live; "Salva Valutazione" → `submitAssessment(supplierId)` (richiede almeno una risposta); aggiorna score, last_assessment_date e conformita' del fornitore
Dati da: POST /api/supply-chain/{id}/assess (`api.assessSupplier(id, {assessment_responses})`)
Confine: `#modal-overlay`; domande in `#assessment-questions` (riga ~1079), preview score `#assessment-score-preview` (~1082) e valore `#assessment-live-score` (~1084)
• U-SUPPLYCHAIN.8 — Modale "Importa fornitori" (CSV)
Mostra: istruzioni colonne CSV (name obbligatoria; vat_number, contact_email, contact_name, service_type, criticality, category_slug, external_ref — upsert per external_ref); input file `#importFile`; textarea incolla CSV `#importCsv`; area risultato `#importResult` (creati/aggiornati/saltati + dettagli righe scartate); pulsante `#importRunBtn` "Importa"
Azioni: scelta file → contenuto copiato nella textarea; "Importa" → `runSupplierImport()` (max 1000 righe, separatori virgola o punto e virgola); a fine import ricarica la lista
Dati da: POST /api/supply-chain/import (`api.importSuppliers(rows)`)
Confine: overlay dedicato `#importModalDyn` (creato dinamicamente da `openImportModal()`, righe ~1206-1243); elementi `#importFile` (~1225), `#importCsv` (~1226), `#importResult` (~1228), `#importRunBtn` (~1232)
• U-SUPPLYCHAIN.9 — Modale "Categorie fornitore"
Mostra: elenco categorie in `#categories-list-body` con badge "Preset" (di sistema, non modificabili) o "Personalizzata" (con pulsanti Modifica/Elimina); pulsante "+ Nuova categoria"; sotto-form con Nome categoria* (`#cat-name`) e Descrizione (`#cat-desc`)
Azioni: "+ Nuova categoria"/"Modifica" → form categoria → `saveCategory(id)`; "Elimina" → conferma browser → `removeCategory(id)` (fallisce se categoria in uso)
Dati da: GET /api/supply-chain/categories · POST /api/supply-chain/categories (`api.createSupplierCategory`) · PUT /api/supply-chain/categories/{id} (`api.updateSupplierCategory`) · DELETE /api/supply-chain/categories/{id} (`api.deleteSupplierCategory`)
Confine: `#modal-overlay`; lista `#categories-list-body` (riga ~1343); form `#cat-name` (~1359), `#cat-desc` (~1363)
• U-SUPPLYCHAIN.10 — Modale "Template questionari" (sola lettura)
Mostra: elenco template questionari dell'organizzazione: nome, badge Default/Attivo, categoria, numero domande, versione; dettaglio template con elenco domande numerate e badge: riferimento normativo (nis2_ref), tipo domanda (Si/Parziale/No, Scelta singola/multipla, Scala 1-5, Testo, Numero, Allegato), Peso, Obbligatoria, "Solo alta criticita"; avviso "Sola lettura. L'editor delle domande sara' disponibile in una prossima versione"
Azioni: "Vedi domande" → dettaglio template; "← Torna ai template" → elenco; "Chiudi" → chiude
Dati da: GET /api/supply-chain/templates (`api.getQuestionnaireTemplates()`) · GET /api/supply-chain/templates/{id} (`api.getQuestionnaireTemplate(id)`)
Confine: `#modal-overlay` (contenuto renderizzato in `#modal-overlay .modal-body`, funzioni `openTemplatesModal`/`renderTemplatesModal`/`renderTemplateDetail`, righe ~1414-1485); nessun id proprio sugli elementi interni
• U-SUPPLYCHAIN.11 — Invio questionario self-assessment al fornitore
Mostra: prompt browser nativo "Invia il questionario di sicurezza a {fornitore}. Email del fornitore (lascia vuoto per usare il contatto in anagrafica)"; dopo l'invio, notifica di successo e secondo prompt con il link del questionario (valido 30 giorni, inoltrabile manualmente)
Azioni: il pulsante invoca `sendSupplierQuestionnaire(id, nome)` che apre il prompt email; conferma → chiamata API e secondo prompt con il link; annulla → nessuna azione
Dati da: POST /api/supply-chain/{id}/send-questionnaire (`api.sendSupplierQuestionnaire(id, email)`)
Confine: nessun elemento DOM proprio (usa `window.prompt`; funzione righe ~1183-1203); attivabile dal pulsante busta in tabella (~684) e dal pulsante "Invia questionario" nel dettaglio (~757)
⟦ FINE · U-SUPPLYCHAIN ⟧
## ID STABILI MANCANTI (da aggiungere)
| Elemento | Riga | Id proposto |
|---|---|---|
| Pulsante "Categorie" (header) | 377 | `nis2-supply-chain-btn-categories` |
| Pulsante "Template" (header) | 378 | `nis2-supply-chain-btn-templates` |
| Pulsante "Importa" (header) | 379 | `nis2-supply-chain-btn-import` |
| Pulsante "+ Nuovo Fornitore" (header) | 380 | `nis2-supply-chain-btn-new` |
| Card grafico "Distribuzione Criticita'" (`div.card.mb-24`, usata anche da viewSupplier/backToList via selettore di classe) | 406 | `nis2-supply-chain-chart-card` |
| Pulsanti dettaglio "Valuta Fornitore" / "Invia questionario" / "Modifica" (HTML dinamico) | 753-764 | `nis2-supply-chain-detail-btn-assess` / `-send-questionnaire` / `-edit` |