fix+upstream: hooks-order crash, ConfirmDialog/PageHeader/skeletons, styled error boundary
Hoists hooks above early return (render-time Navigate); ports finance's ConfirmDialog/PageHeader/loading/states + APC error-copy mapper; removes window.confirm, dead appbar search, and seeded fake notifications; wires toasts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
98
app/components/loading.tsx
Normal file
98
app/components/loading.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Skeleton } from "~/components/ui/skeleton"
|
||||
import { Card, CardContent } from "~/components/ui/card"
|
||||
|
||||
/** Layout-preserving placeholder for a list/table surface. Renders a
|
||||
* header bar + `rows` row skeletons. Looks far less alarming than a
|
||||
* single spinner during slow loads and keeps the layout from jumping
|
||||
* when data lands. */
|
||||
export function ListSkeleton({ rows = 8 }: { rows?: number }) {
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Skeleton className="h-9 w-72" />
|
||||
<Skeleton className="h-8 w-24" />
|
||||
</div>
|
||||
<div className="rounded-md border">
|
||||
<div className="flex items-center gap-4 border-b bg-muted/30 px-4 py-2">
|
||||
<Skeleton className="h-3 w-16" />
|
||||
<Skeleton className="h-3 w-24" />
|
||||
<Skeleton className="h-3 w-20" />
|
||||
<Skeleton className="ml-auto h-3 w-16" />
|
||||
</div>
|
||||
{Array.from({ length: rows }).map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-4 border-b px-4 py-3 last:border-b-0"
|
||||
>
|
||||
<Skeleton className="h-4 w-20" />
|
||||
<Skeleton className="h-4 w-32" />
|
||||
<Skeleton className="h-4 w-16" />
|
||||
<Skeleton className="ml-auto h-4 w-24" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Layout-preserving placeholder for a dashboard/overview surface. Mirrors
|
||||
* a hero KPI + secondary KPIs, a trend + categories row, a forecast strip
|
||||
* and a list card so there's no visible reshuffle when data lands. Shape
|
||||
* it to match whatever your overview renders. */
|
||||
export function DashboardSkeleton() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Hero KPI row */}
|
||||
<section className="grid gap-6 lg:grid-cols-3 lg:items-end">
|
||||
<div className="lg:col-span-2 space-y-3">
|
||||
<Skeleton className="h-3 w-32" />
|
||||
<Skeleton className="h-14 w-72" />
|
||||
<Skeleton className="h-8 w-full max-w-md" />
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<Skeleton className="h-7 w-full" />
|
||||
<Skeleton className="h-7 w-full" />
|
||||
<Skeleton className="h-3 w-32" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Trend + categories */}
|
||||
<div className="grid gap-6 lg:grid-cols-3">
|
||||
<Card className="lg:col-span-2">
|
||||
<CardContent className="space-y-3 p-5 md:p-7">
|
||||
<Skeleton className="h-4 w-40" />
|
||||
<Skeleton className="h-3 w-64" />
|
||||
<Skeleton className="h-56 w-full" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Card>
|
||||
<CardContent className="space-y-4 p-5 md:p-7">
|
||||
<Skeleton className="h-4 w-40" />
|
||||
<Skeleton className="h-8 w-40" />
|
||||
<Skeleton className="h-2.5 w-full rounded-full" />
|
||||
<div className="space-y-2">
|
||||
{Array.from({ length: 4 }).map((_, i) => (
|
||||
<Skeleton key={i} className="h-4 w-full" />
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Forecast */}
|
||||
<Card>
|
||||
<CardContent className="space-y-4 p-5 md:p-7">
|
||||
<Skeleton className="h-4 w-32" />
|
||||
<div className="grid gap-4 sm:grid-cols-3">
|
||||
{Array.from({ length: 3 }).map((_, i) => (
|
||||
<div key={i} className="space-y-2">
|
||||
<Skeleton className="h-3 w-24" />
|
||||
<Skeleton className="h-7 w-32" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user