Files
govoplan-ops/webui/src/features/ops/runtimeStatus.ts
T

35 lines
1.5 KiB
TypeScript

export function knownMetric(value: number | null | undefined): number | "unavailable" {
return typeof value === "number" && Number.isFinite(value) ? value : "unavailable";
}
export function knownQueueDepthTotal(depths: Record<string, number | null | undefined>): number | null {
const values = Object.values(depths).filter((value): value is number => typeof value === "number" && Number.isFinite(value));
return values.length ? values.reduce((total, value) => total + value, 0) : null;
}
export function runtimeWorkTone(state: string): "good" | "info" | "warning" | "danger" | "neutral" {
if (state === "healthy" || state === "idle") return "good";
if (state === "busy") return "info";
if (state === "starting" || state === "degraded" || state === "unconfigured") return "warning";
if (state === "stale" || state === "unreachable") return "danger";
return "neutral";
}
export function heartbeatAgeSeconds(now: number, value?: string | null): number | null {
if (!value) return null;
const timestamp = Date.parse(value);
if (!Number.isFinite(timestamp)) return null;
return Math.max(0, Math.floor((now - timestamp) / 1000));
}
export function heartbeatAgeLabel(ageSeconds: number | null): string {
if (ageSeconds === null) return "unavailable";
if (ageSeconds < 60) return `${ageSeconds}s ago`;
if (ageSeconds < 3600) return `${Math.floor(ageSeconds / 60)}m ago`;
return `${Math.floor(ageSeconds / 3600)}h ago`;
}
export function shouldPollRuntimeStatus(hidden: boolean, requestInFlight: boolean): boolean {
return !hidden && !requestInFlight;
}