Total Attrition
${exited.length}
Attrition Rate
${attritionRate.toFixed(1)}%
Avg. Tenure of Exited
${avgTenureExited.toFixed(1)} mo
`;
}
const renderChart = (id, type, data, options) => {
if(charts[id]) charts[id].destroy();
charts[id] = new Chart(document.getElementById(id), { type, data, options });
};
function renderAttritionByDeptChart(exited) {
const byDept = exited.reduce((acc, e) => { acc[e.department] = (acc[e.department] || 0) + 1; return acc; }, {});
renderChart('attr-dept-chart', 'bar',
{ labels: Object.keys(byDept), datasets: [{ label: 'Number of Exited Employees', data: Object.values(byDept), backgroundColor: 'rgba(59, 130, 246, 0.7)' }] },
{ responsive: true, maintainAspectRatio: false, indexAxis: 'y', plugins:{legend:{display:false}} }
);
}
function renderAttritionByTenureChart(exited) {
const tenureBins = { "0-6 mo": 0, "7-12 mo": 0, "1-2 yr": 0, "2-5 yr": 0, "5+ yr": 0 };
exited.forEach(e => {
if (e.tenure_months <= 6) tenureBins["0-6 mo"]++;
else if (e.tenure_months <= 12) tenureBins["7-12 mo"]++;
else if (e.tenure_months <= 24) tenureBins["1-2 yr"]++;
else if (e.tenure_months <= 60) tenureBins["2-5 yr"]++;
else tenureBins["5+ yr"]++;
});
renderChart('attr-tenure-chart', 'bar',
{ labels: Object.keys(tenureBins), datasets: [{ label: 'Number of Exited Employees', data: Object.values(tenureBins), backgroundColor: 'rgba(22, 163, 74, 0.7)' }] },
{ responsive: true, maintainAspectRatio: false, plugins:{legend:{display:false}} }
);
}
function renderReasonChart(exited) {
const byReason = exited.filter(e => e.reason_for_leaving).reduce((acc, e) => { acc[e.reason_for_leaving] = (acc[e.reason_for_leaving] || 0) + 1; return acc; }, {});
renderChart('attr-reason-chart', 'doughnut',
{ labels: Object.keys(byReason), datasets: [{ data: Object.values(byReason) }] },
{ responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } } }
);
}
function renderSalaryChart(all) {
const depts = [...new Set(all.map(e => e.department))];
const avgSalaries = {
Active: depts.map(dept => {
const deptEmps = all.filter(e => e.department === dept && e.attrition_status === 'Active');
return deptEmps.length > 0 ? deptEmps.reduce((sum, e) => sum + e.salary_usd, 0) / deptEmps.length : 0;
}),
Exited: depts.map(dept => {
const deptEmps = all.filter(e => e.department === dept && e.attrition_status === 'Exited');
return deptEmps.length > 0 ? deptEmps.reduce((sum, e) => sum + e.salary_usd, 0) / deptEmps.length : 0;
})
};
renderChart('attr-salary-chart', 'bar',
{ labels: depts, datasets: [
{ label: 'Active', data: avgSalaries.Active, backgroundColor: 'rgba(34, 211, 238, 0.7)' },
{ label: 'Exited', data: avgSalaries.Exited, backgroundColor: 'rgba(249, 115, 22, 0.7)' }
]},
{ responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'bottom' } }, scales: { y: { ticks: { callback: v => '$' + (v/1000) + 'k' } } } }
);
}
window.attrDownloadPDF = () => {
html2canvas(document.getElementById('attr-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('Attrition_Analysis_Dashboard.pdf');
});
};
loadSampleData();
});