[UX+SEC] Eccellenza pre-audit: idle timeout, loading states, i18n, UX polish

- common.js: idle session timeout 30min con avviso countdown 5min prima del logout
- common.js: checkAuth() attiva automaticamente il monitor di inattività
- api.js: messaggi errore connessione usano i18n (IT/EN) tramite I18n.t()
- risks.html: saveRisk() e aiSuggest() con setButtonLoading durante salvataggio
- risks.html: deleteRisk() ricarica la matrice se si è in matrix view
- incidents.html: createIncident() con setButtonLoading durante registrazione
- policies.html: savePolicy() e saveAIGeneratedPolicy() con setButtonLoading
- policies.html: banner AI-draft con pulsante X per dismissione

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile
2026-02-20 12:25:52 +01:00
co-authored by Claude Sonnet 4.6
parent a56401f5a9
commit 59fad22c0e
5 changed files with 134 additions and 5 deletions
+14 -2
View File
@@ -520,12 +520,16 @@
${policy.ai_generated ? '<span class="badge badge-info" style="margin-left:6px;">AI Generata</span>' : ''}
</div>
${policy.ai_generated && (policy.status === 'draft' || policy.status === 'review') ? `
<div style="background:#fff7ed; border:1px solid #fed7aa; border-radius:var(--border-radius); padding:12px 16px; margin-top:12px; display:flex; gap:10px; align-items:flex-start;">
<div id="ai-draft-warning-${policy.id}" style="background:#fff7ed; border:1px solid #fed7aa; border-radius:var(--border-radius); padding:12px 16px; margin-top:12px; display:flex; gap:10px; align-items:flex-start; position:relative;">
<svg viewBox="0 0 20 20" fill="#c2410c" width="18" height="18" style="flex-shrink:0; margin-top:2px;"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>
<div>
<div style="flex:1;">
<strong style="color:#c2410c; font-size:0.875rem;">Bozza AI — Revisione obbligatoria prima dell'approvazione</strong>
<p style="font-size:0.78rem; color:#92400e; margin:4px 0 0;">Questo documento e' stato generato automaticamente da un sistema AI. Prima di approvarlo, verificare che sia conforme alle procedure interne, al quadro normativo applicabile e alla realta' specifica dell'organizzazione. L'AI puo' produrre contenuti imprecisi o incompleti.</p>
</div>
<button onclick="document.getElementById('ai-draft-warning-${policy.id}').remove();" title="Chiudi" aria-label="Chiudi avviso"
style="flex-shrink:0;background:none;border:none;cursor:pointer;color:#c2410c;padding:2px;line-height:1;opacity:0.7;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=0.7">
<svg width="16" height="16" viewBox="0 0 20 20" fill="currentColor"><path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/></svg>
</button>
</div>` : ''}
</div>
<div class="detail-header-actions">
@@ -699,6 +703,8 @@
next_review_date: document.getElementById('form-review-date').value || null,
};
const btn = document.querySelector('#modal-overlay .btn-primary');
setButtonLoading(btn, true);
try {
let result;
if (id) {
@@ -715,9 +721,11 @@
viewPolicy(id);
}
} else {
setButtonLoading(btn, false);
showNotification(result.message || 'Errore nel salvataggio.', 'error');
}
} catch (e) {
setButtonLoading(btn, false);
showNotification('Errore di connessione.', 'error');
}
}
@@ -868,6 +876,8 @@
ai_generated: 1,
};
const btn = document.querySelector('#modal-overlay .btn-primary');
setButtonLoading(btn, true);
try {
const result = await api.createPolicy(data);
if (result.success) {
@@ -876,9 +886,11 @@
aiGeneratedData = null;
loadPolicies();
} else {
setButtonLoading(btn, false);
showNotification(result.message || 'Errore nel salvataggio.', 'error');
}
} catch (e) {
setButtonLoading(btn, false);
showNotification('Errore di connessione.', 'error');
}
}