Files
manage/frontend/src/widgets/PrometheusMeanWidget.tsx
T
Developer 65bae95e3c feat(prometheus-direct-charting): slice 2 — gauge + mean widgets
Add gauge widget (recharts RadialBarChart with configurable threshold
bands, scalar-only per SC-111) and mean widget (client-side average over
range-query window, scalar-only per SC-114). Extract shared _instant_query
helper from the metric path; _fetch_gauge and _fetch_mean dispatch in
PrometheusWidgetSource.fetch(). Both new widget kinds declared in
integrations/prometheus.py and frontend registry.

Backend: 305 pytest pass, ruff clean. Frontend: 136 vitest pass, build+lint green.
2026-07-08 22:10:11 +00:00

60 lines
1.6 KiB
TypeScript

import { Alert, AlertDescription } from "@/components/ui/alert";
import { Skeleton } from "@/components/ui/skeleton";
import { SectionCard } from "../components/SectionCard";
import { useWidgetData } from "../hooks/useWidgets";
import type { WidgetInstance } from "../types";
interface Props {
widget: WidgetInstance;
refreshIntervalMs: number;
description?: string;
}
interface MeanData {
value: number;
unit?: string | null;
}
function formatMean(value: number, unit?: string | null): string {
let formatted: string;
if (Math.abs(value) >= 1000) {
formatted = value.toFixed(0);
} else if (Math.abs(value) >= 1) {
formatted = value.toFixed(2).replace(/\.?0+$/, "");
} else {
formatted = value.toFixed(4).replace(/\.?0+$/, "");
}
return unit ? `${formatted} ${unit}` : formatted;
}
export function PrometheusMeanWidget({
widget,
refreshIntervalMs,
description,
}: Props) {
const { data, isLoading } = useWidgetData(widget.id, refreshIntervalMs);
const mean = data?.data as MeanData | undefined;
return (
<SectionCard title={widget.title} description={description}>
{isLoading && !data ? (
<Skeleton className="h-16 w-32" />
) : data?.error ? (
<Alert variant="destructive">
<AlertDescription>{data.error}</AlertDescription>
</Alert>
) : mean ? (
<div className="flex flex-col items-center justify-center py-4">
<span className="text-3xl font-bold">
{formatMean(mean.value, mean.unit)}
</span>
</div>
) : (
<Alert>
<AlertDescription>No data. Check your PromQL query.</AlertDescription>
</Alert>
)}
</SectionCard>
);
}