From ea5966eaeea38a020b22f8a0c14df0f68a74e465 Mon Sep 17 00:00:00 2001 From: DevEnv nis2-agile Date: Tue, 23 Jun 2026 21:12:36 +0200 Subject: [PATCH] [FIX] ARIA: mappa navigazione reale + contesto pagina (ticket #424) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARIA citava voci di menu inesistenti (es. 'A4 - Analisi e Valutazione del Rischio' / 'Fase 4.1 - Inventario') non corrispondenti alla barra laterale, e non sapeva da quale pagina scrivesse l'utente. - AIService::askWithRag: iniettata la mappa REALE della sidebar (navigationMapBlock, sincronizzata con common-bi.js) con regole che vietano di inventare menu/fasi/codici; aggiunto contesto pagina utente. - AiController::ask: accetta e passa il campo 'page' dal frontend. - common.js (ARIA FAB): invia la pagina corrente in /api/ai/ask. - Cache-buster common.js 20260623->20260623h su tutte le pagine. NB: questo commit include anche il fix #419 gia' applicato e live in common.js (email bug-reporter recuperata via api.getMe quando assente nelle claims JWT), finora non committato — incluso come backup. Co-Authored-By: Claude Opus 4.8 --- application/controllers/AiController.php | 4 ++ application/services/AIService.php | 39 ++++++++++++++++++- public/_app-bi-demo.html | 2 +- public/admin/index.html | 2 +- public/admin/organizations.html | 2 +- public/admin/users.html | 2 +- public/architecture.html | 2 +- public/assessment.html | 2 +- public/assets.html | 2 +- public/calendario.html | 2 +- public/companies.html | 2 +- public/competenze.html | 2 +- public/controlli-periodici.html | 2 +- public/cross-analysis.html | 2 +- public/dashboard.html | 2 +- public/guida.html | 2 +- public/incidents.html | 2 +- public/integrazioniext.html | 2 +- public/internal-audits.html | 2 +- public/isms.html | 2 +- public/js/common.js | 48 +++++++++++++++--------- public/kb.html | 2 +- public/login.html | 2 +- public/management-review.html | 2 +- public/misure-requisiti.html | 2 +- public/mktg-api-doc.html | 2 +- public/normative.html | 2 +- public/onboarding.html | 2 +- public/organigramma.html | 2 +- public/policies.html | 2 +- public/raci.html | 2 +- public/register.html | 2 +- public/reports.html | 2 +- public/review-schedule.html | 2 +- public/risks.html | 2 +- public/settings.html | 2 +- public/stakeholder-activities.html | 2 +- public/stakeholders.html | 2 +- public/supply-chain.html | 2 +- public/training.html | 2 +- public/version.json | 2 +- public/whistleblowing.html | 2 +- public/workflow.html | 2 +- 43 files changed, 112 insertions(+), 59 deletions(-) diff --git a/application/controllers/AiController.php b/application/controllers/AiController.php index 909f29a..d00f734 100644 --- a/application/controllers/AiController.php +++ b/application/controllers/AiController.php @@ -39,10 +39,14 @@ class AiController extends BaseController } $user = $this->getCurrentUser() ?? []; + // Pagina corrente (facoltativa) inviata dal frontend, per dare ad ARIA + // contesto sulla schermata da cui l'utente sta scrivendo (ticket #424). + $page = mb_substr(trim((string) $this->getParam('page', '')), 0, 120); $userContext = [ 'user_id' => $userId, 'organization_id' => $this->getCurrentOrgId(), // può essere null 'consulting_firm_id' => $user['consulting_firm_id'] ?? null, + 'page' => $page, ]; try { diff --git a/application/services/AIService.php b/application/services/AIService.php index f1e1729..8b6da4e 100644 --- a/application/services/AIService.php +++ b/application/services/AIService.php @@ -56,6 +56,30 @@ class AIService . "5. ENISA, NIST e ISO sono best practice di settore, NON fonti normative vincolanti: gli obblighi italiani derivano da Direttiva (UE) 2022/2555, D.Lgs. 138/2024 e Determinazioni ACN.\n"; } + /** + * Mappa REALE della barra laterale dell'applicazione, da iniettare nel + * system prompt dell'assistente ARIA cosi' che le indicazioni di navigazione + * usino i nomi ESATTI del menu e non vengano inventati moduli/fasi inesistenti. + * + * ⚠️ TENERE SINCRONIZZATA con la fonte di verita' della sidebar: + * public/js/common-bi.js (array navItems). Se aggiungi/rinomini una voce + * di menu li', aggiorna anche questo elenco. + * (Ticket #424, 2026-06-23: ARIA citava voci di menu inesistenti.) + */ + private function navigationMapBlock(): string + { + return "\n## MAPPA DELLA NAVIGAZIONE (barra laterale REALE dell'app)\n" + . "Sezione \"Principale\": Dashboard · Calendario · Compliance Journey · Gap Analysis · Misure e Requisiti · Modello SGSI (ISO 27001).\n" + . "Sezione \"Gestione\": Organigramma · Competenze · Rischi · Incidenti · Policy · Supply Chain · Stakeholder · Attivita' stakeholder · Segnalazioni · Normative · AI Cross-Analysis · Knowledge Base.\n" + . "Sezione \"Operativo\": Formazione · Inventario · Audit interni · Controlli periodici · Riesame di Direzione · Audit & Report.\n" + . "Sezione \"Sistema\": Guida all'uso · Impostazioni · Architettura · Simulazione Demo · Integrazioni Esterne.\n" + . "\nREGOLE DI NAVIGAZIONE (vincolanti):\n" + . "1. Quando indichi DOVE cliccare o COME raggiungere una funzione, usa ESCLUSIVAMENTE i nomi delle voci qui sopra, esattamente come scritti, indicando la sezione (es. \"nella sezione Operativo, voce Inventario\").\n" + . "2. NON inventare nomi di menu, \"fasi\", codici (es. \"A4\", \"Fase 4.1\") o moduli che NON compaiono in questo elenco.\n" + . "3. L'inventario degli asset si trova in: sezione Operativo > Inventario. La valutazione del rischio in: sezione Gestione > Rischi. Misure e requisiti in: sezione Principale > Misure e Requisiti.\n" + . "4. Se l'utente cerca una funzione che non corrisponde ad alcuna voce, dillo chiaramente e suggerisci la voce piu' vicina, senza inventarne una.\n"; + } + /** * Analizza risultati gap analysis e genera raccomandazioni */ @@ -702,9 +726,20 @@ PROMPT; error_log('[AIService::askWithRag] RAG failed, fallback diretto: ' . $e->getMessage()); } - $systemPrompt = "Sei un esperto consulente di cybersecurity NIS2 (EU 2022/2555) e D.Lgs. 138/2024.\n" + $systemPrompt = "Sei ARIA, l'assistente dell'applicazione NIS2 Agile, esperta di cybersecurity NIS2 (EU 2022/2555) e D.Lgs. 138/2024.\n" . "Rispondi in modo preciso e cita le fonti del contesto quando rilevanti.\n" - . $this->authoritativeSourcesBlock(); + . $this->authoritativeSourcesBlock() + . $this->navigationMapBlock(); + + // Pagina corrente dell'utente (se inviata dal frontend): aiuta ARIA a + // dare indicazioni contestuali invece di rimandare a moduli a caso. + $page = trim((string) ($userContext['page'] ?? '')); + if ($page !== '') { + $systemPrompt .= "\n## CONTESTO UTENTE\n" + . "L'utente si trova attualmente nella pagina: \"" . $page . "\". " + . "Tieni conto di dove si trova quando dai indicazioni di navigazione.\n"; + } + if (!empty($contextBlock)) { $systemPrompt .= "\n## Contesto documentale (knowledge base)\n" . $contextBlock . "\n\nQuando rispondi, cita esplicitamente i numeri tra parentesi quadre [1], [2], ... che corrispondono ai documenti del contesto."; diff --git a/public/_app-bi-demo.html b/public/_app-bi-demo.html index 70c88b8..136c08f 100644 --- a/public/_app-bi-demo.html +++ b/public/_app-bi-demo.html @@ -71,7 +71,7 @@ - + - + diff --git a/public/admin/organizations.html b/public/admin/organizations.html index 07c1f0e..35e8e1f 100644 --- a/public/admin/organizations.html +++ b/public/admin/organizations.html @@ -148,7 +148,7 @@ - + diff --git a/public/admin/users.html b/public/admin/users.html index e9dc3fd..967ac0b 100644 --- a/public/admin/users.html +++ b/public/admin/users.html @@ -170,7 +170,7 @@ - + diff --git a/public/architecture.html b/public/architecture.html index 85e326a..ec03b1f 100644 --- a/public/architecture.html +++ b/public/architecture.html @@ -1091,7 +1091,7 @@ - + diff --git a/public/assessment.html b/public/assessment.html index d9ffbe2..05bb31c 100644 --- a/public/assessment.html +++ b/public/assessment.html @@ -155,7 +155,7 @@ - + diff --git a/public/assets.html b/public/assets.html index 1c3deaa..1522bf9 100644 --- a/public/assets.html +++ b/public/assets.html @@ -362,7 +362,7 @@ - + diff --git a/public/calendario.html b/public/calendario.html index 3efad53..c5e4f1f 100644 --- a/public/calendario.html +++ b/public/calendario.html @@ -152,7 +152,7 @@ - + diff --git a/public/companies.html b/public/companies.html index 7f1dfa2..71494fd 100644 --- a/public/companies.html +++ b/public/companies.html @@ -350,7 +350,7 @@ - + diff --git a/public/competenze.html b/public/competenze.html index 5360822..a3edd7b 100644 --- a/public/competenze.html +++ b/public/competenze.html @@ -184,7 +184,7 @@ - + diff --git a/public/controlli-periodici.html b/public/controlli-periodici.html index 40aeef0..313df2f 100644 --- a/public/controlli-periodici.html +++ b/public/controlli-periodici.html @@ -137,7 +137,7 @@ - + diff --git a/public/cross-analysis.html b/public/cross-analysis.html index 51247c3..03859e3 100644 --- a/public/cross-analysis.html +++ b/public/cross-analysis.html @@ -383,7 +383,7 @@ - + diff --git a/public/dashboard.html b/public/dashboard.html index cdcd8a3..1ebbc1b 100644 --- a/public/dashboard.html +++ b/public/dashboard.html @@ -144,7 +144,7 @@ - + diff --git a/public/guida.html b/public/guida.html index beeff90..c79bdaf 100644 --- a/public/guida.html +++ b/public/guida.html @@ -1143,7 +1143,7 @@ - + diff --git a/public/incidents.html b/public/incidents.html index 54983a5..c8ec191 100644 --- a/public/incidents.html +++ b/public/incidents.html @@ -352,7 +352,7 @@ - + diff --git a/public/integrazioniext.html b/public/integrazioniext.html index 20ba344..4142a37 100644 --- a/public/integrazioniext.html +++ b/public/integrazioniext.html @@ -912,7 +912,7 @@ curl -H "X-API-Key: nis2_TUA_CHIAVE" \ - + - + diff --git a/public/isms.html b/public/isms.html index 65956fa..0576237 100644 --- a/public/isms.html +++ b/public/isms.html @@ -185,7 +185,7 @@ - + diff --git a/public/js/common.js b/public/js/common.js index 7ccd9ed..ec94b0f 100644 --- a/public/js/common.js +++ b/public/js/common.js @@ -952,22 +952,33 @@ function switchLang(lang) { // ── 1. Inietta il bug-reporter Nexus ──────────────────────────────────── if (!window.__nexusWidgetLoaded) { window.__nexusWidgetLoaded = true; - var s = document.createElement('script'); - s.src = 'js/bug-reporter.js?v=20260613'; - s.async = true; - s.dataset.product = 'NIS2'; - s.dataset.tenantId = '7'; - s.dataset.apiUrl = 'https://agilehub.agile.software'; - // X-API-Key per il gateway ticket AgileHub. Senza, il widget riceve 401 - // "Header X-API-Key o Authorization: Bearer richiesto". Valorizzare con la - // chiave NIS2 emessa da AgileHub (prodotto NIS2, tenant 7). Inietta via - // window.NEXUS_WIDGET_API_KEY oppure hardcoda qui quando disponibile. - s.dataset.apiKey = (window.NEXUS_WIDGET_API_KEY || 'nis2_ak_dev_109b4bb54557c4ab3bdbed45'); - s.dataset.userName = userName; - s.dataset.userEmail = userEmail; - s.dataset.userRole = userRole; - s.dataset.lang = 'it'; - document.body.appendChild(s); + // [#419 fix] L'email NON e' nelle claims del JWT NIS2 (claims.email vuoto) → se manca, + // recuperala dal profilo via api.getMe() PRIMA di iniettare. Senza email il widget crea + // ticket con richiedente vuoto → invisibili in "Le mie segnalazioni". Fallback safe = prec. + var _injectBugReporter = function (email, name) { + var s = document.createElement('script'); + s.src = 'js/bug-reporter.js?v=20260623'; + s.async = true; + s.dataset.product = 'NIS2'; + s.dataset.tenantId = '7'; + s.dataset.apiUrl = 'https://agilehub.agile.software'; + s.dataset.apiKey = (window.NEXUS_WIDGET_API_KEY || 'nis2_ak_dev_109b4bb54557c4ab3bdbed45'); + s.dataset.userName = name || userName || ''; + s.dataset.userEmail = email || userEmail || ''; + s.dataset.userRole = userRole; + s.dataset.lang = 'it'; + document.body.appendChild(s); + }; + if (userEmail) { + _injectBugReporter(userEmail, userName); + } else if (typeof api !== 'undefined' && typeof api.getMe === 'function') { + api.getMe().then(function (r) { + var u = (r && r.data) || {}; + _injectBugReporter(u.email || '', u.full_name || u.name || ''); + }).catch(function () { _injectBugReporter('', ''); }); + } else { + _injectBugReporter('', ''); + } } // ── 2. AI Chat FAB viola "ARIA" ───────────────────────────────────────── @@ -1118,6 +1129,9 @@ function switchLang(lang) { // AI nativa NIS2 (RAG con KB Multi-Livello già attivo) var t = localStorage.getItem('nis2_access_token') || ''; var orgId = localStorage.getItem('nis2_org_id') || ''; + // Pagina corrente: aiuta ARIA a dare indicazioni contestuali (ticket #424). + var pageName = (document.title || '').replace(/\s*[—|·-]\s*NIS2.*$/i, '').trim() + || (location.pathname.split('/').pop() || ''); var r = await fetch('/api/ai/ask', { method: 'POST', headers: { @@ -1125,7 +1139,7 @@ function switchLang(lang) { 'Authorization': t ? ('Bearer ' + t) : '', 'X-Organization-Id': orgId }, - body: JSON.stringify({ question: text, history: history }) + body: JSON.stringify({ question: text, history: history, page: pageName }) }); var resp = await r.json(); var answer = (resp && resp.success && resp.data diff --git a/public/kb.html b/public/kb.html index d365f16..9864d3c 100644 --- a/public/kb.html +++ b/public/kb.html @@ -151,7 +151,7 @@ - + diff --git a/public/login.html b/public/login.html index 4bce150..5577472 100644 --- a/public/login.html +++ b/public/login.html @@ -95,7 +95,7 @@ - + - + diff --git a/public/misure-requisiti.html b/public/misure-requisiti.html index 2b1e3b0..5cbe123 100644 --- a/public/misure-requisiti.html +++ b/public/misure-requisiti.html @@ -168,7 +168,7 @@ - + diff --git a/public/mktg-api-doc.html b/public/mktg-api-doc.html index bccef8b..39d13ac 100644 --- a/public/mktg-api-doc.html +++ b/public/mktg-api-doc.html @@ -426,6 +426,6 @@ curl https://nis2.agile.software/api/services/status - + diff --git a/public/normative.html b/public/normative.html index d5a255e..a1996d8 100644 --- a/public/normative.html +++ b/public/normative.html @@ -113,7 +113,7 @@ - + diff --git a/public/onboarding.html b/public/onboarding.html index 5b41384..e8fdaf8 100644 --- a/public/onboarding.html +++ b/public/onboarding.html @@ -495,7 +495,7 @@ - + - + diff --git a/public/policies.html b/public/policies.html index 0636287..c5f98a6 100644 --- a/public/policies.html +++ b/public/policies.html @@ -334,7 +334,7 @@ - + diff --git a/public/raci.html b/public/raci.html index 97d3290..140a43e 100644 --- a/public/raci.html +++ b/public/raci.html @@ -132,7 +132,7 @@ - + diff --git a/public/register.html b/public/register.html index 418eb17..9a6389d 100644 --- a/public/register.html +++ b/public/register.html @@ -269,7 +269,7 @@ - + - + diff --git a/public/review-schedule.html b/public/review-schedule.html index d1d3743..847b838 100644 --- a/public/review-schedule.html +++ b/public/review-schedule.html @@ -142,7 +142,7 @@ - + diff --git a/public/risks.html b/public/risks.html index b147b07..d8c8093 100644 --- a/public/risks.html +++ b/public/risks.html @@ -495,7 +495,7 @@ - + diff --git a/public/settings.html b/public/settings.html index 66b47d9..3ec091a 100644 --- a/public/settings.html +++ b/public/settings.html @@ -689,7 +689,7 @@ - + diff --git a/public/stakeholder-activities.html b/public/stakeholder-activities.html index fef385d..9cadfe7 100644 --- a/public/stakeholder-activities.html +++ b/public/stakeholder-activities.html @@ -195,7 +195,7 @@ - + diff --git a/public/stakeholders.html b/public/stakeholders.html index 3b37836..e8e21f2 100644 --- a/public/stakeholders.html +++ b/public/stakeholders.html @@ -192,7 +192,7 @@ - + diff --git a/public/supply-chain.html b/public/supply-chain.html index 5879fb1..ae03593 100644 --- a/public/supply-chain.html +++ b/public/supply-chain.html @@ -478,7 +478,7 @@ - + diff --git a/public/training.html b/public/training.html index 8d8b265..578193e 100644 --- a/public/training.html +++ b/public/training.html @@ -293,7 +293,7 @@ - + diff --git a/public/version.json b/public/version.json index 5361e80..ff7c24b 100644 --- a/public/version.json +++ b/public/version.json @@ -1 +1 @@ -{"version": "1.24.8", "build": "2026-06-23-v1.24.8", "date": "2026-06-23", "changelog": "Inventario beni — Sottoclassi: ora dalla finestra di modifica del bene si possono anche RINOMINARE e RIMUOVERE le sottoclassi create dalla propria organizzazione (prima si potevano solo aggiungere). I due nuovi link «✎ rinomina» e «🗑 rimuovi» stanno accanto a «+ aggiungi». Le sottoclassi di sistema (predefinite) restano protette e non si modificano/eliminano. Rimuovendo una sottoclasse, i beni che la usavano restano semplicemente senza sottoclasse. Risolto ticket #426."} +{"version": "1.24.9", "build": "2026-06-23-v1.24.9", "date": "2026-06-23", "changelog": "Assistente ARIA — Ora ARIA conosce la barra laterale REALE dell'app e, quando ti spiega dove cliccare, usa i nomi esatti delle voci di menu (es. «sezione Operativo > Inventario») senza piu' inventare moduli o «fasi» inesistenti. Inoltre riceve la pagina da cui stai scrivendo, cosi' da darti indicazioni piu' pertinenti al punto in cui ti trovi. Risolto ticket #424."} diff --git a/public/whistleblowing.html b/public/whistleblowing.html index bb013cb..77b87ac 100644 --- a/public/whistleblowing.html +++ b/public/whistleblowing.html @@ -208,7 +208,7 @@ - + diff --git a/public/workflow.html b/public/workflow.html index 4ad0ca3..aa8a21f 100644 --- a/public/workflow.html +++ b/public/workflow.html @@ -322,7 +322,7 @@ - +