Results & CAPA
Audit scores, non-conformances, and corrective actions
JS
'); w.document.close(); } function toggleAudit(id){ var el=document.getElementById(id), arr=document.getElementById('au-arr-'+id.replace('au-','')); if(!el) return; el.style.display = el.style.display==='none' ? 'block' : 'none'; if(arr) arr.style.transform = el.style.display==='none' ? 'rotate(-90deg)' : ''; } /* ── CAPA Tab ── */ function renderCapa(){ var container = document.getElementById('main-content'); var capas = JSON.parse(localStorage.getItem('sc_capas')||'[]'); if(!capas.length){ container.innerHTML = '
' +'
πŸ“‹
' +'
No CAPAs yet
' +'
Complete an audit and create CAPAs from non-conformances in the Results tab.
'; return; } var open = capas.filter(function(c){ return c.status==='Open'; }).length; var inProg = capas.filter(function(c){ return c.status==='In Progress'; }).length; var closed = capas.filter(function(c){ return c.status==='Closed'; }).length; var html = '
'; html += '
Total CAPAs
'+capas.length+'
All created
'; html += '
Open
'+open+'
Require action
'; html += '
In Progress
'+inProg+'
Being resolved
'; html += '
Closed
'+closed+'
Resolved
'; html += '
'; html += '
'; html += '
All CAPAs
'; html += ''; capas.forEach(function(c, i){ var stsCls = c.status==='Closed'?'cs-closed':c.status==='In Progress'?'cs-capa':'cs-open'; html += ''; var clauseDisplay = ''+(c.clause||'')+''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; }); html += '
CAPA IDAudit / ClauseRoot CauseCorrective ActionOwnerDue DateStatus
'+(c.capaId||'CAPA-'+String(i+1).padStart(3,'0'))+'
'+(c.auditName||'')+'
'+clauseDisplay+'
'+(c.rootCause||'β€”')+''+(c.action||'β€”')+''+(c.owner||'β€”')+''+(c.targetDate||c.due||'β€”')+''+c.status+''; if(c.status!=='Closed'){ html += ''; } html += ''; html += '
'; container.innerHTML = html; } function closeCapa(idx){ var capas = JSON.parse(localStorage.getItem('sc_capas')||'[]'); if(capas[idx]) { capas[idx].status = 'Closed'; capas[idx].closedAt = new Date().toISOString(); } localStorage.setItem('sc_capas', JSON.stringify(capas)); renderCapa(); } function viewCapaDetail(idx){ var capas = JSON.parse(localStorage.getItem('sc_capas')||'[]'); var c = capas[idx]; if(!c) return; var m = document.getElementById('capa-detail-modal'); var b = document.getElementById('capa-detail-body'); function row(label, val){ return val ? '
'+label+'
'+val+'
' : ''; } var actions = ''; if(c.correctiveActions && c.correctiveActions.length){ actions = c.correctiveActions.map(function(a,i){ return '
'+(i+1)+'. '+a.action+''+(a.owner?' β€” '+a.owner:'')+(a.dueDate?' Β· Due: '+a.dueDate:'')+'
'; }).join(''); } else if(c.action){ actions = '
'+c.action+'
'; } var docs = ''; if(c.supportingDocs && c.supportingDocs.length){ docs = c.supportingDocs.map(function(d){ return '
'+d+'
'; }).join(''); } var stsCls = c.status==='Closed' ? 'cs-closed' : c.status==='In Progress' ? 'cs-capa' : 'cs-open'; b.innerHTML = '
' + row('CAPA ID', c.capaId||'β€”') + row('Status', ''+c.status+'') + row('Audit', c.auditName||'β€”') + row('Clause', c.clause||c.clauseId||'β€”') + row('Severity', c.severity||'β€”') + row('Created', c.createdAt ? new Date(c.createdAt).toLocaleDateString('en-CA') : 'β€”') + row('Owner', c.owner||'β€”') + row('Due Date', c.targetDate||c.due||'β€”') + row('Closed', c.closedAt ? new Date(c.closedAt).toLocaleDateString('en-CA') : 'β€”') + '
' + '
Finding / NC Description
' + '
'+( c.ncText||'β€”')+'
' + (c.rootCause ? '
Root Cause
'+c.rootCause+'
' : '') + (actions ? '
Corrective Actions
'+actions+'
' : '') + (c.preventiveAction ? '
Preventive Action
'+c.preventiveAction+'
' : '') + (docs ? '
Supporting Documents
'+docs+'
' : ''); m.style.display = 'flex'; } /* ── CAPA Form Navigation ── */ function openCapaForm(ncKey, capaId, ncRef, auditName, clauseId, severity, encodedText, encodedAuditUrl){ var pending = { ncKey: ncKey, capaId: capaId, ncRef: ncRef, auditName: auditName, clauseId: clauseId, severity: severity, ncText: decodeURIComponent(encodedText), auditUrl: decodeURIComponent(encodedAuditUrl) }; localStorage.setItem('sc_pending_capa', JSON.stringify(pending)); location.href = '/capa_form.html'; } /* ── Init ── */ (function(){ var u = JSON.parse(localStorage.getItem('sc_current_user')||'{}'); var ini = u.initials || '?'; var name = u.name || 'User'; var role = u.role || ''; document.getElementById('sb-av').textContent = ini; document.getElementById('sb-name').textContent = name; document.getElementById('sb-role').textContent = role; document.getElementById('tb-av').textContent = ini; // Auto-switch tab if ?tab=capa in URL var urlTab = new URLSearchParams(window.location.search).get('tab'); if(urlTab === 'capa') switchTab('capa'); })(); renderResults(); /* ── Custom grade override ── */ // Stored in sc_custom_grades (separate key, never overwritten by server sync) function _getCustomGrades() { try { return JSON.parse(localStorage.getItem('sc_custom_grades')||'{}'); } catch(e){ return {}; } } function _getCustomGrade(resultId) { return _getCustomGrades()[String(resultId)] || ''; } function openGradeModal(resultId) { var cur = _getCustomGrade(resultId); document.getElementById('grade-modal-rid').value = resultId; document.getElementById('grade-custom-input').value = cur; document.querySelectorAll('.grade-preset-btn').forEach(function(btn){ btn.style.background = btn.dataset.grade === cur ? 'var(--nv)' : ''; btn.style.color = btn.dataset.grade === cur ? '#fff' : ''; }); document.getElementById('grade-modal').style.display = 'flex'; } function closeGradeModal() { document.getElementById('grade-modal').style.display = 'none'; } function pickPresetGrade(grade) { document.getElementById('grade-custom-input').value = grade; document.querySelectorAll('.grade-preset-btn').forEach(function(btn){ btn.style.background = btn.dataset.grade === grade ? 'var(--nv)' : ''; btn.style.color = btn.dataset.grade === grade ? '#fff' : ''; }); } function saveCustomGrade() { var resultId = document.getElementById('grade-modal-rid').value; var grade = document.getElementById('grade-custom-input').value.trim(); var grades = _getCustomGrades(); if(grade) grades[String(resultId)] = grade; else delete grades[String(resultId)]; localStorage.setItem('sc_custom_grades', JSON.stringify(grades)); closeGradeModal(); renderResults(); } function clearCustomGrade() { document.getElementById('grade-custom-input').value = ''; document.querySelectorAll('.grade-preset-btn').forEach(function(btn){ btn.style.background = ''; btn.style.color = ''; }); }