mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 22:50:51 +00:00
Intl.DateTimeFormat instances are memoised per locale, timezone and options signature — construction dominated the 365-day ledger build and the hot path drops about fivefold. A batched GET /api/medications/compliance serves every card from one request through the same per-medication cache cells the detail page reads, replacing the per-card fan-out; the list response stays untouched so the due line never waits on a ledger build. The medications and compliance buckets serve stale-while-revalidate, interactive writes evict while background bulk sync marks stale, the per-id route defers ownership and schedule reads until a cache miss, and the card body reserves constant slots for the status and cycle lines so late data stops shifting the layout.
60 lines
2.6 KiB
TypeScript
60 lines
2.6 KiB
TypeScript
import type { QueryClient } from "@tanstack/react-query";
|
|
import { queryKeys } from "@/lib/query-keys";
|
|
import { apiGet } from "@/lib/api/api-fetch";
|
|
|
|
/**
|
|
* v1.16.7 — intent-based data prefetch for the medications page.
|
|
*
|
|
* The nav links already prefetch the route's JS chunk (next/link
|
|
* default in production); what still serialised the first visit was
|
|
* the data hop — the list query only fired once the page chunk had
|
|
* mounted. Wiring this to the nav link's hover / touch / focus intent
|
|
* puts `/api/medications` (the response carries the per-medication
|
|
* `nextDueAt` due times) in flight while the navigation commits.
|
|
*
|
|
* v1.16.8 — the batched card-compliance read
|
|
* (`/api/medications/compliance`) prefetches alongside the list, so the
|
|
* compliance bars + the status line resolve in the same paint as the
|
|
* cards instead of swapping in late. Two independent requests on
|
|
* purpose: the list response must stay fast (the due line never waits
|
|
* on a compliance ledger build), and the compliance prefetch rides the
|
|
* same server-side cache cells the page query would warm anyway.
|
|
*
|
|
* The 15 s prefetch window only bounds how old a cache entry may be
|
|
* before an intent re-fires the request — the mounted page query rides
|
|
* the provider-default `staleTime` (5 min) and consumes whatever this
|
|
* prefetch parked. The server caches both reads per user (60 s list /
|
|
* 15 min compliance, each with an SWR window), so even a missed window
|
|
* stays cheap.
|
|
*/
|
|
export const MEDICATIONS_LIST_STALE_TIME_MS = 15_000;
|
|
|
|
export function prefetchMedicationsList(queryClient: QueryClient): void {
|
|
void queryClient.prefetchQuery({
|
|
queryKey: queryKeys.medications(),
|
|
queryFn: () => apiGet("/api/medications"),
|
|
staleTime: MEDICATIONS_LIST_STALE_TIME_MS,
|
|
});
|
|
void queryClient.prefetchQuery({
|
|
queryKey: queryKeys.medicationComplianceSummary(),
|
|
queryFn: () => apiGet("/api/medications/compliance"),
|
|
staleTime: MEDICATIONS_LIST_STALE_TIME_MS,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Intent props for a nav link pointing at `/medications` — fires the
|
|
* list prefetch on hover / touch / keyboard focus, i.e. before the
|
|
* router even commits. `prefetchQuery` dedupes internally (in-flight
|
|
* promise reuse + the stale window above), so the three handlers and a
|
|
* subsequent route-commit prefetch collapse into at most one request.
|
|
*/
|
|
export function medicationsPrefetchIntentProps(queryClient: QueryClient): {
|
|
onPointerEnter: () => void;
|
|
onTouchStart: () => void;
|
|
onFocus: () => void;
|
|
} {
|
|
const fire = () => prefetchMedicationsList(queryClient);
|
|
return { onPointerEnter: fire, onTouchStart: fire, onFocus: fire };
|
|
}
|