Files
app/frontend/src/components/Health/queries.ts
T
stroblme 6ff6dabdcd Fold the brain and health screens into Home
Home is the one overview now: the brain graph flat across the top, the flow
switches, then the health sections. The /brain and /health routes and their
sidebar entries are gone.

- charts report their cursor and clicks, so hovering one filters the list
  beside it to that minute and a click pins it until Escape or Clear
- chart values round to about three significant digits, the legend mounts
  under the plot so it can wrap without leaving the card, and axis ticks
  shorten past a thousand
- the embedded brain leaves the wheel to the page rather than zooming
- number fields no longer draw their up/down spinner (NOTEPAD)
2026-08-17 00:17:14 +02:00

100 lines
3.4 KiB
TypeScript

import { ObservabilityService } from "@/client"
/** The live view; anything older is the collector's rollups. */
export const HOURS = 24
/** Another tab can change things, and the engine fails flows on its own. */
const REFRESH = 30_000
/**
* How deep the run and failure lists are read.
*
* Deeper than they are shown: picking a minute off a chart filters these rows
* client-side, and a list holding only the newest handful would have nothing
* to find for any minute but the current one.
*
* ponytail: the runs endpoint caps at 200 and takes no time range, so a busy
* engine still only covers its last minute or so. A `since` parameter is what
* would let a moment on the chart reach the whole day.
*/
const RUN_DEPTH = 200
const EVENT_DEPTH = 100
export const healthKeys = {
all: ["observability"] as const,
summary: ["observability", "summary"] as const,
events: ["observability", "events"] as const,
}
/** The resting surface these screens are built from. */
export const CARD = "rounded-lg border border-border bg-card p-4 shadow-e1"
export const summaryQueryOptions = () => ({
queryKey: healthKeys.summary,
queryFn: () => ObservabilityService.readSummary(),
refetchInterval: 10_000,
})
export const timeseriesQueryOptions = () => ({
queryKey: ["observability", "timeseries", HOURS] as const,
queryFn: () => ObservabilityService.readTimeseries({ hours: HOURS }),
refetchInterval: REFRESH,
})
export const flowRollupsQueryOptions = () => ({
queryKey: ["observability", "flows", HOURS] as const,
queryFn: () => ObservabilityService.readFlowRollups({ hours: HOURS }),
refetchInterval: REFRESH,
})
export const runsQueryOptions = () => ({
queryKey: ["observability", "runs"] as const,
queryFn: () => ObservabilityService.readRuns({ limit: RUN_DEPTH }),
refetchInterval: REFRESH,
})
export const failuresQueryOptions = () => ({
queryKey: [...healthKeys.events, "failure"] as const,
queryFn: () =>
ObservabilityService.readEvents({ kind: "failure", limit: EVENT_DEPTH }),
refetchInterval: REFRESH,
})
export const auditQueryOptions = () => ({
queryKey: [...healthKeys.events, "audit"] as const,
queryFn: () => ObservabilityService.readEvents({ kind: "audit", limit: 15 }),
refetchInterval: REFRESH,
})
export const deadLetterQueryOptions = () => ({
queryKey: ["observability", "dead-letter"] as const,
queryFn: () => ObservabilityService.readDeadLetters({ limit: 20 }),
refetchInterval: REFRESH,
})
/** How long ago something happened, in the coarsest unit that still says it. */
export function ago(ts: string | number | null | undefined): string {
if (!ts) return "—"
const stamp = typeof ts === "number" ? ts * 1000 : Date.parse(ts)
const seconds = Math.max(0, (Date.now() - stamp) / 1000)
if (seconds < 90) return `${Math.round(seconds)}s ago`
if (seconds < 5400) return `${Math.round(seconds / 60)}m ago`
if (seconds < 172800) return `${Math.round(seconds / 3600)}h ago`
return `${Math.round(seconds / 86400)}d ago`
}
/**
* The minute a timestamp belongs to, as unix seconds.
*
* Minutes are the granularity the engine stores its metrics at, so this is
* also the finest moment a chart can be asked about.
*/
export const minuteOf = (ts: string) => Math.floor(Date.parse(ts) / 60000) * 60
/** A moment as a clock reading, which is how the charts label their axis. */
export const clock = (ts: number) =>
new Date(ts * 1000).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})