From 5ff58aecebe5becd21457803b620b3bed48b46bf Mon Sep 17 00:00:00 2001 From: DevEnv nis2-agile Date: Sun, 31 May 2026 08:17:05 +0200 Subject: [PATCH] =?UTF-8?q?[FEAT]=20Supply=20chain:=20bottone=20'Invia=20q?= =?UTF-8?q?uestionario'=20al=20fornitore=20(finding=20review=20CISO=20?= =?UTF-8?q?=F0=9F=94=B4C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Il backend sendQuestionnaire/questionnaire-status esisteva ma la UI non lo esponeva (guida prometteva una funzione non azionabile). Aggiunti: - bottone 'Invia questionario' in lista (azioni) e in dettaglio fornitore - funzione sendSupplierQuestionnaire (chiede email opzionale, mostra il link sq_ generato) - api.js: sendSupplierQuestionnaire + getSupplierQuestionnaireStatus Distinto 'Valuta (interna)' da 'Invia questionario' (link esterno). E2E prod: 201 + link generato. Co-Authored-By: Claude Opus 4.8 (1M context) --- public/js/api.js | 4 +++- public/supply-chain.html | 28 +++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/public/js/api.js b/public/js/api.js index 5c2de9e..bafe57d 100644 --- a/public/js/api.js +++ b/public/js/api.js @@ -269,7 +269,9 @@ class NIS2API { importAssets(data) { return this.post('/assets/import', data); } // P2 import CMDB/CSV getControlsMonitoring() { return this.get('/audit/controlsMonitoring'); } getAcnRequirements() { return this.get('/audit/acnRequirements'); } // requisiti ACN per org - updateAcnRequirement(id, status, note) { return this.put(`/audit/acnRequirements/${id}`, { status, evidence_note: note }); } // P1 continuous control monitoring (JWT) + updateAcnRequirement(id, status, note) { return this.put(`/audit/acnRequirements/${id}`, { status, evidence_note: note }); } + sendSupplierQuestionnaire(id, email) { return this.post(`/supply-chain/${id}/send-questionnaire`, email ? { email } : {}); } // self-assessment fornitore (link esterno) + getSupplierQuestionnaireStatus(id) { return this.get(`/supply-chain/${id}/questionnaire-status`); } // P1 continuous control monitoring (JWT) // ═══════════════════════════════════════════════════════════════════ // Audit diff --git a/public/supply-chain.html b/public/supply-chain.html index 450b95d..92421a3 100644 --- a/public/supply-chain.html +++ b/public/supply-chain.html @@ -661,9 +661,12 @@ - + `; @@ -1130,6 +1133,29 @@ } } + // ── Invio questionario self-assessment al fornitore (link esterno) ── + async function sendSupplierQuestionnaire(supplierId, supplierName) { + const def = ''; + const email = prompt('Invia il questionario di sicurezza a "' + (supplierName || 'fornitore') + + '".\nEmail del fornitore (lascia vuoto per usare il contatto in anagrafica):', def); + if (email === null) return; // annullato + try { + const result = await api.sendSupplierQuestionnaire(supplierId, email.trim() || null); + if (result.success) { + const link = result.data?.link; + showNotification('Questionario inviato' + (result.data?.sent_to ? ' a ' + result.data.sent_to : '') + '.', 'success'); + if (link) { + // mostra anche il link generato (utile se l'email non parte / per invio manuale) + prompt('Link del questionario (valido 30 giorni) — puoi inoltrarlo manualmente:', link); + } + } else { + showNotification(result.message || 'Errore nell\'invio del questionario.', 'error'); + } + } catch (e) { + showNotification('Errore di connessione.', 'error'); + } + } + // ── Init ──────────────────────────────────────────────── loadSuppliers();