115 lines
6.3 KiB
JavaScript
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] }] });
|
|
}
|
|
};
|
|
};
|