/** * Prometheus Metrics tab (spec R2.4, R8.2). * * Instance-scoped tab showing Prometheus service health. * usePrometheusStatus is scoped by instance.id; usePrometheusTargets * stays global (returns Node Exporter scrape targets for external Prom). */ import { Radio } from "lucide-react"; import { usePrometheusStatus, usePrometheusTargets, } from "../../hooks/useObservability"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Skeleton } from "@/components/ui/skeleton"; import type { PrometheusTarget, ServiceInstance } from "../../types"; function TargetsTable({ targets }: { targets: PrometheusTarget[] }) { return (
{targets.map((target, idx) => (
{target.targets.join(", ")}
{target.labels && Object.keys(target.labels).length > 0 && (
{Object.entries(target.labels).map(([key, value]) => ( {key}: {value} ))}
)}
))}
); } export function MetricsTab({ instance }: { instance: ServiceInstance }) { const { data: status, isLoading: statusLoading, error: statusError, } = usePrometheusStatus(instance.id); const { data: targets, isLoading: targetsLoading, error: targetsError, } = usePrometheusTargets(); const statusDetail = status?.up ? status.version ? `version ${status.version}` : "reachable" : statusLoading ? "checking…" : "unreachable"; return (
Prometheus {statusDetail}
{statusError && ( Failed to reach Prometheus {statusError.message} )} Node Exporter Targets ({targets?.length ?? 0}) {targetsLoading ? (
) : !targets || targets.length === 0 ? (
No Node Exporter targets
Enable Node Exporter on an SSH machine in Settings to populate Prometheus scrape targets.
) : ( )}
{targetsError && ( Failed to load targets {targetsError.message} )}
); }