Card docs/backlog/13-dashboards.md · Milestone M3 · Blocked-by: #5 (needs seed rows to be anything but empty)
Scope
src/dashboards/platform-overview.dashboard.ts, funnel.dashboard.ts, employer-hiring.dashboard.ts, src/datasets/*.dataset.ts, barrels, wire dashboards + datasets; add the dashboard nav items to the apps from #4. Load objectstack-ui (dashboards, charts) and objectstack-data (analytics).
ats_platform_overview — KPI tiles: employers (verified), open jobs (status == 'published'), applications this month, active candidates (seeking_status != 'not_looking'), review-queue length (pending employers + pending jobs); bar chart: applications per week over 12 weeks.
ats_hiring_funnel — funnel over ats_application.stage: applied → screening → interview → offer → hired, with conversion % between stages. Exclude rejected / withdrawn from the funnel itself and show them as a separate tile: a funnel that mixes "did not progress" into its stages misreads as a much worse pipeline than it is.
ats_employer_hiring — scoped to the viewer's employer automatically by RLS, no dashboard-level filter needed: open jobs, applications awaiting action (stage in ['applied','screening']), interviews this week, median days from applied_at to the offer's created_at for hired applications.
Every widget binds to a dataset field that exists — pnpm validate checks widget bindings, and a dangling one renders an empty chart rather than failing loudly at runtime.
Acceptance
pnpm validate && pnpm lint && pnpm typecheck green; pnpm validate reports 3 dashboards.
- With
demo-en loaded the funnel reads 88 / 46 / 28 / 14 / 9. If it does not, either the dashboard or the seed is wrong — say which.
Out of scope
Reports and scheduled digests; any new object.
Card
docs/backlog/13-dashboards.md· Milestone M3 · Blocked-by: #5 (needs seed rows to be anything but empty)Scope
src/dashboards/platform-overview.dashboard.ts,funnel.dashboard.ts,employer-hiring.dashboard.ts,src/datasets/*.dataset.ts, barrels, wiredashboards+datasets; add the dashboard nav items to the apps from #4. Loadobjectstack-ui(dashboards, charts) andobjectstack-data(analytics).Spec —
DESIGN.md§04ats_platform_overview— KPI tiles: employers (verified), open jobs (status == 'published'), applications this month, active candidates (seeking_status != 'not_looking'), review-queue length (pending employers + pending jobs); bar chart: applications per week over 12 weeks.ats_hiring_funnel— funnel overats_application.stage: applied → screening → interview → offer → hired, with conversion % between stages. Excluderejected/withdrawnfrom the funnel itself and show them as a separate tile: a funnel that mixes "did not progress" into its stages misreads as a much worse pipeline than it is.ats_employer_hiring— scoped to the viewer's employer automatically by RLS, no dashboard-level filter needed: open jobs, applications awaiting action (stage in ['applied','screening']), interviews this week, median days fromapplied_atto the offer'screated_atfor hired applications.Every widget binds to a dataset field that exists —
pnpm validatechecks widget bindings, and a dangling one renders an empty chart rather than failing loudly at runtime.Acceptance
pnpm validate && pnpm lint && pnpm typecheckgreen;pnpm validatereports 3 dashboards.demo-enloaded the funnel reads 88 / 46 / 28 / 14 / 9. If it does not, either the dashboard or the seed is wrong — say which.Out of scope
Reports and scheduled digests; any new object.