/* ============================================================ analytics.js — Analytics dashboard (many charts) ============================================================ */ window.Views = window.Views || {}; Views.analytics = function () { const a = DB.analytics; const html = `

Analytics

Deep-dive metrics across your recruitment funnel

WeekMonthQuarter

Hiring Trend

Hires vs applications
${Charts.legend([{ label: 'Applications', color: Charts.PALETTE[4] }, { label: 'Hires', color: Charts.PALETTE[0] }])}

Applications Received

Monthly volume

Source Breakdown

Offer Acceptance

Accepted Pending Declined

Pipeline Distribution

Applications by Department

Volume per team

Recruiter Performance

Hires by recruiter (top 8)

Time to Hire

Days, monthly average

Time to Fill

Days, monthly average
`; 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) => `${s.source}`).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] }] }); } }; };