/* ============================================================
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}
`).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'
});
}
};
};