/** * One period, one request, one window. * * The five per-panel endpoints this replaces could each answer for a different * span, so the page had to reconcile five window identities, retry the laggards * and fail the ones that stayed behind. `GET /api/overview` answers every panel * out of a single read transaction: the totals, both timelines and both * breakdowns describe the same span and the same database state by construction, * and none of that reconciliation has anything left to reconcile. * * The query key carries the period and the client, so the body the hook * returns is always the body of the scope the toolbar names. A rescope shows * the loading state until its own answer lands rather than the previous * scope's charts under the new label: a complete, self-consistent body for the * wrong device is still the wrong body. */ import { useCallback } from "react"; import { useQuery } from "@tanstack/react-query"; import { overviewQuery } from "@/lib/queries"; import type { Overview, Period } from "@/lib/types"; export type Panel = { status: "loading" } | { status: "error"; error: unknown; retry: () => void } | { status: "ready"; data: T }; export function useOverviewWindow(period: Period, client: string | undefined): Panel { const query = useQuery(overviewQuery(period, client)); const { refetch } = query; const retry = useCallback(() => void refetch(), [refetch]); if (query.isError) return { status: "error", error: query.error, retry }; if (query.data !== undefined) return { status: "ready", data: query.data }; return { status: "loading" }; }