Legacy org setup page was sending annual_turnover instead of annual_turnover_eur to classify and create endpoints. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
378 lines
21 KiB
HTML
378 lines
21 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="it">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Configura Organizzazione - NIS2 Agile</title>
|
|
<link rel="stylesheet" href="css/style.css">
|
|
</head>
|
|
<body>
|
|
<div class="app-layout">
|
|
<!-- Sidebar -->
|
|
<aside class="sidebar" id="sidebar"></aside>
|
|
|
|
<!-- Main Content -->
|
|
<main class="main-content">
|
|
<header class="content-header">
|
|
<h2>Configurazione Organizzazione</h2>
|
|
</header>
|
|
|
|
<div class="content-body">
|
|
<div class="grid-2" style="max-width:960px;">
|
|
<!-- Form -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3>Dati Aziendali</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<form id="org-form" novalidate>
|
|
<div class="form-group">
|
|
<label class="form-label" for="company-name">Ragione Sociale <span class="required">*</span></label>
|
|
<input type="text" id="company-name" class="form-input"
|
|
placeholder="Es. Acme S.r.l." required>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="vat-number">Partita IVA</label>
|
|
<input type="text" id="vat-number" class="form-input"
|
|
placeholder="IT12345678901" maxlength="16">
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label class="form-label" for="sector">Settore <span class="required">*</span></label>
|
|
<select id="sector" class="form-select" required>
|
|
<option value="">-- Seleziona settore --</option>
|
|
<optgroup label="Settori ad Alta Criticita' (Allegato I)">
|
|
<option value="energy_electricity">Energia - Elettricita'</option>
|
|
<option value="energy_district_heating">Energia - Teleriscaldamento</option>
|
|
<option value="energy_oil">Energia - Petrolio</option>
|
|
<option value="energy_gas">Energia - Gas</option>
|
|
<option value="energy_hydrogen">Energia - Idrogeno</option>
|
|
<option value="transport_air">Trasporti - Aereo</option>
|
|
<option value="transport_rail">Trasporti - Ferroviario</option>
|
|
<option value="transport_water">Trasporti - Marittimo/Fluviale</option>
|
|
<option value="transport_road">Trasporti - Stradale</option>
|
|
<option value="banking">Banche</option>
|
|
<option value="financial_markets">Infrastrutture Mercati Finanziari</option>
|
|
<option value="health">Sanita'</option>
|
|
<option value="drinking_water">Acqua Potabile</option>
|
|
<option value="waste_water">Acque Reflue</option>
|
|
<option value="digital_infrastructure">Infrastruttura Digitale</option>
|
|
<option value="ict_service_management">Gestione Servizi ICT (B2B)</option>
|
|
<option value="public_administration">Pubblica Amministrazione</option>
|
|
<option value="space">Spazio</option>
|
|
</optgroup>
|
|
<optgroup label="Altri Settori Critici (Allegato II)">
|
|
<option value="postal_courier">Servizi Postali e Corrieri</option>
|
|
<option value="waste_management">Gestione Rifiuti</option>
|
|
<option value="chemicals">Fabbricazione Prodotti Chimici</option>
|
|
<option value="food">Produzione e Distribuzione Alimentare</option>
|
|
<option value="manufacturing_medical">Fabbricazione - Dispositivi Medici</option>
|
|
<option value="manufacturing_computers">Fabbricazione - Computer/Elettronica</option>
|
|
<option value="manufacturing_electrical">Fabbricazione - Apparecchiature Elettriche</option>
|
|
<option value="manufacturing_machinery">Fabbricazione - Macchinari</option>
|
|
<option value="manufacturing_vehicles">Fabbricazione - Autoveicoli</option>
|
|
<option value="manufacturing_transport">Fabbricazione - Altri Mezzi di Trasporto</option>
|
|
<option value="digital_providers">Fornitori Servizi Digitali</option>
|
|
<option value="research">Organizzazioni di Ricerca</option>
|
|
</optgroup>
|
|
<optgroup label="Altro">
|
|
<option value="other">Altro Settore</option>
|
|
</optgroup>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label class="form-label" for="employee-count">Numero Dipendenti <span class="required">*</span></label>
|
|
<input type="number" id="employee-count" class="form-input"
|
|
placeholder="Es. 150" min="1" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label" for="annual-turnover">Fatturato Annuo (EUR) <span class="required">*</span></label>
|
|
<input type="number" id="annual-turnover" class="form-input"
|
|
placeholder="Es. 15000000" min="0" required>
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary btn-lg w-full mt-16" id="save-btn">
|
|
Salva e Continua
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Classification Preview -->
|
|
<div>
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h3>Classificazione NIS2</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="classification-preview not-applicable" id="classification-preview">
|
|
<div class="classification-label">In Attesa</div>
|
|
<p class="classification-desc">
|
|
Compila i dati aziendali per ottenere la classificazione automatica
|
|
secondo i criteri della Direttiva NIS2 (UE) 2022/2555.
|
|
</p>
|
|
</div>
|
|
|
|
<div class="mt-24" id="classification-details" style="display:none;">
|
|
<h4 style="font-size:0.875rem; margin-bottom:12px; color:var(--gray-700);">Dettagli Classificazione</h4>
|
|
<div id="classification-info" style="font-size:0.8125rem; color:var(--gray-600); line-height:1.7;"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mt-24">
|
|
<div class="card-header">
|
|
<h3>Criteri di Classificazione</h3>
|
|
</div>
|
|
<div class="card-body" style="font-size:0.8125rem; color:var(--gray-600); line-height:1.7;">
|
|
<p><strong>Soggetti Essenziali:</strong></p>
|
|
<ul style="padding-left:20px; margin-bottom:12px;">
|
|
<li>Settori Allegato I con ≥ 250 dipendenti o fatturato ≥ 50M EUR</li>
|
|
<li>Alcuni soggetti designati indipendentemente dalle dimensioni</li>
|
|
</ul>
|
|
<p><strong>Soggetti Importanti:</strong></p>
|
|
<ul style="padding-left:20px; margin-bottom:12px;">
|
|
<li>Settori Allegato I/II con ≥ 50 dipendenti o fatturato ≥ 10M EUR</li>
|
|
<li>Non qualificati come essenziali</li>
|
|
</ul>
|
|
<p><strong>Non Applicabile:</strong></p>
|
|
<ul style="padding-left:20px;">
|
|
<li>Organizzazioni sotto le soglie minime</li>
|
|
<li>Settori non coperti dalla direttiva</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<script src="js/api.js"></script>
|
|
<script src="js/common.js"></script>
|
|
<script>
|
|
// ── Auth check ───────────────────────────────────────────
|
|
if (!checkAuth()) throw new Error('Not authenticated');
|
|
loadSidebar();
|
|
|
|
// ── Auto-classify on input change ────────────────────────
|
|
const sectorSelect = document.getElementById('sector');
|
|
const employeeInput = document.getElementById('employee-count');
|
|
const turnoverInput = document.getElementById('annual-turnover');
|
|
|
|
const debouncedClassify = debounce(autoClassify, 500);
|
|
|
|
sectorSelect.addEventListener('change', debouncedClassify);
|
|
employeeInput.addEventListener('input', debouncedClassify);
|
|
turnoverInput.addEventListener('input', debouncedClassify);
|
|
|
|
async function autoClassify() {
|
|
const sector = sectorSelect.value;
|
|
const employees = parseInt(employeeInput.value) || 0;
|
|
const turnover = parseInt(turnoverInput.value) || 0;
|
|
|
|
if (!sector || employees <= 0) {
|
|
resetClassification();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const result = await api.classifyEntity({
|
|
sector: sector,
|
|
employee_count: employees,
|
|
annual_turnover_eur: turnover
|
|
});
|
|
|
|
if (result.success && result.data) {
|
|
updateClassificationUI(result.data);
|
|
} else {
|
|
// Fallback: classificazione locale
|
|
const localResult = classifyLocally(sector, employees, turnover);
|
|
updateClassificationUI(localResult);
|
|
}
|
|
} catch (e) {
|
|
// Fallback: classificazione locale
|
|
const localResult = classifyLocally(sector, employees, turnover);
|
|
updateClassificationUI(localResult);
|
|
}
|
|
}
|
|
|
|
function classifyLocally(sector, employees, turnover) {
|
|
// Settori Allegato I (alta criticita')
|
|
const annexI = [
|
|
'energy_electricity', 'energy_district_heating', 'energy_oil', 'energy_gas',
|
|
'energy_hydrogen', 'transport_air', 'transport_rail', 'transport_water',
|
|
'transport_road', 'banking', 'financial_markets', 'health', 'drinking_water',
|
|
'waste_water', 'digital_infrastructure', 'ict_service_management',
|
|
'public_administration', 'space'
|
|
];
|
|
|
|
// Settori Allegato II
|
|
const annexII = [
|
|
'postal_courier', 'waste_management', 'chemicals', 'food',
|
|
'manufacturing_medical', 'manufacturing_computers', 'manufacturing_electrical',
|
|
'manufacturing_machinery', 'manufacturing_vehicles', 'manufacturing_transport',
|
|
'digital_providers', 'research'
|
|
];
|
|
|
|
const isAnnexI = annexI.includes(sector);
|
|
const isAnnexII = annexII.includes(sector);
|
|
const isLarge = employees >= 250 || turnover >= 50000000;
|
|
const isMedium = employees >= 50 || turnover >= 10000000;
|
|
|
|
if (isAnnexI && isLarge) {
|
|
return {
|
|
classification: 'essential',
|
|
label: 'Soggetto Essenziale',
|
|
description: 'La vostra organizzazione rientra tra i soggetti essenziali ai sensi della Direttiva NIS2, in quanto opera in un settore ad alta criticita\' (Allegato I) e supera le soglie dimensionali per la classificazione come grande impresa.'
|
|
};
|
|
} else if ((isAnnexI || isAnnexII) && isMedium) {
|
|
return {
|
|
classification: 'important',
|
|
label: 'Soggetto Importante',
|
|
description: 'La vostra organizzazione rientra tra i soggetti importanti ai sensi della Direttiva NIS2. Siete tenuti a rispettare gli obblighi di sicurezza e di notifica degli incidenti, con un regime di vigilanza ex post.'
|
|
};
|
|
} else if (sector === 'other' || (!isAnnexI && !isAnnexII)) {
|
|
return {
|
|
classification: 'not_applicable',
|
|
label: 'Non Applicabile',
|
|
description: 'In base ai dati forniti, la vostra organizzazione non sembra rientrare nell\'ambito di applicazione della Direttiva NIS2. Consigliamo comunque di verificare con le autorita\' competenti.'
|
|
};
|
|
} else {
|
|
return {
|
|
classification: 'not_applicable',
|
|
label: 'Sotto le Soglie',
|
|
description: 'La vostra organizzazione opera in un settore coperto dalla NIS2 ma non raggiunge le soglie dimensionali minime (50 dipendenti o 10M EUR di fatturato). Potreste comunque essere designati dalle autorita\' nazionali.'
|
|
};
|
|
}
|
|
}
|
|
|
|
function updateClassificationUI(data) {
|
|
const preview = document.getElementById('classification-preview');
|
|
const details = document.getElementById('classification-details');
|
|
const info = document.getElementById('classification-info');
|
|
|
|
const classification = data.classification || data.type || 'not_applicable';
|
|
const label = data.label || classification;
|
|
const description = data.description || data.explanation || '';
|
|
|
|
// Reset classes
|
|
preview.className = 'classification-preview';
|
|
|
|
if (classification === 'essential' || classification === 'essenziale') {
|
|
preview.classList.add('essential');
|
|
preview.innerHTML = `
|
|
<div class="classification-label">Soggetto Essenziale</div>
|
|
<p class="classification-desc">${escapeHtml(description) || 'Obblighi completi NIS2 - Vigilanza ex ante.'}</p>
|
|
`;
|
|
} else if (classification === 'important' || classification === 'importante') {
|
|
preview.classList.add('important');
|
|
preview.innerHTML = `
|
|
<div class="classification-label">Soggetto Importante</div>
|
|
<p class="classification-desc">${escapeHtml(description) || 'Obblighi NIS2 - Vigilanza ex post.'}</p>
|
|
`;
|
|
} else {
|
|
preview.classList.add('not-applicable');
|
|
preview.innerHTML = `
|
|
<div class="classification-label">${escapeHtml(label)}</div>
|
|
<p class="classification-desc">${escapeHtml(description) || 'Non rientra nell\'ambito NIS2.'}</p>
|
|
`;
|
|
}
|
|
|
|
if (data.details || data.obligations) {
|
|
details.style.display = 'block';
|
|
let detailsHtml = '';
|
|
if (data.obligations && data.obligations.length > 0) {
|
|
detailsHtml += '<p><strong>Obblighi principali:</strong></p><ul style="padding-left:20px;">';
|
|
data.obligations.forEach(o => {
|
|
detailsHtml += `<li>${escapeHtml(o)}</li>`;
|
|
});
|
|
detailsHtml += '</ul>';
|
|
}
|
|
if (data.details) {
|
|
detailsHtml += `<p>${escapeHtml(data.details)}</p>`;
|
|
}
|
|
info.innerHTML = detailsHtml;
|
|
}
|
|
}
|
|
|
|
function resetClassification() {
|
|
const preview = document.getElementById('classification-preview');
|
|
preview.className = 'classification-preview not-applicable';
|
|
preview.innerHTML = `
|
|
<div class="classification-label">In Attesa</div>
|
|
<p class="classification-desc">
|
|
Compila i dati aziendali per ottenere la classificazione automatica
|
|
secondo i criteri della Direttiva NIS2 (UE) 2022/2555.
|
|
</p>
|
|
`;
|
|
document.getElementById('classification-details').style.display = 'none';
|
|
}
|
|
|
|
// ── Form Submit ──────────────────────────────────────────
|
|
document.getElementById('org-form').addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
|
|
const companyName = document.getElementById('company-name').value.trim();
|
|
const vatNumber = document.getElementById('vat-number').value.trim();
|
|
const sector = sectorSelect.value;
|
|
const employees = parseInt(employeeInput.value) || 0;
|
|
const turnover = parseInt(turnoverInput.value) || 0;
|
|
|
|
// Validazione
|
|
if (!companyName) {
|
|
showNotification('Inserisci la ragione sociale.', 'warning');
|
|
return;
|
|
}
|
|
if (!sector) {
|
|
showNotification('Seleziona un settore.', 'warning');
|
|
return;
|
|
}
|
|
if (employees <= 0) {
|
|
showNotification('Inserisci il numero di dipendenti.', 'warning');
|
|
return;
|
|
}
|
|
if (turnover <= 0) {
|
|
showNotification('Inserisci il fatturato annuo.', 'warning');
|
|
return;
|
|
}
|
|
|
|
const saveBtn = document.getElementById('save-btn');
|
|
saveBtn.disabled = true;
|
|
saveBtn.textContent = 'Salvataggio in corso...';
|
|
|
|
try {
|
|
const result = await api.createOrganization({
|
|
name: companyName,
|
|
vat_number: vatNumber,
|
|
sector: sector,
|
|
employee_count: employees,
|
|
annual_turnover_eur: turnover
|
|
});
|
|
|
|
if (result.success && result.data) {
|
|
// Imposta l'organizzazione come attiva
|
|
api.setOrganization(result.data.id || result.data.organization_id);
|
|
showNotification('Organizzazione creata con successo!', 'success');
|
|
|
|
setTimeout(() => {
|
|
window.location.href = 'dashboard.html';
|
|
}, 1000);
|
|
} else {
|
|
showNotification(result.message || 'Errore nella creazione.', 'error');
|
|
}
|
|
} catch (err) {
|
|
showNotification('Errore di connessione al server.', 'error');
|
|
} finally {
|
|
saveBtn.disabled = false;
|
|
saveBtn.textContent = 'Salva e Continua';
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|