Template · React · HTML
People ops
A people dashboard in Tiles: a hiring funnel, today's schedule, a payroll list and an attendance dot matrix.
Preview
React
Install the template with shadcn. It lands in your project as ordinary components that you edit yourself.
npx shadcn@latest add https://mikkelmanniche.dk/lab/r/people-ops.jsonnpx shadcn@latest add @manniche/people-opspeople-ops.tsx
// A people-ops screen: hiring funnel, today's schedule, payroll list and an attendance dot matrix.
import { cn } from '@/lib/utils'
import { BarChart, type BarPoint } from '@/registry/manniche/bar-chart/bar-chart'
import { DataTile } from '@/registry/manniche/data-tile/data-tile'
import { DotMatrix } from '@/registry/manniche/dot-matrix/dot-matrix'
import { TransactionList, type Transaction } from '@/registry/manniche/transaction-list/transaction-list'
import type { ValueFormat } from '@/registry/manniche/chart-kit/chart-utils'
export type PeopleOpsData = {
funnel: { title: string; stages: BarPoint[] }
today: { title: string; items: { id: string; time: string; title: string; who: string }[] }
payroll: { title: string; items: Transaction[]; today?: string; format?: ValueFormat }
attendance: { title: string; data: number[][]; rows: string[]; columns: string[] }
}
export type PeopleOpsLabels = { note?: string }
export type PeopleOpsProps = { data: PeopleOpsData; labels?: PeopleOpsLabels; className?: string }
export function PeopleOps({ data, labels, className }: PeopleOpsProps) {
const note = labels?.note ?? 'Example data.'
return (
<div className={cn('@container w-full rounded-2xl bg-background p-3 text-foreground', className)}>
<div className="grid grid-cols-1 gap-3 @2xl:grid-cols-6">
<div className="@2xl:col-span-3">
<DataTile className="h-full" title={data.today.title} inverted>
<ol className="flex flex-col gap-3">
{data.today.items.map((it) => (
<li key={it.id} className="flex items-baseline gap-3 text-sm">
<time className="w-12 shrink-0 tabular-nums opacity-70">{it.time}</time>
<span className="min-w-0 flex-1 truncate font-medium">{it.title}</span>
<span className="opacity-70">{it.who}</span>
</li>
))}
</ol>
</DataTile>
</div>
<div className="@2xl:col-span-3">
<DataTile className="h-full" title={data.funnel.title} footer={note}>
<BarChart data={data.funnel.stages} label={data.funnel.title} current={null} />
</DataTile>
</div>
<div className="@2xl:col-span-3">
<DataTile className="h-full" title={data.payroll.title} footer={note}>
<TransactionList data={data.payroll.items} label={data.payroll.title} today={data.payroll.today} format={data.payroll.format} />
</DataTile>
</div>
<div className="@2xl:col-span-3">
<DataTile className="h-full" title={data.attendance.title} footer={note}>
<DotMatrix data={data.attendance.data} rows={data.attendance.rows} columns={data.attendance.columns} label={data.attendance.title} />
</DataTile>
</div>
</div>
</div>
)
}people-ops-demo.tsx
import { PeopleOps, type PeopleOpsData } from '@/components/people-ops'
// Example data only. All names and figures are invented.
const data: PeopleOpsData = {
funnel: {
title: 'Hiring funnel',
stages: [
{ label: 'Applied', value: 120 },
{ label: 'Screen', value: 48 },
{ label: 'Interview', value: 20 },
{ label: 'Offer', value: 5 },
],
},
today: {
title: "Today's schedule",
items: [
{ id: 's1', time: '09:30', title: 'Sample stand-up', who: 'Team' },
{ id: 's2', time: '11:00', title: 'Demo interview', who: 'Alex E.' },
{ id: 's3', time: '14:00', title: 'Example onboarding', who: 'Sam S.' },
],
},
payroll: {
title: 'Payroll',
today: '2026-10-06',
format: { currency: 'EUR', decimals: 0 },
items: [
{ id: 'p1', name: 'Alex Example', amount: -3200, date: '2026-09-30', category: 'Salary' },
{ id: 'p2', name: 'Sam Sample', amount: -2900, date: '2026-09-30', category: 'Salary' },
{ id: 'p3', name: 'Robin Demo', amount: -2600, date: '2026-09-30', category: 'Salary' },
],
},
attendance: {
title: 'Attendance',
rows: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
columns: ['W1', 'W2', 'W3', 'W4'],
data: [[8, 7, 8, 6], [7, 8, 8, 7], [8, 6, 7, 8], [7, 7, 8, 8], [5, 6, 4, 6]],
},
}
export default function PeopleOpsDemo() {
return <PeopleOps data={data} />
}HTML
The whole page as one file with all CSS built in and no JavaScript. Toggles, tabs and filters work with HTML and CSS alone.
The colour in your own app
If you already have shadcn set up, these variables give the same colour. Light in :root, dark in .dark.
:root {
--primary: oklch(0.52 0.2 262);
--primary-foreground: oklch(0.99 0 0);
--accent: oklch(0.95 0.034 262);
--accent-foreground: oklch(0.36 0.140 262);
--ring: oklch(0.52 0.2 262);
}
.dark {
--primary: oklch(0.74 0.150 262);
--primary-foreground: oklch(0.18 0.024 262);
--accent: oklch(0.29 0.050 262);
--accent-foreground: oklch(0.9 0.050 262);
--ring: oklch(0.74 0.150 262);
}Install
npx shadcn@latest add https://mikkelmanniche.dk/lab/r/people-ops.json