Files
HealthLog/src/components/ui/page-auth-gate.tsx
T
Marc-André Bombeck 23ded20306 fix(pages): replace bare auth-gate spinners with a layout-reserving skeleton
Introduce a shared PageAuthGate (header + tile skeleton) and adopt it on the
top-level pages that flashed a lone centered spinner while auth resolved:
mood, measurements, achievements, illness, mental-wellbeing, checkups, cycle,
and the labs / medications / custom-metrics / illness detail routes. The
auth-resolving frame now reserves the page geometry instead of snapping in
after a spinner.
2026-07-09 00:23:13 +02:00

50 lines
1.6 KiB
TypeScript

import { Skeleton } from "@/components/ui/skeleton";
import { cn } from "@/lib/utils";
/**
* Auth-resolving placeholder for a top-level page.
*
* Every module page renders while `useAuth()` / its first query settles. The
* legacy pattern was a lone centered `Loader2` that then snapped into the full
* layout — a reflow, and inconsistent with the skeleton-first surfaces
* (documents, Insights tiles, dashboard hero). This reserves the page frame
* instead: a `PageHeader`-shaped title/description block above a tile grid, so
* the auth-resolving frame holds the same geometry the loaded page will.
*
* Presentational + `aria-hidden` (matching the old spinner, which announced
* nothing); pass `label` to expose an `sr-only` status line for screen readers.
*/
export function PageAuthGate({
className,
label,
tiles = 3,
}: {
className?: string;
label?: string;
/** Number of content tile skeletons below the header. */
tiles?: number;
}) {
return (
<div
data-slot="page-auth-gate"
className={cn("space-y-6", className)}
aria-hidden="true"
>
{label ? (
<span className="sr-only" aria-live="polite" aria-hidden={false}>
{label}
</span>
) : null}
<div className="space-y-1.5">
<Skeleton className="h-8 w-48 max-w-full rounded" />
<Skeleton className="h-4 w-72 max-w-full rounded" />
</div>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{Array.from({ length: tiles }).map((_, i) => (
<Skeleton key={i} className="h-32 rounded-xl" />
))}
</div>
</div>
);
}