mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 13:52:20 +00:00
Opening an Insights category page froze the whole UI: the per-metric status card fired a cold, uncapped LLM fetch on mount with no client abort, so the request hung up to the server's 60s ceiling and the pending/resolve re-renders thrashed the tab strip and ate touch events. Timeouts were never negatively cached, so every visit re-blocked. Fix the class holistically: - Bound every status GET on the client. The shared use-insight-status hook and the inline medication-compliance query now wrap fetch in an 8s AbortController and stop retrying inline; navigation can no longer await an uncapped round-trip. - Make the status routes read-only. On a cache miss the generator enqueues an out-of-band pg-boss job (insight-status-generate) and returns a preparing envelope; the client polls a bounded interval until the worker warms the cache. Models the dashboard snapshot's ready/preparing pattern. The queue is registered in the worker allQueues loop + a boss.work handler, with a guard test. - Fix the nightly warm-pass regression: warm every supported locale (the client reads against the active UI locale, not the persisted User.locale) and warm on a cached comprehensive outcome too (its write still evicts the per-status caches). Add a short-TTL negative cache on the timeout path so a transient provider stall no longer makes every subsequent visit re-block or re-enqueue. - Add loading.tsx for the insights segment + the root so any future RSC await streams a skeleton instead of blocking the transition. - Stabilise the advisor regenerate callback so a status-query flip can't re-render the memoised tab strip mid-gesture. New i18n key insights.assessmentPreparing across all six locales.
38 lines
1.8 KiB
TypeScript
38 lines
1.8 KiB
TypeScript
/**
|
|
* v1.8.3 — route-level loading skeleton for every `/insights/*` segment.
|
|
*
|
|
* Defense-in-depth for the navigation-freeze class: any RSC await on an
|
|
* insights route (now or in a future refactor) streams this skeleton
|
|
* instead of blocking the transition. The geometry mirrors `SubPageShell`
|
|
* (heading + chart card + assessment card) so the post-load swap reflows
|
|
* by less than a row. Pure server component — no client hooks, no data
|
|
* fetch; Next.js renders it instantly while the segment streams.
|
|
*/
|
|
export default function InsightsLoading() {
|
|
return (
|
|
<div data-slot="insights-subpage-loading" className="space-y-6">
|
|
<header className="space-y-2">
|
|
<div className="bg-muted h-7 w-48 animate-pulse rounded motion-reduce:animate-none" />
|
|
<div className="bg-muted/70 h-4 w-72 animate-pulse rounded motion-reduce:animate-none" />
|
|
</header>
|
|
|
|
{/* Chart card */}
|
|
<div className="bg-card rounded-xl border p-4">
|
|
<div className="bg-muted mb-4 h-4 w-32 animate-pulse rounded motion-reduce:animate-none" />
|
|
<div className="bg-muted/60 h-56 w-full animate-pulse rounded-lg motion-reduce:animate-none" />
|
|
</div>
|
|
|
|
{/* Assessment card */}
|
|
<div className="bg-card space-y-2 rounded-xl border p-4">
|
|
<div className="flex items-center gap-2">
|
|
<div className="bg-muted h-5 w-5 animate-pulse rounded motion-reduce:animate-none" />
|
|
<div className="bg-muted h-4 w-40 animate-pulse rounded motion-reduce:animate-none" />
|
|
</div>
|
|
<div className="bg-muted h-3.5 w-full animate-pulse rounded motion-reduce:animate-none" />
|
|
<div className="bg-muted h-3.5 w-11/12 animate-pulse rounded motion-reduce:animate-none" />
|
|
<div className="bg-muted h-3.5 w-9/12 animate-pulse rounded motion-reduce:animate-none" />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|