[FEAT] Visura auto-fill, adesione volontaria, modulo NCR/CAPA

1. Fix auto-fill visura: mapping corretto suggested_sector e employees_range,
   indicatori visivi verdi sui campi auto-compilati, fatturato sempre manuale
2. Adesione volontaria: colonna voluntary_compliance, checkbox in onboarding
   step 5 quando not_applicable, toggle in settings, reset su ri-classificazione
3. Modulo NCR/CAPA: NonConformityController con 10 endpoint API,
   tabelle non_conformities + capa_actions, generazione NCR dai gap assessment,
   predisposizione integrazione SistemiG.agile (webhook + sync)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-18 08:12:57 +01:00
co-authored by Claude Opus 4.6
parent 517cab76a5
commit 4e3408e9f6
11 changed files with 985 additions and 21 deletions
+67
View File
@@ -99,6 +99,25 @@
</div>
<div class="card-body" id="ai-analysis-content"></div>
</div>
<!-- Generate NCR from gaps -->
<div class="card mb-24" id="ncr-generation-card">
<div class="card-header">
<h3>Non Conformita'</h3>
</div>
<div class="card-body" style="text-align:center; padding:32px;">
<p style="color:var(--gray-600); margin-bottom:16px; font-size:0.875rem;">
Genera automaticamente le non conformita' (NCR) dai gap identificati nell'assessment.
I controlli con stato "Non Implementato" saranno classificati come <strong>major</strong>,
quelli "Parzialmente Implementato" come <strong>minor</strong>.
</p>
<button class="btn btn-primary" id="btn-generate-ncr" onclick="generateNCRs()">
<svg viewBox="0 0 20 20" fill="currentColor" width="16" height="16"><path fill-rule="evenodd" d="M6 2a2 2 0 00-2 2v12a2 2 0 002 2h8a2 2 0 002-2V7.414A2 2 0 0015.414 6L12 2.586A2 2 0 0010.586 2H6zm5 6a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V8z" clip-rule="evenodd"/></svg>
Genera Non Conformita' dai Gap
</button>
<div id="ncr-generation-result" style="display:none; margin-top:16px;"></div>
</div>
</div>
</div>
</div>
</main>
@@ -590,6 +609,54 @@
showNotification('Errore di connessione.', 'error');
}
}
// ── Generate NCRs from Assessment Gaps ─────────────────────
async function generateNCRs() {
if (!currentAssessmentId) {
showNotification('Nessun assessment selezionato.', 'warning');
return;
}
const btn = document.getElementById('btn-generate-ncr');
btn.disabled = true;
btn.innerHTML = '<div class="spinner" style="width:16px;height:16px;border-width:2px;margin:0;"></div> Generazione in corso...';
try {
const result = await api.createNCRsFromAssessment(currentAssessmentId);
const resultDiv = document.getElementById('ncr-generation-result');
resultDiv.style.display = 'block';
if (result.success && result.data) {
const total = result.data.total || 0;
if (total > 0) {
const majorCount = (result.data.created || []).filter(n => n.severity === 'major').length;
const minorCount = total - majorCount;
resultDiv.innerHTML = `
<div style="background:var(--secondary-bg); border:1px solid #bbf7d0; border-radius:var(--border-radius); padding:16px; text-align:left;">
<strong style="color:#15803d;">${total} non conformita' generate con successo</strong>
<div style="margin-top:8px; font-size:0.8125rem; color:var(--gray-600);">
${majorCount > 0 ? `<span style="color:var(--danger); font-weight:600;">${majorCount} major</span> ` : ''}
${minorCount > 0 ? `<span style="color:var(--warning); font-weight:600;">${minorCount} minor</span>` : ''}
</div>
</div>`;
showNotification(`${total} non conformita' generate.`, 'success');
} else {
resultDiv.innerHTML = '<p style="color:var(--gray-500);">Nessun gap trovato — tutti i controlli sono implementati.</p>';
showNotification('Nessun gap trovato.', 'info');
}
btn.style.display = 'none';
} else {
resultDiv.innerHTML = `<p style="color:var(--danger);">${escapeHtml(result.message || 'Errore nella generazione.')}</p>`;
showNotification(result.message || 'Errore.', 'error');
btn.disabled = false;
btn.innerHTML = '<svg viewBox="0 0 20 20" fill="currentColor" width="16" height="16"><path fill-rule="evenodd" d="M6 2a2 2 0 00-2 2v12a2 2 0 002 2h8a2 2 0 002-2V7.414A2 2 0 0015.414 6L12 2.586A2 2 0 0010.586 2H6zm5 6a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V8z" clip-rule="evenodd"/></svg> Genera Non Conformita\' dai Gap';
}
} catch (e) {
showNotification('Errore di connessione.', 'error');
btn.disabled = false;
btn.innerHTML = '<svg viewBox="0 0 20 20" fill="currentColor" width="16" height="16"><path fill-rule="evenodd" d="M6 2a2 2 0 00-2 2v12a2 2 0 002 2h8a2 2 0 002-2V7.414A2 2 0 0015.414 6L12 2.586A2 2 0 0010.586 2H6zm5 6a1 1 0 10-2 0v2H7a1 1 0 100 2h2v2a1 1 0 102 0v-2h2a1 1 0 100-2h-2V8z" clip-rule="evenodd"/></svg> Genera Non Conformita\' dai Gap';
}
}
</script>
</body>
</html>