/* ============================================================ reports.js — Reports page (cards, charts, table, export) ============================================================ */ window.Views = window.Views || {}; Views.reports = function () { const a = DB.analytics; const reportCards = [ { title: 'Total Hires (YTD)', val: a.hiringTrend.hires.reduce((s, v) => s + v, 0), icn: 'award', cls: 'i-green', sub: '+18% vs last year' }, { title: 'Total Applications', val: a.hiringTrend.applications.reduce((s, v) => s + v, 0).toLocaleString(), icn: 'users', cls: 'i-blue', sub: 'across all channels' }, { title: 'Avg. Time to Hire', val: '27 days', icn: 'clock', cls: 'i-teal', sub: '3 days faster' }, { title: 'Avg. Cost per Hire', val: '$4,280', icn: 'dollar', cls: 'i-amber', sub: 'within budget' } ]; const deptRows = a.departments.map(d => { const rate = Math.round((d.open ? d.apps / (d.open * 40) : 0.5) * 100); return { dept: d.dept, open: d.open, apps: d.apps, hires: DB.int(1, 8), ttf: DB.int(28, 52), rate: Math.min(rate, 98) }; }); const table = UI.dataTable({ pageSize: 10, rows: deptRows, columns: [ { key: 'dept', label: 'Department', sortable: true, render: r => `${r.dept}` }, { key: 'open', label: 'Open Roles', sortable: true, align: 'center' }, { key: 'apps', label: 'Applications', sortable: true, align: 'center', render: r => `${r.apps}` }, { key: 'hires', label: 'Hires', sortable: true, align: 'center' }, { key: 'ttf', label: 'Time to Fill', sortable: true, align: 'center', render: r => r.ttf + ' days' }, { key: 'rate', label: 'Fill Rate', sortable: true, render: r => `
${UI.pbar(r.rate)}
${r.rate}%
` } ] }); const reportTypes = [ { name: 'Hiring Funnel Report', desc: 'Conversion rates across each pipeline stage', icn: 'filter', cls: 'i-indigo' }, { name: 'Source Effectiveness', desc: 'ROI and quality by sourcing channel', icn: 'target', cls: 'i-teal' }, { name: 'Diversity & Inclusion', desc: 'Demographic breakdown of the pipeline', icn: 'users', cls: 'i-purple' }, { name: 'Recruiter Scorecard', desc: 'Individual performance metrics', icn: 'award', cls: 'i-amber' }, { name: 'Offer Analysis', desc: 'Acceptance rates and compensation trends', icn: 'file', cls: 'i-green' }, { name: 'Interview Analytics', desc: 'Interviewer load and feedback quality', icn: 'calendar', cls: 'i-blue' } ]; const html = `

Reports

Recruitment metrics and downloadable insights

${reportCards.map(c => `
${c.title}${UI.icon(c.icn)}
${c.val}
${c.sub}
`).join('')}

Hiring Funnel

Stage-by-stage conversion

Time to Hire vs Fill

Monthly trend (days)
${Charts.legend([{ label: 'Time to Hire', color: Charts.PALETTE[0] }, { label: 'Time to Fill', color: Charts.PALETTE[2] }])}

Department Performance

Hiring breakdown by team
${table.html}

Report Library

Generate a detailed report
${reportTypes.map(r => `
${UI.icon(r.icn)}
${r.name}
${r.desc}
Generate ${UI.icon('chevron-right')}
`).join('')}
`; return { html, onMount() { table.mount(); const funnel = [{ stage: 'Applied', v: 100 }, { stage: 'Screened', v: 62 }, { stage: 'Assessed', v: 41 }, { stage: 'Interviewed', v: 28 }, { stage: 'Offered', v: 14 }, { stage: 'Hired', v: 9 }]; Charts.bar(document.getElementById('rptFunnel'), { labels: funnel.map(f => f.stage), data: funnel.map(f => f.v), colors: Charts.PALETTE, yFmt: v => v + '%' }); Charts.groupedBar(document.getElementById('rptTime'), { labels: DB.analytics.hiringTrend.labels, datasets: [ { label: 'Time to Hire', data: DB.analytics.timeToHire, color: Charts.PALETTE[0] }, { label: 'Time to Fill', data: DB.analytics.timeToFill, color: Charts.PALETTE[2] } ], yFmt: v => v + 'd' }); } }; };