Hiring Velocity
Time-to-fill by department against target, with open-req load and the roles dragging hiring.
Acme · Hiring Velocity
41d
avg time-to-fill · 30 open reqs
- At/under target
- Over target
- Target
Engineering roles take 57d to fill vs the 45d target — and hold 16 open reqs.
| Dept | Days to fill | Target | Open reqs |
|---|---|---|---|
| Engineering | 57d | 45d | 16 |
| Ops | 42d | 40d | 3 |
| Sales | 39d | 38d | 5 |
| Design | 38d | 42d | 2 |
| Support | 29d | 35d | 4 |
Acme ATS · updated Q2 2026
Demo data is illustrative. Replace with your own typed data prop.
How it computes
Average days-to-fill across departments, each ranked against its target and flagged above a slow threshold.
avg = mean(daysToFill); red if daysToFill > target·1.2, amber if > target; slowest = argmax(daysToFill − target)Assumptions
- Each department carries a target days-to-fill.
- The 1.2× urgency threshold is a fixed constant.
- Open-req counts are cumulative for the period.
Honest about
- The average is unweighted — a department with many open reqs counts the same as one with a single req.
- The flagged "slowest" dept is the largest gap to target, not the longest absolute time-to-fill.
- Empty input degrades to an explicit empty state.
Use it with your data
Data contract
Passed via the data prop — an array of objects: validated at runtime, so a bad shape degrades to the error state rather than crashing.
| Field | Type | Required |
|---|---|---|
| dept | string | yes |
| daysToFill | number | yes |
| target | number | yes |
| openReqs | number | yes |
Static props
import { PeopleHiringVelocity } from "@/components/blocks/people-hiring-velocity/people-hiring-velocity";
<PeopleHiringVelocity data={myData} />Client fetch (SWR)
const { data, isLoading, error } = useSWR("/api/metric", fetcher);
// route isLoading/error through <ChartStates>, then:
<PeopleHiringVelocity data={data} />Server component (RSC)
const data = await fetch(url, {
next: { revalidate: 3600 },
}).then((r) => r.json());
<PeopleHiringVelocity data={data} />Map arbitrary rows
import { mapRows } from "@/lib/sextant/column-map";
const out = mapRows(rows, { /* contractKey: "yourColumn" */ }, dataSchema);
if (out.ok) <PeopleHiringVelocity data={out.data} />;Make it yours
Every customization below is an optional prop — omit them all and you get the defaults shown above. Recolor, reformat, restyle, or drop the card chrome without forking the component.
Recolor or rename a series (onTarget, slow, target)
<PeopleHiringVelocity
series={{ onTarget: { color: "var(--chart-3)", label: "…" } }}
/>Format numbers (currency / locale)
const eur = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
notation: "compact",
});
<PeopleHiringVelocity valueFormatter={(n) => eur.format(n)} />Replace or hide the narrative
<PeopleHiringVelocity narrative="Your own one-liner." />
// …or hide it entirely:
<PeopleHiringVelocity narrative={false} />Restyle any region
<PeopleHiringVelocity
className="max-w-xl"
classNames={{ body: "bg-muted/20" }}
/>Drop the card chrome (headless plot)
import { PeopleHiringVelocityPlot } from "@/components/blocks/people-hiring-velocity/people-hiring-velocity";
// Just the chart body — bring your own card/layout:
<PeopleHiringVelocityPlot data={data} config={config} />