/** * 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. * * What remains is the one rule a single request does not settle by itself. * `keepPreviousData` holds the body of the period the reader just left — a * complete, self-consistent answer, and still the wrong one to draw under the * new label — so a body is a member of this window only while its own `period` * is the selected one. Until then the page is loading. */ import { useCallback } from "react"; import { keepPreviousData, 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): Panel { const query = useQuery({ ...overviewQuery(period), placeholderData: keepPreviousData }); const { refetch } = query; const retry = useCallback(() => void refetch(), [refetch]); if (query.isError) return { status: "error", error: query.error, retry }; if (query.data !== undefined && query.data.period === period) return { status: "ready", data: query.data }; return { status: "loading" }; }