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
-
+