loading.tsx28 lines · main
| 1 | /** |
| 2 | * Cockpit-wide loading fallback. Shown while an admin page's server-side |
| 3 | * /v1/admin/* fetch is in flight, so the operator sees a calm, honest |
| 4 | * "working" state instead of a blank frame. Server-safe (no client hooks). |
| 5 | * |
| 6 | * Deliberately generic — a heading shimmer plus a small grid of card |
| 7 | * skeletons — because it backs every cockpit page (overview, billing, |
| 8 | * health, mcp), each of which leads with a header and a card row. |
| 9 | */ |
| 10 | export default function AdminCockpitLoading() { |
| 11 | return ( |
| 12 | <div className="flex flex-col gap-8" aria-busy="true" aria-live="polite"> |
| 13 | <span className="sr-only">loading…</span> |
| 14 | <header className="flex flex-col gap-2"> |
| 15 | <div className="h-6 w-48 animate-pulse rounded-[var(--radius-md)] bg-[var(--color-surface)]" /> |
| 16 | <div className="h-4 w-full max-w-prose animate-pulse rounded-[var(--radius-md)] bg-[var(--color-surface)]" /> |
| 17 | </header> |
| 18 | <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4"> |
| 19 | {Array.from({ length: 4 }).map((_, i) => ( |
| 20 | <div |
| 21 | key={i} |
| 22 | className="h-24 animate-pulse rounded-[var(--radius-md)] border border-[var(--color-border-subtle)] bg-[var(--color-surface)]" |
| 23 | /> |
| 24 | ))} |
| 25 | </div> |
| 26 | </div> |
| 27 | ); |
| 28 | } |