refactor(ui): server metrics page uses global time range
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m31s
CI / docker (push) Successful in 1m10s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 44s

Drop the page-local DS Select window picker. Drive from() / to() off
useGlobalFilters().timeRange so the dashboard tracks the same TopBar range
as Exchanges / Dashboard / Runtime. Bucket size auto-scales via
stepSecondsFor(windowSeconds) (10 s for ≤30 min → 1 h for >48 h). Query
hooks now take ServerMetricsRange = { from: Date; to: Date } instead of a
windowSeconds number, so they support arbitrary absolute or rolling ranges
the TopBar may supply (not just "now − N"). Toolbar collapses to just the
server-instance badges.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-24 09:19:20 +02:00
parent 3c2409ed6e
commit 35319dc666
4 changed files with 97 additions and 72 deletions

View File

@@ -49,30 +49,47 @@ export interface ServerMetricQueryRequest {
serverInstanceIds?: string[] | null;
}
// ── Range helper ───────────────────────────────────────────────────────
/**
* Time range driving every hook below. Callers pass the window they want
* to render; the hooks never invent their own "now" — that's the job of
* the global time-range control.
*/
export interface ServerMetricsRange {
from: Date;
to: Date;
}
function serializeRange(range: ServerMetricsRange) {
return {
from: range.from.toISOString(),
to: range.to.toISOString(),
};
}
// ── Query Hooks ────────────────────────────────────────────────────────
export function useServerMetricsCatalog(windowSeconds = 3600) {
export function useServerMetricsCatalog(range: ServerMetricsRange) {
const refetchInterval = useRefreshInterval(60_000);
const { from, to } = serializeRange(range);
return useQuery({
queryKey: ['admin', 'server-metrics', 'catalog', windowSeconds],
queryFn: async () => {
const to = new Date();
const from = new Date(to.getTime() - windowSeconds * 1000);
const params = new URLSearchParams({ from: from.toISOString(), to: to.toISOString() });
queryKey: ['admin', 'server-metrics', 'catalog', from, to],
queryFn: () => {
const params = new URLSearchParams({ from, to });
return adminFetch<ServerMetricCatalogEntry[]>(`/server-metrics/catalog?${params}`);
},
refetchInterval,
});
}
export function useServerMetricsInstances(windowSeconds = 3600) {
export function useServerMetricsInstances(range: ServerMetricsRange) {
const refetchInterval = useRefreshInterval(60_000);
const { from, to } = serializeRange(range);
return useQuery({
queryKey: ['admin', 'server-metrics', 'instances', windowSeconds],
queryFn: async () => {
const to = new Date();
const from = new Date(to.getTime() - windowSeconds * 1000);
const params = new URLSearchParams({ from: from.toISOString(), to: to.toISOString() });
queryKey: ['admin', 'server-metrics', 'instances', from, to],
queryFn: () => {
const params = new URLSearchParams({ from, to });
return adminFetch<ServerInstanceInfo[]>(`/server-metrics/instances?${params}`);
},
refetchInterval,
@@ -80,28 +97,23 @@ export function useServerMetricsInstances(windowSeconds = 3600) {
}
/**
* Run a time-series query against the server_metrics table.
* Generic time-series query against the server_metrics table.
*
* The window [from, to) is supplied in seconds of "now minus N" so the panel
* refreshes automatically at the polling interval without the caller
* recomputing timestamps.
* The caller owns the window — passing the globally-selected range keeps
* every panel aligned with the app-wide time control and allows inspection
* of historical windows, not just "last N seconds from now".
*/
export function useServerMetricsSeries(
request: Omit<ServerMetricQueryRequest, 'from' | 'to'>,
windowSeconds: number,
range: ServerMetricsRange,
opts?: { enabled?: boolean },
) {
const refetchInterval = useRefreshInterval(30_000);
const { from, to } = serializeRange(range);
return useQuery({
queryKey: ['admin', 'server-metrics', 'query', request, windowSeconds],
queryFn: async () => {
const to = new Date();
const from = new Date(to.getTime() - windowSeconds * 1000);
const body: ServerMetricQueryRequest = {
...request,
from: from.toISOString(),
to: to.toISOString(),
};
queryKey: ['admin', 'server-metrics', 'query', request, from, to],
queryFn: () => {
const body: ServerMetricQueryRequest = { ...request, from, to };
return adminFetch<ServerMetricQueryResponse>('/server-metrics/query', {
method: 'POST',
body: JSON.stringify(body),