HR-ATS-Portal/js/analytics.js

115 lines
6.3 KiB
JavaScript

/* ============================================================
analytics.js — Analytics dashboard (many charts)
============================================================ */
window.Views = window.Views || {};
Views.analytics = function () {
const a = DB.analytics;
const html = `
<div class="page">
<div class="page-head">
<div><h1 class="page-title">Analytics</h1><p class="page-sub">Deep-dive metrics across your recruitment funnel</p></div>
<div class="page-head-actions">
<div class="pill-tabs"><span class="pill-tab">Week</span><span class="pill-tab active">Month</span><span class="pill-tab">Quarter</span></div>
<button class="btn btn-secondary" onclick="UI.toast('Analytics exported','success')">${UI.icon('download')} Export</button>
</div>
</div>
<div class="grid g-2 mb-18">
<div class="card">
<div class="card-head"><div><h3>Hiring Trend</h3><span class="ch-sub">Hires vs applications</span></div></div>
<div class="card-body"><div class="chart-wrap"><canvas id="anTrend" height="260"></canvas></div>
${Charts.legend([{ label: 'Applications', color: Charts.PALETTE[4] }, { label: 'Hires', color: Charts.PALETTE[0] }])}</div>
</div>
<div class="card">
<div class="card-head"><div><h3>Applications Received</h3><span class="ch-sub">Monthly volume</span></div></div>
<div class="card-body"><div class="chart-wrap"><canvas id="anApps" height="260"></canvas></div></div>
</div>
</div>
<div class="grid g-3 mb-18">
<div class="card">
<div class="card-head"><div><h3>Source Breakdown</h3></div></div>
<div class="card-body">
<div class="chart-wrap"><canvas id="anSource" height="220"></canvas></div>
<div class="chart-legend" id="anSourceLegend"></div>
</div>
</div>
<div class="card">
<div class="card-head"><div><h3>Offer Acceptance</h3></div></div>
<div class="card-body">
<div class="chart-wrap"><canvas id="anOffer" height="220"></canvas></div>
<div class="chart-legend">
<span class="legend-item"><span class="legend-dot" style="background:var(--success)"></span>Accepted</span>
<span class="legend-item"><span class="legend-dot" style="background:var(--warning)"></span>Pending</span>
<span class="legend-item"><span class="legend-dot" style="background:var(--danger)"></span>Declined</span>
</div>
</div>
</div>
<div class="card">
<div class="card-head"><div><h3>Pipeline Distribution</h3></div></div>
<div class="card-body"><div class="chart-wrap"><canvas id="anPipeline" height="260"></canvas></div></div>
</div>
</div>
<div class="grid g-2 mb-18">
<div class="card">
<div class="card-head"><div><h3>Applications by Department</h3><span class="ch-sub">Volume per team</span></div></div>
<div class="card-body"><div class="chart-wrap"><canvas id="anDept" height="300"></canvas></div></div>
</div>
<div class="card">
<div class="card-head"><div><h3>Recruiter Performance</h3><span class="ch-sub">Hires by recruiter (top 8)</span></div></div>
<div class="card-body"><div class="chart-wrap"><canvas id="anRec" height="300"></canvas></div></div>
</div>
</div>
<div class="grid g-2">
<div class="card">
<div class="card-head"><div><h3>Time to Hire</h3><span class="ch-sub">Days, monthly average</span></div></div>
<div class="card-body"><div class="chart-wrap"><canvas id="anTTH" height="240"></canvas></div></div>
</div>
<div class="card">
<div class="card-head"><div><h3>Time to Fill</h3><span class="ch-sub">Days, monthly average</span></div></div>
<div class="card-body"><div class="chart-wrap"><canvas id="anTTF" height="240"></canvas></div></div>
</div>
</div>
</div>`;
return {
html,
onMount() {
Charts.line(document.getElementById('anTrend'), {
labels: a.hiringTrend.labels, area: true,
datasets: [
{ label: 'Applications', data: a.hiringTrend.applications, color: Charts.PALETTE[4] },
{ label: 'Hires', data: a.hiringTrend.hires, color: Charts.PALETTE[0] }
]
});
Charts.bar(document.getElementById('anApps'), { labels: a.hiringTrend.labels, data: a.hiringTrend.applications });
Charts.doughnut(document.getElementById('anSource'), {
labels: a.sources.map(s => s.source), data: a.sources.map(s => s.count),
centerValue: DB.candidates.length, centerLabel: 'Total'
});
document.getElementById('anSourceLegend').innerHTML = a.sources.map((s, i) =>
`<span class="legend-item"><span class="legend-dot" style="background:${Charts.PALETTE[i % Charts.PALETTE.length]}"></span>${s.source}</span>`).join('');
Charts.doughnut(document.getElementById('anOffer'), {
labels: ['Accepted', 'Pending', 'Declined'],
data: [a.offerAcceptance.accepted, a.offerAcceptance.pending, a.offerAcceptance.declined],
colors: [Charts.token('--success'), Charts.token('--warning'), Charts.token('--danger')],
centerValue: Math.round(a.offerAcceptance.accepted / (a.offerAcceptance.accepted + a.offerAcceptance.declined || 1) * 100) + '%',
centerLabel: 'Accept rate'
});
Charts.horizontalBar(document.getElementById('anPipeline'), {
labels: a.pipeline.map(p => p.stage), data: a.pipeline.map(p => p.count),
colors: Charts.PALETTE
});
Charts.bar(document.getElementById('anDept'), { labels: a.departments.map(d => d.dept), data: a.departments.map(d => d.apps) });
const topRecs = [...DB.recruiters].sort((x, y) => y.hires - x.hires).slice(0, 8);
Charts.horizontalBar(document.getElementById('anRec'), { labels: topRecs.map(r => r.name), data: topRecs.map(r => r.hires) });
Charts.line(document.getElementById('anTTH'), { labels: a.hiringTrend.labels, area: true, yFmt: v => v + 'd', datasets: [{ label: 'Time to Hire', data: a.timeToHire, color: Charts.PALETTE[0] }] });
Charts.line(document.getElementById('anTTF'), { labels: a.hiringTrend.labels, area: true, yFmt: v => v + 'd', datasets: [{ label: 'Time to Fill', data: a.timeToFill, color: Charts.PALETTE[2] }] });
}
};
};