59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import type { CSSProperties } from "react";
|
|
import { clampPercent, formatMetricValue } from "../../format";
|
|
import type { UiTelemetryCard } from "../../types";
|
|
import { IconGlyph } from "../foundation/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;
|
|
}
|