Project progress
Milestones and how far along each is.
Built with
HR system migration
At riskMoving every Northwind office to one HR system before year end.
- Go-live 2 Nov 2026
- Owner: Priya Nair, Head of people
Overall progress62%Overall
Milestones
- DiscoveryDone: Done 14 Aug
- Data migrationDone: Done 18 Sep
- Pilot with People OpsIn progress: In progress, due 16 Oct
- Company rolloutUp next: Starts 2 Nov
- Retire old systemUp next: Due 30 Nov
Workstreams
- Employee records · Sara Malik100%1,284 of 1,284 records moved
- Payroll integration · Chloe Martin72%Parallel run 2 of 3 passed
- Leave policies · Yuki Tanaka55%6 of 11 countries configured
- Training · Mia Jensen20%Manager sessions booked for October
project-progress.tsx
import { LuCalendarClock, LuUsers } from "react-icons/lu";
import { Badge, CircularProgress, ProgressBar, Stepper } from "@nexera-ui/react";
const MILESTONES = [
{ label: "Discovery", description: "Done 14 Aug" },
{ label: "Data migration", description: "Done 18 Sep" },
{ label: "Pilot with People Ops", description: "In progress, due 16 Oct" },
{ label: "Company rollout", description: "Starts 2 Nov" },
{ label: "Retire old system", description: "Due 30 Nov" },
] as const;
const WORKSTREAMS = [
{
name: "Employee records",
owner: "Sara Malik",
value: 100,
note: "1,284 of 1,284 records moved",
},
{
name: "Payroll integration",
owner: "Chloe Martin",
value: 72,
note: "Parallel run 2 of 3 passed",
},
{ name: "Leave policies", owner: "Yuki Tanaka", value: 55, note: "6 of 11 countries configured" },
{ name: "Training", owner: "Mia Jensen", value: 20, note: "Manager sessions booked for October" },
] as const;
const OVERALL = Math.round(WORKSTREAMS.reduce((sum, w) => sum + w.value, 0) / WORKSTREAMS.length);
/** Project progress: milestones of a project and how far along each workstream is. */
export default function ProjectProgress() {
return (
<div className="@container bg-page px-4 py-8 @3xl:px-8">
<div className="mx-auto grid w-full max-w-5xl gap-5">
<header className="flex flex-wrap items-start justify-between gap-4 rounded-xl border border-default bg-surface p-5 shadow-xs">
<div className="grid min-w-0 flex-1 gap-2">
<div className="flex flex-wrap items-center gap-2">
<h2 className="text-heading-h2 text-primary">HR system migration</h2>
<Badge tone="warning" label="At risk" />
</div>
<p className="text-body-default text-secondary">
Moving every Northwind office to one HR system before year end.
</p>
<ul className="flex flex-wrap gap-x-4 gap-y-1 text-body-small text-secondary">
<li className="flex items-center gap-1.5">
<LuCalendarClock aria-hidden="true" className="size-4" /> Go-live 2 Nov 2026
</li>
<li className="flex items-center gap-1.5">
<LuUsers aria-hidden="true" className="size-4" /> Owner: Priya Nair, Head of people
</li>
</ul>
</div>
<CircularProgress size="lg" value={OVERALL} label="Overall progress" caption="Overall" />
</header>
<section
aria-labelledby="milestones-title"
className="grid gap-4 rounded-xl border border-default bg-surface p-5 shadow-xs"
>
<h3 id="milestones-title" className="text-heading-h4 text-primary">
Milestones
</h3>
<Stepper aria-labelledby="milestones-title" current={3} steps={MILESTONES} />
</section>
<section
aria-labelledby="streams-title"
className="grid gap-4 rounded-xl border border-default bg-surface p-5 shadow-xs"
>
<h3 id="streams-title" className="text-heading-h4 text-primary">
Workstreams
</h3>
<ul className="m-0 grid list-none gap-5 p-0 @3xl:grid-cols-2">
{WORKSTREAMS.map((w) => (
<li key={w.name}>
<ProgressBar
label={`${w.name} · ${w.owner}`}
labelPosition="top"
size="md"
value={w.value}
description={w.note}
tone={w.value === 100 ? "success" : w.value < 30 ? "warning" : "brand"}
/>
</li>
))}
</ul>
</section>
</div>
</div>
);
}