dimensionlab-website/packages/ui/src/components/TelemetryCard.tsx

59 lines
1.9 KiB
TypeScript

import type { CSSProperties } from "react";
import { clampPercent, formatMetricValue } from "../format";
import type { UiTelemetryCard } from "../types";
import { IconGlyph } from "./IconGlyph";
import { LineChart } from "./LineChart";
export interface TelemetryCardProps {
card: UiTelemetryCard;
index?: number;
}
export function TelemetryCard({ card, index }: TelemetryCardProps) {
const progress = resolveProgress(card);
const value = formatMetricValue(card.value);
const metricIndex = index ? String(index).padStart(2, "0") : undefined;
return (
<article
className="telemetry-card"
data-severity={card.severity}
data-model-id={card.id}
data-metric-index={metricIndex}
>
<header>
<div>
{metricIndex ? (
<span className="telemetry-card__index">{metricIndex}</span>
) : null}
<h3>{card.label}</h3>
</div>
{card.icon ? <IconGlyph name={card.icon} size="sm" /> : null}
</header>
<strong>{value}</strong>
{progress !== null ? (
<div className="telemetry-card__bar" aria-hidden="true">
<span style={{ "--metric-progress": `${progress}%` } as CSSProperties} />
</div>
) : null}
{card.sparkline?.length ? (
<LineChart
values={card.sparkline}
severity={card.severity}
label={`${card.label} trend`}
/>
) : null}
{card.detail || card.description ? (
<p className="telemetry-card__source">{card.detail || card.description}</p>
) : null}
</article>
);
}
function resolveProgress(card: UiTelemetryCard): number | null {
if (typeof card.progress === "number") return clampPercent(card.progress);
if (card.value.kind !== "percent") return null;
const progress = Number(card.value.value);
return Number.isFinite(progress) ? clampPercent(progress) : null;
}