Open Findings
${open.length}
Critical/High Risk Open
${(byRisk['Critical'] || 0) + (byRisk['High'] || 0)}
`;
}
function renderCharts(all, open) {
const byStatus = all.reduce((acc, f) => { acc[f.status] = (acc[f.status] || 0) + 1; return acc; }, {});
renderChart('audit-status-chart', 'doughnut',
{ labels: Object.keys(byStatus), datasets: [{ data: Object.values(byStatus), backgroundColor: ['#fde68a', '#c4b5fd', '#dcfce7', '#e5e7eb'] }] },
{ responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } }
);
const byDept = open.reduce((acc, f) => { acc[f.dept] = (acc[f.dept] || 0) + 1; return acc; }, {});
renderChart('audit-dept-chart', 'bar',
{ labels: Object.keys(byDept), datasets: [{ label: 'Open Findings', data: Object.values(byDept), backgroundColor: 'rgba(7, 89, 133, 0.7)' }] },
{ responsive: true, maintainAspectRatio: false, indexAxis: 'y', plugins: { legend: { display: false } } }
);
}
const renderTable = (tbodyId, theadId, data, isMaster = false) => {
document.getElementById(theadId).innerHTML = `
| Risk | Description | Department | Owner | Due Date | Status | Actions |
|---|
`;
const tbody = document.getElementById(tbodyId);
tbody.innerHTML = data.map(f => {
const isOverdue = new Date(f.due) < new Date() && (f.status === 'Open' || f.status === 'In Progress');
const actionButtons = {
"Open": `
`,
"In Progress": `
`,
};
return `
| ${f.risk} |
${f.desc} | ${f.dept} | ${f.owner} |
${f.due}${isOverdue ? ' \u26A0' : ''} |
${f.status} |
${!isMaster ? (actionButtons[f.status] || '') : ``} |
`;
}).join('');
}
const renderFindingsTable = (data) => renderTable('audit-findings-table-body', 'audit-findings-table-head', data);
const renderMasterTable = (data) => renderTable('audit-master-table-body', 'audit-master-table-head', data, true);
// --- Data Management & Actions ---
window.auditAddFinding = function() {
findings.push({
id: Date.now(),
desc: document.getElementById('add-desc').value,
dept: document.getElementById('add-dept').value,
owner: document.getElementById('add-owner').value,
risk: document.getElementById('add-risk').value,
status: 'Open',
due: document.getElementById('add-due').value,
});
renderAll();
}
function handleActionClick(e) {
if (!e.target.matches('.action-btn')) return;
const id = parseInt(e.target.dataset.id);
const action = e.target.dataset.action;
if (action === 'delete') {
findings = findings.filter(f => f.id !== id);
} else {
const finding = findings.find(f => f.id === id);
if (finding) {
if(action === 'progress') finding.status = 'In Progress';
else if(action === 'remediate') finding.status = 'Remediated';
}
}
renderAll();
}
// --- Global & Utility ---
window.auditShowTab = id => {
document.querySelectorAll('.audit-main-tab-content, .audit-main-tab-button').forEach(el => el.classList.remove('active'));
document.getElementById(`audit-tab-${id}`).classList.add('active');
document.querySelector(`.audit-main-tab-button[onclick="auditShowTab('${id}')"]`).classList.add('active');
};
window.auditDownloadPDF = () => {
html2canvas(document.getElementById('audit-pdf-content'), { scale: 2 }).then(canvas => {
const pdf = new jspdf.jsPDF({ orientation: 'landscape', unit: 'pt', format: 'a4' });
pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 40, 40, pdf.internal.pageSize.getWidth() - 80, 0);
pdf.save('Audit_Findings_Dashboard.pdf');
});
};
initialize();
});