Mikkel Manniche
Dansk English Deutsch

Vorlage · React · HTML

People ops

Ein Personal-Dashboard in Tiles: ein Recruiting-Trichter, der heutige Plan, eine Gehaltsliste und eine Punktmatrix der Anwesenheit.

Vorschau

React

Installiere die Vorlage mit shadcn. Sie landet als normale Komponenten in deinem Projekt, die du selbst anpasst.

npx shadcn@latest add https://mikkelmanniche.dk/lab/r/people-ops.json
npx shadcn@latest add @manniche/people-ops
people-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

Die ganze Seite als eine Datei mit eingebautem CSS und ohne JavaScript. Umschalter, Tabs und Filter funktionieren allein mit HTML und CSS.

In neuem Tab öffnen

Die Farbe in deiner eigenen App

Wenn shadcn bereits eingerichtet ist, ergeben diese Variablen dieselbe Farbe. Hell in :root, dunkel 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);
}

Installieren

npx shadcn@latest add https://mikkelmanniche.dk/lab/r/people-ops.json

Abhängigkeiten

    Dateien

    • people-ops.tsx
    • people-ops.html

    Manniche Lab · Open Source

    Kostenlose Werkzeuge für KI-Agenten und Webentwicklung.