Production Dashboard Table
A production-line status table combining status badges, output/utilization progress bars, and 7-day trend sparklines — a composition of the Conditional Formatting and Summary/KPI examples on one realistic dataset, not a new feature on its own.
npx shadcn add https://shad-table.dev/r/production-dashboard-table.jsonLine | Status | Output vs. Target | Utilization | 7-Day Trend |
|---|---|---|---|---|
Line A — Assembly Day shift | Running | 1,420 / 1,200118% | 94% | |
Line B — Packaging Day shift | Running | 980 / 1,10089% | 78% | |
Line C — Welding Night shift | Down | 210 / 95022% | 22% | |
Line D — Paint Booth Day shift | Maintenance | 340 / 80043% | 41% | |
Line E — Final QC Day shift | Running | 610 / 600102% | 88% | |
Line F — CNC Mill Night shift | Idle | 0 / 5000% | 0% | |
Line G — Injection Mold Day shift | Running | 2,150 / 1,800119% | 97% | |
Line H — Labeling Night shift | Running | 730 / 90081% | 65% |
How it works
1.Three primitives, not one new idea
This example is deliberately a composition, not a new feature: the same matchRule engine from the Conditional Formatting Table, the same hand-rolled Sparkline from the Summary/KPI Table, and a small new ProgressBar are combined onto one realistic dataset. Nothing here needs to be understood in isolation if you've already seen those two examples.
import { ProgressBar } from './progress-bar'import { Sparkline } from './sparkline'import { matchRule } from './rules'
2.Progress bars need a default color; text formatting doesn't
The Conditional Formatting Table lets rule?.className be undefined for a healthy row — cn() just drops it and the cell renders unstyled. A progress bar can't do that; an unstyled bar still has to be some color. So every rule list here ends with a catch-all test: () => true, guaranteeing matchRule always returns something to color the bar with.
const outputRules: FormatRule<ProductionLine>[] = [{ test: (row) => row.outputToday / row.targetOutput < 0.8, className: 'bg-rose-500' },{ test: (row) => row.outputToday / row.targetOutput < 1, className: 'bg-amber-500' },{ test: () => true, className: 'bg-emerald-500' }, // catch-all — always matches]
3.The bar renders a percentage; the label renders the real numbers
ProgressBar only ever sees a 0–100 value and clamps it — it has no idea what a 'line' or a 'target' is. The Output column computes that percentage itself and separately renders the actual output/target integers next to the bar, since over-target output (>100%) still needs to clamp visually but shouldn't lie about the real number.
const pct = (row.original.outputToday / row.original.targetOutput) * 100<span>{row.original.outputToday} / {row.original.targetOutput}</span><ProgressBar value={pct} barClassName={rule?.className} />
4.A sparkline can be colored the same way a bar can
Sparkline renders its polyline with stroke="currentColor", so wrapping it in a <span className={rule?.className}> recolors the whole trend line through CSS inheritance — no prop threading into the SVG itself. The rule here compares the trend's last value to its first, so a declining line renders in the same rose-500 used for a low output bar.
const trendRules: FormatRule<ProductionLine>[] = [{ test: (row) => row.trend[row.trend.length - 1] < row.trend[0], className: 'text-rose-500' },{ test: () => true, className: 'text-emerald-500' },]<span className={rule?.className}><Sparkline data={row.original.trend} /></span>