[FEAT] ARIA allineata all'Help contestuale della pagina + pageId canonico (Fase A)

Lamentela: "AI non allineata con help online e contesto". Causa: ARIA e Help
erano due fonti separate (Help=help.js, ARIA=KB) → divergevano.
Fix (fonte unica = help.js, zero duplicazione):
- help.js: getContextText(pageId) (testo piano dell'help della pagina) + detectPageId().
- common.js: invia page_id (canonico) + page_help (stesso testo del pannello "?") a /api/ai/ask.
- AiController: passa page_id/page_help in userContext.
- AIService::askWithRag: inietta "GUIDA CONTESTUALE DELLA PAGINA" nel system prompt
  (ARIA si allinea all'help mostrato all'utente, non lo contraddice) + usa page_id
  per il trigger relevanceGrid (più robusto del title).
Smoke: ARIA cita "registro dei rischi" dall'help + "Gestione > Rischi". Cache-buster
common.js/help.js -> 20260624e (39 pagine).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-06-24 08:30:10 +02:00
co-authored by Claude Opus 4.8
parent 3b9f30a890
commit bafc305503
43 changed files with 138 additions and 74 deletions
+10 -1
View File
@@ -1132,6 +1132,15 @@ function switchLang(lang) {
// Pagina corrente: aiuta ARIA a dare indicazioni contestuali (ticket #424).
var pageName = (document.title || '').replace(/\s*[—|·-]\s*NIS2.*$/i, '').trim()
|| (location.pathname.split('/').pop() || '');
// pageId canonico + testo dell'help "?" della pagina → ARIA risponde
// ALLINEATA a ciò che l'utente vede nella guida (fonte unica help.js).
var pageId = '', pageHelp = '';
try {
if (typeof HelpSystem !== 'undefined') {
if (HelpSystem.detectPageId) pageId = HelpSystem.detectPageId() || '';
if (HelpSystem.getContextText) pageHelp = HelpSystem.getContextText(pageId) || '';
}
} catch (e) { /* help non disponibile → ARIA prosegue senza */ }
var r = await fetch('/api/ai/ask', {
method: 'POST',
headers: {
@@ -1139,7 +1148,7 @@ function switchLang(lang) {
'Authorization': t ? ('Bearer ' + t) : '',
'X-Organization-Id': orgId
},
body: JSON.stringify({ question: text, history: history, page: pageName })
body: JSON.stringify({ question: text, history: history, page: pageName, page_id: pageId, page_help: pageHelp })
});
var resp = await r.json();
var answer = (resp && resp.success && resp.data
+39 -1
View File
@@ -1859,6 +1859,42 @@ const HelpSystem = (function () {
'</span>';
}
/**
* Versione testo-piano del contenuto di help della pagina (per ARIA).
* Stessa fonte del pannello "?" → l'AI resta ALLINEATA all'help mostrato all'utente.
* @param {string} [pageId] - se assente, rileva la pagina corrente.
* @param {number} [maxLen] - cap caratteri (default 2200).
* @returns {string} testo piano, '' se la pagina non ha help.
*/
function _stripHtml(s) {
return String(s || '')
.replace(/<[^>]+>/g, '')
.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&')
.replace(/&ge;/g, '≥').replace(/&le;/g, '≤').replace(/&nbsp;/g, ' ')
.replace(/\s+/g, ' ').trim();
}
function getContextText(pageId, maxLen) {
if (!pageId) pageId = _detectPage();
var d = pageId && _helpContent[pageId];
if (!d) return '';
var out = [];
if (d.title) out.push(_stripHtml(d.title).replace(/^Guida\s*-\s*/i, '').trim());
if (d.intro) out.push(_stripHtml(d.intro));
(d.sections || []).forEach(function (s) {
var items = (s.items || []).map(_stripHtml).join(' ');
out.push(('• ' + (s.heading || '') + ': ' + items).trim());
});
if (d.references && d.references.length) {
out.push('Riferimenti: ' + d.references.map(_stripHtml).join('; '));
}
var txt = out.join('\n');
maxLen = maxLen || 2200;
if (txt.length > maxLen) txt = txt.slice(0, maxLen) + '…';
return txt;
}
/** pageId canonico della pagina corrente (o '' se non mappata). */
function detectPageId() { return _detectPage() || ''; }
/**
* Inizializza il sistema di help: inietta gli stili e aggiunge il pulsante help
* nell'header della pagina corrente (se applicabile).
@@ -1883,7 +1919,9 @@ const HelpSystem = (function () {
showHelp: showHelp,
addHelpButton: addHelpButton,
inlineHelp: inlineHelp,
getAvailablePages: getAvailablePages
getAvailablePages: getAvailablePages,
getContextText: getContextText, // testo help della pagina per ARIA (allineamento)
detectPageId: detectPageId // pageId canonico corrente
};
})();