-
Notifications
You must be signed in to change notification settings - Fork 55
Expand file tree
/
Copy pathasync-section-cards.tsx
More file actions
152 lines (144 loc) · 3.53 KB
/
async-section-cards.tsx
File metadata and controls
152 lines (144 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import { Skeleton } from "@/components/ui/skeleton";
import { Suspense } from "react";
import { SectionCards } from "./section-cards";
// Loading skeleton that matches the SectionCards layout
function SectionCardsLoading() {
return (
<div className="*:data-[slot=card]:shadow-xs @xl/main:grid-cols-2 @5xl/main:grid-cols-4 grid grid-cols-1 gap-4 px-4 lg:px-6">
{Array.from({ length: 4 }).map((_, i) => (
<div key={i} className="rounded-lg border p-6 space-y-4">
<div className="flex items-center justify-between">
<div className="space-y-2">
<Skeleton className="h-4 w-24" />
<Skeleton className="h-8 w-20" />
</div>
<Skeleton className="h-6 w-16" />
</div>
<div className="space-y-2">
<Skeleton className="h-4 w-32" />
<Skeleton className="h-3 w-40" />
</div>
</div>
))}
</div>
);
}
// Individual card component with artificial delay for demo
async function AsyncSectionCard({
title,
value,
description,
trend,
footer,
delay = 0,
}: {
title: string;
value: string;
description: string;
trend: string;
footer: string;
delay?: number;
}) {
// Simulate network delay
if (delay > 0) {
await new Promise((resolve) => setTimeout(resolve, delay));
}
return (
<div className="rounded-lg border p-6 space-y-4">
<div className="flex items-center justify-between">
<div className="space-y-2">
<div className="text-sm font-medium text-muted-foreground">
{title}
</div>
<div className="text-2xl font-bold">{value}</div>
</div>
<div className="text-xs bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200 px-2 py-1 rounded">
{trend}
</div>
</div>
<div className="space-y-1">
<div className="text-sm font-medium">{description}</div>
<div className="text-xs text-muted-foreground">{footer}</div>
</div>
</div>
);
}
// Component that demonstrates progressive loading
export function AsyncSectionCards() {
return (
<div className="grid grid-cols-1 gap-4 px-4 lg:px-6 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
<Suspense
fallback={
<div className="rounded-lg border p-6">
<Skeleton className="h-32 w-full" />
</div>
}
>
<AsyncSectionCard
title="Total Institutions"
value="1,234"
description="Growing steadily"
trend="+12%"
footer="Across all states"
delay={300}
/>
</Suspense>
<Suspense
fallback={
<div className="rounded-lg border p-6">
<Skeleton className="h-32 w-full" />
</div>
}
>
<AsyncSectionCard
title="Sedang Disemak"
value="23"
description="Needs attention"
trend="+2"
footer="Requires admin action"
delay={600}
/>
</Suspense>
<Suspense
fallback={
<div className="rounded-lg border p-6">
<Skeleton className="h-32 w-full" />
</div>
}
>
<AsyncSectionCard
title="Active Users"
value="456"
description="Engaged community"
trend="+5%"
footer="Monthly active users"
delay={900}
/>
</Suspense>
<Suspense
fallback={
<div className="rounded-lg border p-6">
<Skeleton className="h-32 w-full" />
</div>
}
>
<AsyncSectionCard
title="QR Scans"
value="8,901"
description="High engagement"
trend="+18%"
footer="This month"
delay={1200}
/>
</Suspense>
</div>
);
}
// Simpler version with single Suspense boundary
export function AsyncSectionCardsSimple() {
return (
<Suspense fallback={<SectionCardsLoading />}>
<SectionCards />
</Suspense>
);
}