[FEAT] A2 UI: wizard onboarding esteso (bilancio, autonomia/gruppo, categorie size-independent art.3 c.5, mission GV.OC, codice etico facoltativo, 3 criteri sotto-soglia c.9 b/c/d) + classificazione v2 lato server + disclaimer ACN 'autovalutazione preliminare'. Markup Bootstrap Italia/AGID
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
c5fce7bdf7
commit
717f9ac396
+119
-7
@@ -303,6 +303,48 @@
|
||||
<label class="active" for="annual-turnover">Fatturato Annuo EUR <span aria-hidden="true">*</span></label>
|
||||
<input type="number" id="annual-turnover" class="form-control" placeholder="Es. 15000000" min="0" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="active" for="total-balance">Totale di bilancio EUR</label>
|
||||
<input type="number" id="total-balance" class="form-control" placeholder="Es. 12000000 (facoltativo)" min="0">
|
||||
<small class="form-text text-muted">Terzo criterio dimensionale (Racc. 2003/361/CE): grande impresa se > 43M€ di totale di bilancio.</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="active" for="is-autonomous">Struttura dell'impresa</label>
|
||||
<select id="is-autonomous" class="form-select select-pdnd">
|
||||
<option value="1">Impresa autonoma</option>
|
||||
<option value="0">Parte di un gruppo (imprese collegate/associate)</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Se parte di un gruppo, le soglie vanno calcolate in modo consolidato (art. 6 Racc. 2003/361/CE).</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="active" for="size-independent-category">Categoria speciale NIS2 (a prescindere dalla dimensione)</label>
|
||||
<select id="size-independent-category" class="form-select select-pdnd">
|
||||
<option value="none">Nessuna delle seguenti</option>
|
||||
<option value="dns">Fornitore di servizi DNS</option>
|
||||
<option value="tld">Gestore di registri TLD</option>
|
||||
<option value="ixp">Fornitore IXP (Internet Exchange Point)</option>
|
||||
<option value="trust_qualified">Prestatore di servizi fiduciari qualificati</option>
|
||||
<option value="trust_unqualified">Prestatore di servizi fiduciari non qualificati</option>
|
||||
<option value="ecomm_public_network">Fornitore di reti pubbliche di comunicazione elettronica</option>
|
||||
<option value="ecomm_public_service">Fornitore di servizi di comunicazione elettronica accessibili al pubblico</option>
|
||||
<option value="domain_registrar">Fornitore di servizi di registrazione nomi di dominio</option>
|
||||
<option value="pa_central">Pubblica Amministrazione centrale</option>
|
||||
<option value="pa_regional_local">Pubblica Amministrazione regionale/locale</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Questi soggetti rientrano nell'ambito a prescindere dalle soglie (art. 3, comma 5, D.Lgs. 138/2024).</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="active" for="mission">Mission / contesto organizzativo</label>
|
||||
<textarea id="mission" class="form-control" rows="2" placeholder="Obiettivi, servizi critici e dipendenze principali (facoltativo)"></textarea>
|
||||
<small class="form-text text-muted">Alimenta il contesto organizzativo e il registro dei sistemi rilevanti (misura GV.OC).</small>
|
||||
</div>
|
||||
<div class="form-group mb-0">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="has-code-of-ethics" class="form-check-input">
|
||||
<label class="form-check-label" for="has-code-of-ethics">L'organizzazione ha adottato un codice etico</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">Facoltativo — buona prassi di governance, non richiesto dalla NIS2.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -384,6 +426,21 @@
|
||||
<div class="classification-result-desc" id="classification-desc"></div>
|
||||
</div>
|
||||
|
||||
<ul class="mb-4" id="classification-obligations" style="display:none"></ul>
|
||||
|
||||
<div class="card card-bg has-bkg-grey shadow mb-4" id="subthreshold-block">
|
||||
<div class="card-header bg-white"><h3 class="h6 mb-0">Sei sotto le soglie dimensionali? — criteri qualitativi (art. 3, comma 9, D.Lgs. 138/2024)</h3></div>
|
||||
<div class="card-body">
|
||||
<p class="small text-muted mb-3">Compila solo se la tua organizzazione <strong>non supera</strong> le soglie dimensionali: anche un soggetto sotto-soglia può essere individuato dall'Autorità (ACN) se ricorre uno di questi criteri.</p>
|
||||
<div class="form-check"><input type="checkbox" id="crit-sole" class="form-check-input"><label class="form-check-label" for="crit-sole">Siamo l'<strong>unico fornitore nazionale</strong> di un servizio essenziale per attività sociali o economiche fondamentali <em>(lett. b)</em></label></div>
|
||||
<div class="form-check"><input type="checkbox" id="crit-impact" class="form-check-input"><label class="form-check-label" for="crit-impact">Un'interruzione del nostro servizio avrebbe <strong>impatto significativo su sicurezza, incolumità o salute pubblica</strong> <em>(lett. c)</em></label></div>
|
||||
<div class="form-check"><input type="checkbox" id="crit-systemic" class="form-check-input"><label class="form-check-label" for="crit-systemic">Un'interruzione comporterebbe un <strong>rischio sistemico significativo</strong>, anche transfrontaliero <em>(lett. d)</em></label></div>
|
||||
<button class="btn btn-sm btn-outline-primary mt-3" type="button" onclick="runClassificationAndSummary()">Ricalcola classificazione</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" id="classification-disclaimer" role="alert" style="font-size:.85rem;display:none"></div>
|
||||
|
||||
<div class="voluntary-section" id="voluntary-section">
|
||||
<div class="voluntary-card" id="voluntary-card">
|
||||
<input type="checkbox" id="voluntary-checkbox" onchange="onVoluntaryChange(this.checked)" aria-label="Adesione volontaria alla NIS2">
|
||||
@@ -472,7 +529,10 @@
|
||||
visuraFile: null,
|
||||
company: {
|
||||
name: '', vat_number: '', fiscal_code: '', address: '', city: '',
|
||||
website: '', email: '', phone: '', sector: '', employee_count: '', annual_turnover: ''
|
||||
website: '', email: '', phone: '', sector: '', employee_count: '', annual_turnover: '',
|
||||
total_balance: '', is_autonomous: 1, size_independent_category: 'none',
|
||||
crit_sole_supplier: 0, crit_public_impact: 0, crit_systemic_risk: 0,
|
||||
mission: '', has_code_of_ethics: 0
|
||||
},
|
||||
profile: { full_name: '', role: '', phone: '' },
|
||||
classification: null,
|
||||
@@ -714,6 +774,11 @@
|
||||
wizardState.company.sector = sector;
|
||||
wizardState.company.employee_count = parseInt(employees);
|
||||
wizardState.company.annual_turnover = parseInt(turnover);
|
||||
wizardState.company.total_balance = parseInt(document.getElementById('total-balance').value) || 0;
|
||||
wizardState.company.is_autonomous = parseInt(document.getElementById('is-autonomous').value);
|
||||
wizardState.company.size_independent_category = document.getElementById('size-independent-category').value;
|
||||
wizardState.company.mission = document.getElementById('mission').value.trim();
|
||||
wizardState.company.has_code_of_ethics = document.getElementById('has-code-of-ethics').checked ? 1 : 0;
|
||||
goToStep(3);
|
||||
}
|
||||
|
||||
@@ -774,20 +839,43 @@
|
||||
}
|
||||
}
|
||||
|
||||
function runClassificationAndSummary() {
|
||||
async function runClassificationAndSummary() {
|
||||
const c = wizardState.company;
|
||||
const sector = c.sector;
|
||||
const employees = parseInt(c.employee_count) || 0;
|
||||
const turnover = parseInt(c.annual_turnover) || 0;
|
||||
const result = classifyLocally(sector, employees, turnover);
|
||||
// criteri qualitativi sotto-soglia (art. 3 c. 9) letti dai checkbox dello step 4
|
||||
c.crit_sole_supplier = (document.getElementById('crit-sole') || {}).checked ? 1 : 0;
|
||||
c.crit_public_impact = (document.getElementById('crit-impact') || {}).checked ? 1 : 0;
|
||||
c.crit_systemic_risk = (document.getElementById('crit-systemic') || {}).checked ? 1 : 0;
|
||||
|
||||
// Classificazione v2 lato server (single source of truth: Nis2ClassificationService)
|
||||
let result = null;
|
||||
try {
|
||||
const resp = await api.post('/organizations/classify', {
|
||||
sector: c.sector, employee_count: employees, annual_turnover_eur: turnover,
|
||||
total_balance_eur: parseInt(c.total_balance) || 0,
|
||||
is_autonomous: c.is_autonomous, size_independent_category: c.size_independent_category,
|
||||
crit_sole_supplier: c.crit_sole_supplier, crit_public_impact: c.crit_public_impact, crit_systemic_risk: c.crit_systemic_risk
|
||||
});
|
||||
if (resp && resp.success && resp.data) {
|
||||
const d = resp.data;
|
||||
result = { classification: d.entity_type, label: d.label, description: d.explanation,
|
||||
designation_basis: d.designation_basis, sub_threshold_candidate: !!d.sub_threshold_candidate,
|
||||
disclaimer: d.disclaimer || '', obligations: d.obligations || [] };
|
||||
}
|
||||
} catch (e) { /* fallback locale sotto */ }
|
||||
if (!result) {
|
||||
const r = classifyLocally(c.sector, employees, turnover);
|
||||
result = { classification: r.classification, label: r.label, description: r.description, obligations: [], disclaimer: '', sub_threshold_candidate: false };
|
||||
}
|
||||
wizardState.classification = result;
|
||||
|
||||
// Adesione volontaria: visibile solo per soggetti non in ambito
|
||||
wizardState.voluntaryCompliance = false;
|
||||
const voluntaryCheckbox = document.getElementById('voluntary-checkbox');
|
||||
if (voluntaryCheckbox) voluntaryCheckbox.checked = false;
|
||||
const voluntaryCard = document.getElementById('voluntary-card');
|
||||
if (voluntaryCard) voluntaryCard.classList.remove('checked');
|
||||
|
||||
const voluntarySection = document.getElementById('voluntary-section');
|
||||
if (result.classification === 'not_applicable') voluntarySection.classList.add('visible');
|
||||
else voluntarySection.classList.remove('visible');
|
||||
@@ -809,8 +897,24 @@
|
||||
iconEl.innerHTML = '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>';
|
||||
}
|
||||
|
||||
labelEl.textContent = result.label;
|
||||
descEl.textContent = result.description;
|
||||
// Etichetta + badge "autovalutazione preliminare"
|
||||
labelEl.textContent = (result.label || '') + ' — autovalutazione preliminare';
|
||||
descEl.textContent = result.description || '';
|
||||
|
||||
// Obblighi (dal server, citano il D.Lgs. 138/2024)
|
||||
const obEl = document.getElementById('classification-obligations');
|
||||
if (obEl) {
|
||||
if (result.obligations && result.obligations.length) {
|
||||
obEl.innerHTML = result.obligations.map(o => '<li>' + String(o).replace(/[<>]/g, '') + '</li>').join('');
|
||||
obEl.style.display = '';
|
||||
} else { obEl.style.display = 'none'; obEl.innerHTML = ''; }
|
||||
}
|
||||
// Disclaimer ACN (non vincolante)
|
||||
const disEl = document.getElementById('classification-disclaimer');
|
||||
if (disEl) {
|
||||
if (result.disclaimer) { disEl.textContent = result.disclaimer; disEl.style.display = ''; }
|
||||
else { disEl.style.display = 'none'; }
|
||||
}
|
||||
|
||||
const sectorSelect = document.getElementById('sector');
|
||||
const sectorOption = sectorSelect.querySelector('option[value="' + c.sector + '"]');
|
||||
@@ -860,6 +964,14 @@
|
||||
contact_email: c.email, contact_phone: c.phone, sector: c.sector,
|
||||
employee_count: parseInt(c.employee_count) || 0,
|
||||
annual_turnover_eur: parseInt(c.annual_turnover) || 0,
|
||||
total_balance_eur: parseInt(c.total_balance) || 0,
|
||||
is_autonomous: c.is_autonomous,
|
||||
size_independent_category: c.size_independent_category,
|
||||
crit_sole_supplier: c.crit_sole_supplier,
|
||||
crit_public_impact: c.crit_public_impact,
|
||||
crit_systemic_risk: c.crit_systemic_risk,
|
||||
mission: c.mission,
|
||||
has_code_of_ethics: c.has_code_of_ethics,
|
||||
voluntary_compliance: wizardState.voluntaryCompliance ? 1 : 0,
|
||||
full_name: p.full_name, phone: p.phone, country: 'IT',
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user