Metric targets
This is the dashboard knowing what good is. A target turns a number into a judgement, which means the target needs an owner and a date on it or nobody can say whether missing it matters.
npx shadcn@latest add card progress
Tokens this needs: --background, --card, --card-foreground, --muted, --muted-foreground, --border, --status-warn, --status-critical, --chart-1
The status, chart, scale, state and direction names are an extension,
not a rename. shadcn has --destructive and five --chart-* and nothing
else in this territory.
────────────────────────────────────────────────────────────────────────
// MetricTarget.tsx
────────────────────────────────────────────────────────────────────────
import { Card } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
/**
* A target is a commitment with provenance, and the provenance is the type.
* Owner and review date are required because a target with no name cannot
* be revised, only ignored, and an unreviewed one is wrong within a year and
* still colouring panels. Panels take a Target; they never retype the number.
*/
export type Target = {
name: string;
/** The objective, as a fraction: 0.999 for 99.9%. */
objective: number;
/** What the objective is a fraction of: "requests", "checkouts". */
of: string;
/** How the indicator is computed, so the reasoning is reachable from the panel. */
measuredAs: string;
owner: string;
reviewDue: Date;
/** What draws or fires on this target. Stored once, referenced from here. */
referencedBy: string[];
};
const DAY = 86_400_000;
const pct = (f: number) => `${+(f * 100).toFixed(2)}%`;
function review(due: Date, now: Date) {
const days = Math.ceil((due.getTime() - now.getTime()) / DAY);
if (days < 0) return { text: `overdue by ${-days} days`, cls: "text-status-critical" };
const text = days >= 14 ? `due in ${Math.round(days / 7)} weeks` : `due in ${days} days`;
return { text, cls: days <= 30 ? "text-status-warn" : "text-card-foreground" };
}
export function TargetCard({ target, now, onOpen }: { target: Target; now: Date; onOpen?: (t: Target) => void }) {
const r = review(target.reviewDue, now);
return (
{target.name} used by Error budget {Math.round(b.left * 100)}% left, {b.daysToGo} days to go
{b.fast ? "burning faster than the quarter allows" : "on pace for the quarter"}
{target.referencedBy.map((ref) => (