/** * The compact status block at the sidebar's foot (milestone 39): whether * filtering is in force, how fast queries are arriving, and how long the * process has been up. Three readings the operator glances at from any page, * each from data the shell already holds — no request of its own. * * Queries/min derives from the Overview body the page in front of the reader * is drawing, so it appears only while that page is open and its window has * landed; on every other page the row is absent rather than stale. */ import { useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { useMatch } from "@tanstack/react-router"; import * as stylex from "@stylexjs/stylex"; import { formatDuration, formatRate } from "@/lib/format"; import { overviewQuery, versionQuery } from "@/lib/queries"; import { useProtection } from "@/features/pause/protection"; import { DEFAULT_PERIOD } from "@/features/overview/period"; import { styles as shared } from "@/ui/styles"; import { colors, metrics } from "@/ui/tokens.stylex"; const styles = stylex.create({ block: { display: "flex", flexDirection: "column", gap: "0.4375rem", marginInline: "1rem", marginBottom: "0.75rem", borderRadius: metrics.radius, borderWidth: 1, borderStyle: "solid", borderColor: colors.border, backgroundColor: colors.surface, paddingInline: "0.75rem", paddingBlock: "0.875rem", fontSize: "0.75rem", lineHeight: "1rem", color: colors.textMuted, }, line: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: "0.625rem", margin: 0, }, key: { display: "inline-flex", alignItems: "center", gap: "0.4375rem", }, value: { fontWeight: 550, color: colors.text, }, dot: { flexShrink: 0, width: "7px", height: "7px", borderRadius: "999px", }, dotActive: { backgroundColor: colors.chartGreen }, dotPaused: { backgroundColor: colors.warnBorderStrong }, dotUnavailable: { backgroundColor: colors.chartRed }, dotUnknown: { backgroundColor: colors.borderStrong }, /** The narrow header's rendering: the dot and the state alone, inline. */ inline: { display: "inline-flex", alignItems: "center", gap: "0.4375rem", fontSize: "0.75rem", lineHeight: "1rem", color: colors.textSecondary, }, }); const PROTECTION_TEXT = { active: "Active", paused: "Paused", unavailable: "Unavailable", unknown: "Unknown", } as const; const DOT_STYLE = { active: styles.dotActive, paused: styles.dotPaused, unavailable: styles.dotUnavailable, unknown: styles.dotUnknown, } as const; /** The dot and the word, for the narrow header where there is room for nothing more. */ export function ProtectionDot() { const { state } = useProtection(); return ( Protection {PROTECTION_TEXT[state].toLowerCase()} ); } /** * Uptime as of now, not as of the fetch: the version answer is a minute old at * most, so the seconds it reported are aged by the time since it landed. A * restart shows within that minute. Ticks every second, because the first * minute reads in seconds. */ function useUptimeSeconds(): number | null { const version = useQuery(versionQuery()); const [now, setNow] = useState(() => Date.now()); useEffect(() => { const timer = setInterval(() => setNow(Date.now()), 1000); return () => clearInterval(timer); }, []); if (version.data === undefined) return null; return version.data.uptime_seconds + Math.max(0, now - version.dataUpdatedAt) / 1000; } function useQueriesPerMinute(): number | null { const match = useMatch({ from: "/shell/overview", shouldThrow: false }); const period = match?.search.period ?? DEFAULT_PERIOD; // Reads the body the page fetched; never fetches one of its own. A failed // refetch leaves the last body in the cache, and the page is then showing // its error, not that body: the row goes with it. const overview = useQuery({ ...overviewQuery(period, match?.search.client), enabled: false }); if (match === undefined || overview.isError || overview.data === undefined) return null; if (overview.data.period !== period) return null; const minutes = (overview.data.until - overview.data.since) / 60; return minutes > 0 ? overview.data.totals.queries / minutes : null; } export default function SidebarStatus() { const { state } = useProtection(); const rate = useQueriesPerMinute(); const uptime = useUptimeSeconds(); return (