Files
nis2-agile/public/forgot-password.html
DevEnv nis2-agileandClaude Opus 4.8 8a0bc5ca39 [FEAT] Gestione Rischi: tabella 12 colonne (inerente+residuo) — integrazione prototipo "Modulo Azioni" (Simon)
Integra la spec di Simon per la pagina Rischi con le correzioni normative
(parere nis2-expert + iso-27001-expert) applicate come eccezioni motivate:
- 12 colonne: Codice (link → Misure e Requisiti), Descrizione (RISCHIO DEFAULT =
  cfg_nis2_rischi.risk_descr), VALUTAZIONE RISCHIO INERENTE (P/I/PxI), Alert,
  Rischio Inerente, VALUTAZIONE RISCHIO RESIDUO (P/I/PxI), Esito residuo, Rischio Residuo.
- Editing INLINE di Probabilità/Impatto (1-5), salvataggio automatico.
- Alert a bande (15-25 Prioritaria / 9-14 Critica / 3-8 Necessaria / 1-2 Suggerita)
  → popup azioni + "crea azione" (tabella requisito_actions, seme Modulo Azioni).
- Eccezione #1: il rischio RESIDUO NON aggiorna in automatico lo STATO DI CONFORMITÀ del
  requisito (piani distinti). Col.11 "Esito residuo" = flag Rivalutare/Adeguato; conformità esplicita.
- Eccezione #3: banda 13-14 inclusa in "Critica" (9-14). "Danno €" → "Impatto" (1-5).
  Soglie = metodo interno (ISO 27001 §6.1.2), non obbligo (help/UI lo dichiarano).

Backend: mig 067; derivedList (+risk_descr +residuo), setState (+residuo), requisitoActions list/create.
help.js + i18n IT/EN. Cache-buster api/i18n/help 20260730a. Prossima mig=068.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 10:49:52 +02:00

126 lines
5.6 KiB
HTML

<!DOCTYPE html>
<html lang="it">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>Password dimenticata - NIS2 Agile</title>
<!-- Bootstrap Italia v2.18.1 — self-hostato da /vendor (MAI CDN). Variante "-bi" della forgot-password.html. -->
<link rel="stylesheet" href="vendor/bootstrap-italia/dist/css/bootstrap-italia.min.css">
<style>
body { min-height: 100vh; display: flex; align-items: center; background: #f5f6f7; }
.auth-wrap { width: 100%; max-width: 460px; margin: 0 auto; padding: 24px; }
.auth-brand { text-align: center; margin-bottom: 24px; }
.auth-brand .brand-name { font-weight: 700; font-size: 1.5rem; color: #06c; }
.auth-brand .brand-name span { color: #17324d; }
.auth-brand .brand-sub { color: #5a6772; font-size: .9rem; margin-top: 4px; }
.ico { width: 1em; height: 1em; fill: currentColor; vertical-align: -.125em; }
</style>
<!-- PWA:start -->
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#0066CC">
<link rel="icon" type="image/png" sizes="32x32" href="/assets/icons/favicon-32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16.png">
<link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="NIS2 Agile">
<meta name="application-name" content="NIS2 Agile">
<script src="/js/pwa.js?v=20260614" defer></script>
<!-- PWA:end -->
</head>
<body>
<main class="auth-wrap">
<div class="auth-brand">
<div class="brand-name">NIS2 <span>Agile</span></div>
<div class="brand-sub">Reimposta la tua password</div>
</div>
<div class="card card-bg has-bkg-grey shadow">
<div class="card-body">
<h1 class="h4 mb-3">Password dimenticata</h1>
<div class="alert alert-danger d-none" id="err" role="alert"></div>
<div class="alert alert-success d-none" id="ok" role="status"></div>
<p class="text-muted small mb-4">Inserisci l'indirizzo email associato al tuo account. Ti invieremo un link valido 30 minuti per impostare una nuova password.</p>
<form id="forgot-form" novalidate>
<div class="form-group">
<label for="email" class="active">Indirizzo Email</label>
<input type="email" id="email" name="email" class="form-control"
autocomplete="email" required aria-describedby="err">
</div>
<button type="submit" class="btn btn-primary w-100 mt-2" id="submit-btn">
Invia link
</button>
</form>
</div>
</div>
<p class="text-center mt-3 mb-0">
<a href="login.html">
<svg class="ico me-1" aria-hidden="true"><use href="vendor/bootstrap-italia/dist/svg/sprites.svg#it-arrow-left"></use></svg>
Torna al login
</a>
</p>
</main>
<script src="vendor/bootstrap-italia/dist/js/bootstrap-italia.bundle.min.js"></script>
<script>
if (window.bootstrap && bootstrap.loadFonts) { bootstrap.loadFonts('vendor/bootstrap-italia/dist/fonts'); }
</script>
<!-- Stessa logica della forgot-password.html: ZERO modifiche backend -->
<script src="js/api.js?v=20260730a"></script>
<script>
const form = document.getElementById('forgot-form');
const err = document.getElementById('err');
const ok = document.getElementById('ok');
const btn = document.getElementById('submit-btn');
form.addEventListener('submit', async function(e) {
e.preventDefault();
err.classList.add('d-none');
ok.classList.add('d-none');
const email = document.getElementById('email').value.trim();
if (!email) {
err.textContent = 'Inserisci l\'indirizzo email.';
err.classList.remove('d-none');
return;
}
btn.disabled = true;
btn.textContent = 'Invio in corso...';
try {
const res = await fetch('/api/auth/forgot-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: email })
});
const data = await res.json();
if (res.status === 429) {
err.textContent = data.message || 'Troppe richieste. Riprova più tardi.';
err.classList.remove('d-none');
} else if (data.success) {
ok.textContent = data.message;
ok.classList.remove('d-none');
form.style.display = 'none';
} else {
err.textContent = data.message || 'Errore. Riprova.';
err.classList.remove('d-none');
}
} catch (e) {
err.textContent = 'Errore di connessione al server.';
err.classList.remove('d-none');
} finally {
btn.disabled = false;
btn.textContent = 'Invia link';
}
});
</script>
</body>
</html>