Add robust error boundaries and improved skeletons to dashboard
Introduces reusable error boundary and suspense timeout components across dashboard pages for better error handling and user feedback. Enhances loading skeletons with subtle progress indicators, animation, and slow-loading warnings. All dynamic imports now include error handling and improved fallback skeletons, and a shared DashboardContentWrapper is added for consistent dashboard content loading experience.
This commit is contained in:
@@ -44,6 +44,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuTrigger,
|
||||
@@ -232,8 +233,58 @@ export default function CustomerManagementPage() {
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="p-8 flex justify-center bg-black/60">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||
<div className="p-8 bg-black/60">
|
||||
{/* Loading indicator */}
|
||||
<div className="absolute top-0 left-0 right-0 h-1 bg-muted overflow-hidden">
|
||||
<div className="h-full bg-primary w-1/3"
|
||||
style={{
|
||||
background: 'linear-gradient(90deg, transparent, hsl(var(--primary)), transparent)',
|
||||
backgroundSize: '200% 100%',
|
||||
animation: 'shimmer 2s ease-in-out infinite',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Table skeleton */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-4 pb-4 border-b border-zinc-800">
|
||||
{['Customer', 'Orders', 'Total Spent', 'Last Order', 'Status'].map((header, i) => (
|
||||
<Skeleton
|
||||
key={i}
|
||||
className="h-4 w-20 flex-1 animate-in fade-in"
|
||||
style={{
|
||||
animationDelay: `${i * 50}ms`,
|
||||
animationDuration: '300ms',
|
||||
animationFillMode: 'both',
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-4 pb-4 border-b border-zinc-800 last:border-b-0 animate-in fade-in"
|
||||
style={{
|
||||
animationDelay: `${250 + i * 50}ms`,
|
||||
animationDuration: '300ms',
|
||||
animationFillMode: 'both',
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-3 flex-1">
|
||||
<Skeleton className="h-10 w-10 rounded-full" />
|
||||
<div className="space-y-1">
|
||||
<Skeleton className="h-4 w-32" />
|
||||
<Skeleton className="h-3 w-24" />
|
||||
</div>
|
||||
</div>
|
||||
<Skeleton className="h-6 w-12 flex-1 rounded-full" />
|
||||
<Skeleton className="h-4 w-20 flex-1" />
|
||||
<Skeleton className="h-4 w-24 flex-1" />
|
||||
<Skeleton className="h-6 w-24 flex-1 rounded-full" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : filteredCustomers.length === 0 ? (
|
||||
<div className="p-8 text-center bg-black/60">
|
||||
|
||||
Reference in New Issue
Block a user