Tracking
Data DisplayCompact service history with labeled samples and a full text legend.
Responsive viewport — resize the window to explore. Direction and density apply only to this preview. Theme applies to the docs too.
"use client";
import { Tracking, type TrackingEntry } from "@/components/ui/tracking";
export default function TrackingExample() {
const entries: TrackingEntry[] = Array.from({ length: 30 }, (_, index) => ({
id: String(index),
label: `October ${index + 1}`,
status:
index === 8
? "down"
: index === 9 || index === 21
? "degraded"
: index > 27
? "unknown"
: "up",
}));
return (
<div className="w-full max-w-xl space-y-7">
<div className="rounded-xl border bg-card p-5">
<p className="mb-5 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
Service health · Last 30 samples
</p>
<Tracking label="API availability" entries={entries} />
</div>
<Tracking
label="Background workers"
compact
entries={entries
.slice(0, 14)
.map((entry) => ({ ...entry, status: "up" }))}
/>
<Tracking label="New integration" entries={[]} />
<p className="text-xs leading-5 text-muted-foreground">
This sample ratio treats every known observation equally. It is not
time-weighted uptime.
</p>
</div>
);
}Usage
Provide chronological observations with unique ids, a visible time label, and up, degraded, down, or unknown status. The summary reports operational known samples divided by all known samples; unknown observations are excluded. This is an equally weighted sample ratio, not time-weighted uptime. Supply actual telemetry in your application; the component makes no network requests.
import { Tracking, type TrackingEntry } from "@/components/ui/tracking";Shipped as editable source in src/components/ui/tracking.tsx in both apps. Copy the implementation below when moving it to another project. Copy the complete example from Source code for all imports and state. Add "use client" when using hooks or event handlers. See installation and theming for project setup.
Component source
The complete implementation is included in both apps. It uses the React, Radix, Lucide, and shared-core dependencies already installed in Apex. Keep your project’s semantic theme tokens when reusing it.
src/components/ui/tracking.tsx
import { cn } from "@dashboardpack/core/lib/utils";
export interface TrackingEntry {
id: string;
label: string;
status: "up" | "degraded" | "down" | "unknown";
}
export interface TrackingProps {
label: string;
entries: TrackingEntry[];
compact?: boolean;
className?: string;
}
export function Tracking({
label,
entries,
compact = false,
className,
}: TrackingProps) {
const labels = {
up: "Operational",
degraded: "Degraded",
down: "Outage",
unknown: "No data",
};
const known = entries.filter((entry) => entry.status !== "unknown");
const availability = known.length
? `${Math.round((known.filter((entry) => entry.status === "up").length / known.length) * 100)}% operational samples`
: "No known samples";
return (
<figure className={cn("min-w-0 space-y-3", className)}>
<figcaption className="flex flex-wrap items-center justify-between gap-2 text-sm">
<span className="font-medium">{label}</span>
<span className="text-xs text-muted-foreground">{availability}</span>
</figcaption>
{entries.length ? (
<ul
aria-label={`${label} history`}
className={cn("flex gap-1", compact ? "h-5" : "h-9")}
>
{entries.map((entry) => (
<li
key={entry.id}
title={`${entry.label}: ${labels[entry.status]}`}
className={cn(
"min-w-0 flex-1 rounded-sm",
entry.status === "up"
? "bg-success"
: entry.status === "degraded"
? "bg-warning"
: entry.status === "down"
? "bg-destructive"
: "bg-muted",
)}
>
<span className="sr-only">
{entry.label}: {labels[entry.status]}
</span>
</li>
))}
</ul>
) : (
<p className="rounded-md border border-dashed p-4 text-sm text-muted-foreground">
No history available.
</p>
)}
<ul
aria-label="History legend"
className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground"
>
{Object.entries(labels).map(([status, text]) => (
<li key={status} className="flex items-center gap-1.5">
<span
aria-hidden
className={cn(
"size-2 rounded-sm",
status === "up"
? "bg-success"
: status === "degraded"
? "bg-warning"
: status === "down"
? "bg-destructive"
: "bg-muted",
)}
/>
{text}
</li>
))}
</ul>
</figure>
);
}API reference
Common props and defaults. Slot-specific props are named below; native elements also accept their standard HTML attributes. The component TypeScript definitions are the complete type reference.
| Prop | Type / Default | Description |
|---|---|---|
| label | stringDefault: required | Visible caption and accessible history name. |
| entries | TrackingEntry[]Default: required | Unique id, observation label, and up/degraded/down/unknown state. |
| compact | booleanDefault: false | Use shorter history blocks for dense dashboards. |
| className | stringDefault: — | Extend the component container with theme-aware utility classes. |