Error: Could not load required libraries.
';
return;
}
let iso_statusChart, iso_ncChart;
const TABS = ['dashboardTab', 'dataInputTab'];
let iso_currentTabIndex = 0;
const STATUS_CONFIG = {
Certified: { color: '#10b981' },
'At Risk': { color: '#f97316' },
'Pending Audit': { color: '#3b82f6' },
Expired: { color: '#ef4444' }
};
const sampleData = [
{ standard: "ISO 9001:2015", scope: "Corporate Quality Management", expDate: "2026-12-31", auditDate: "2025-11-15", major: 0, minor: 1 },
{ standard: "ISO 27001:2022", scope: "Information Security (All Depts)", expDate: "2025-09-20", auditDate: "2025-08-01", major: 0, minor: 0 },
{ standard: "ISO 14001:2015", scope: "Environmental Management", expDate: "2027-05-10", auditDate: "2026-04-01", major: 1, minor: 3 },
{ standard: "ISO 45001:2018", scope: "Occupational Health & Safety", expDate: "2025-08-15", auditDate: "2025-07-20", major: 0, minor: 0 },
{ standard: "ISO 22301:2019", scope: "Business Continuity", expDate: "2025-02-01", auditDate: "2024-12-15", major: 2, minor: 5 },
];
function iso_initialize() {
sampleData.forEach(d => iso_add_row(d));
iso_updateDashboard();
iso_updateNavButtons();
}
window.iso_add_row = function(data = {}) {
const tableBody = document.getElementById('iso-input-table-body');
const row = tableBody.insertRow();
row.innerHTML = `
|
|
|
|
|
|
|
`;
// Apply styling to inputs to match table cells
row.querySelectorAll('input').forEach(input => {
input.style.width = '100%';
input.style.padding = '8px';
input.style.border = '1px solid #d1d5db';
input.style.borderRadius = '4px';
});
}
function iso_collectAndProcessData() {
const rows = document.getElementById('iso-input-table-body').rows;
const today = new Date("2025-07-10T00:00:00Z"); // Using specified current date
const risk_threshold = today.getTime() + 90 * 24 * 60 * 60 * 1000; // 90 days from now
return Array.from(rows).map(row => {
const expDateStr = row.querySelector('.expDate').value;
const expDate = expDateStr ? new Date(expDateStr + "T00:00:00Z") : null;
const auditDateStr = row.querySelector('.auditDate').value;
const auditDate = auditDateStr ? new Date(auditDateStr + "T00:00:00Z") : null;
let status = "Pending Audit";
let daysToExpire = null;
if (expDate) {
const isExpired = expDate.getTime() < today.getTime();
daysToExpire = Math.ceil((expDate - today) / (1000 * 60 * 60 * 24));
if (isExpired) {
status = "Expired";
} else if (expDate.getTime() < risk_threshold) {
status = "At Risk";
} else {
status = "Certified";
}
}
return {
standard: row.querySelector('.standard').value,
scope: row.querySelector('.scope').value,
expDate: expDateStr,
auditDate: auditDateStr,
majorNCs: parseInt(row.querySelector('.major').value) || 0,
minorNCs: parseInt(row.querySelector('.minor').value) || 0,
status: status,
daysToExpire: daysToExpire,
daysToAudit: auditDate ? Math.ceil((auditDate - today) / (1000 * 60 * 60 * 24)) : null,
};
});
}
window.iso_updateDashboard = function() {
const data = iso_collectAndProcessData();
iso_updateKPIs(data);
iso_updateCharts(data);
iso_renderTables(data);
iso_changeTab('dashboardTab');
}
function iso_updateKPIs(data) {
document.getElementById('kpi-total').textContent = data.length;
document.getElementById('kpi-active').textContent = data.filter(d => d.status === 'Certified').length;
document.getElementById('kpi-at-risk').textContent = data.filter(d => d.status === 'At Risk').length;
const totalNCs = data.reduce((sum, d) => sum + d.majorNCs + d.minorNCs, 0);
document.getElementById('kpi-open-ncs').textContent = totalNCs;
}
function iso_updateCharts(data) {
// Status Chart
if(iso_statusChart) iso_statusChart.destroy();
const statusCounts = data.reduce((acc, d) => { acc[d.status] = (acc[d.status] || 0) + 1; return acc; }, {});
iso_statusChart = new Chart(document.getElementById('iso-status-chart'), {
type: 'doughnut',
data: {
labels: Object.keys(statusCounts),
datasets: [{ data: Object.values(statusCounts), backgroundColor: Object.keys(statusCounts).map(k => STATUS_CONFIG[k]?.color || '#ccc') }]
},
options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top' } } }
});
// NC Chart
if(iso_ncChart) iso_ncChart.destroy();
iso_ncChart = new Chart(document.getElementById('iso-nc-chart'), {
type: 'bar',
data: {
labels: data.map(d => d.standard),
datasets: [
{ label: 'Major NCs', data: data.map(d => d.majorNCs), backgroundColor: '#ef4444' },
{ label: 'Minor NCs', data: data.map(d => d.minorNCs), backgroundColor: '#f59e0b' }
]
},
options: {
responsive: true, maintainAspectRatio: false,
scales: { x: { stacked: true }, y: { stacked: true, beginAtZero: true } },
plugins: { legend: { position: 'top' } }
}
});
}
function iso_renderTables(data) {
const deadlinesBody = document.getElementById('iso-deadlines-table-body');
deadlinesBody.innerHTML = '';
const deadlines = [];
data.forEach(d => {
if (d.daysToExpire !== null && d.daysToExpire >= 0) deadlines.push({standard: d.standard, event: 'Expiration', date: d.expDate, days: d.daysToExpire});
if (d.daysToAudit !== null && d.daysToAudit >= 0) deadlines.push({standard: d.standard, event: 'Next Audit', date: d.auditDate, days: d.daysToAudit});
});
deadlines.sort((a, b) => a.days - b.days).slice(0, 5).forEach(d => {
const daysClass = d.days < 30 ? 'days-left-due' : d.days < 90 ? 'days-left-soon' : 'days-left-ok';
deadlinesBody.innerHTML += `
| ${d.standard} | ${d.event} | ${d.date} | ${d.days} |
`;
});
}
window.iso_downloadPDF = function() {
const { jsPDF } = window.jspdf;
const doc = new jsPDF('landscape');
const data = iso_collectAndProcessData();
doc.setFontSize(20).text("ISO Certification Status Report", 148, 20, { align: 'center' });
const chartCanvas1 = document.getElementById('iso-status-chart').toDataURL('image/png', 1.0);
const chartCanvas2 = document.getElementById('iso-nc-chart').toDataURL('image/png', 1.0);
doc.addImage(chartCanvas1, 'PNG', 15, 35, 100, 80);
doc.addImage(chartCanvas2, 'PNG', 125, 35, 155, 80);
const tableHead = [['ISO Standard', 'Scope/Unit', 'Status', 'Expires On', 'Days Left', 'Major NCs', 'Minor NCs', 'Next Audit']];
const tableBody = data.map(d => [d.standard, d.scope, d.status, d.expDate, d.daysToExpire, d.majorNCs, d.minorNCs, d.auditDate]);
doc.autoTable({
head: tableHead, body: tableBody,
startY: 120, theme: 'grid',
headStyles: { fillColor: '#047857' }
});
doc.save('ISO_Certification_Report.pdf');
}
// --- TABBING & NAVIGATION ---
window.iso_changeTab = function(tabId) {
document.querySelectorAll('.iso-tab-content').forEach(c => c.classList.remove('active'));
document.querySelectorAll('.iso-tab-button').forEach(b => b.classList.remove('active'));
document.getElementById(tabId).classList.add('active');
const activeButton = Array.from(document.querySelectorAll('.iso-tab-button')).find(btn => btn.getAttribute('onclick').includes(tabId));
if (activeButton) activeButton.classList.add('active');
iso_currentTabIndex = TABS.indexOf(tabId);
iso_updateNavButtons();
}
window.iso_navigateTabs = function(direction) {
let newIndex = iso_currentTabIndex;
if (direction === 'next') newIndex = Math.min(newIndex + 1, TABS.length - 1);
else if (direction === 'prev') newIndex = Math.max(newIndex - 1, 0);
iso_changeTab(TABS[newIndex]);
}
function iso_updateNavButtons() {
const prevBtn = document.getElementById('iso-prev-btn');
const nextBtn = document.getElementById('iso-next-btn');
if(prevBtn) prevBtn.disabled = iso_currentTabIndex === 0;
if(nextBtn) nextBtn.disabled = iso_currentTabIndex === TABS.length - 1;
}
iso_initialize();
});