main
js 144 lines 4.11 KB
Raw
1 import {Suspense} from 'react';
2 import StatsGrid from './StatsGrid';
3 import TableRow from './TableRow';
4 import TableHeader from './TableHeader';
5 import Pagination from './Pagination';
6 import ActivityItem from './ActivityItem';
7 import ChartPanel from './ChartPanel';
8 import Skeleton from './Skeleton';
9 import {generateProducts, generateActivities, generateStats} from './data';
10
11 function fetchData(generator, ...args) {
12 return new Promise(resolve => {
13 setTimeout(() => resolve(generator(...args)), 1);
14 });
15 }
16
17 function fetchDelayed(value, delayMs) {
18 return new Promise(resolve => {
19 setTimeout(() => resolve(value), delayMs);
20 });
21 }
22
23 async function AsyncStatsSection() {
24 const stats = await fetchData(generateStats);
25 return <StatsGrid stats={stats} />;
26 }
27
28 const productColumns = [
29 {key: 'name', label: 'Product'},
30 {key: 'sku', label: 'SKU'},
31 {key: 'category', label: 'Category'},
32 {key: 'price', label: 'Price'},
33 {key: 'stock', label: 'Stock'},
34 {key: 'status', label: 'Status'},
35 {key: 'rating', label: 'Rating'},
36 ];
37
38 async function AsyncProductRow({product, delay}) {
39 const resolved = await fetchDelayed(product, delay);
40 return <TableRow product={resolved} columns={productColumns} />;
41 }
42
43 async function AsyncProductSection({itemCount}) {
44 const products = await fetchData(generateProducts, itemCount);
45 return (
46 <div className="product-table-container">
47 <div className="table-toolbar">
48 <h2>Products</h2>
49 <span className="table-count">{products.length} items</span>
50 </div>
51 <table className="product-table">
52 <thead>
53 <tr>
54 {productColumns.map(col => (
55 <TableHeader key={col.key} column={col} />
56 ))}
57 </tr>
58 </thead>
59 <tbody>
60 {products.map((product, i) => (
61 <Suspense
62 key={product.id}
63 fallback={
64 <tr>
65 <td colSpan={7}>Loading...</td>
66 </tr>
67 }>
68 <AsyncProductRow product={product} delay={1 + (i % 5)} />
69 </Suspense>
70 ))}
71 </tbody>
72 </table>
73 <Pagination total={products.length} pageSize={20} />
74 </div>
75 );
76 }
77
78 async function AsyncChartSection() {
79 const stats = await fetchData(generateStats);
80 return <ChartPanel title="Revenue" data={stats.revenueByMonth} type="bar" />;
81 }
82
83 async function AsyncActivityItem({activity, delay}) {
84 const resolved = await fetchDelayed(activity, delay);
85 return (
86 <ActivityItem
87 type={resolved.type}
88 user={resolved.user}
89 message={resolved.message}
90 timestamp={resolved.timestamp}
91 details={resolved.details}
92 />
93 );
94 }
95
96 async function AsyncActivitySection({itemCount}) {
97 const activities = await fetchData(
98 generateActivities,
99 Math.min(itemCount, 50)
100 );
101 return (
102 <div className="activity-feed">
103 <h3>Recent Activity</h3>
104 <ul className="activity-list">
105 {activities.map((activity, i) => (
106 <Suspense key={activity.id} fallback={<li>Loading...</li>}>
107 <AsyncActivityItem activity={activity} delay={1 + (i % 5)} />
108 </Suspense>
109 ))}
110 </ul>
111 </div>
112 );
113 }
114
115 export default function DashboardAsync({itemCount}) {
116 return (
117 <main className="dashboard">
118 <div className="dashboard-header">
119 <h1>Dashboard Overview</h1>
120 <p className="dashboard-subtitle">
121 Welcome back. Here is what is happening with your store today.
122 </p>
123 </div>
124 <Suspense fallback={<Skeleton type="stats" />}>
125 <AsyncStatsSection />
126 </Suspense>
127 <div className="dashboard-grid">
128 <div className="dashboard-main">
129 <Suspense fallback={<Skeleton type="table" />}>
130 <AsyncProductSection itemCount={itemCount} />
131 </Suspense>
132 </div>
133 <div className="dashboard-aside">
134 <Suspense fallback={<Skeleton type="chart" />}>
135 <AsyncChartSection />
136 </Suspense>
137 <Suspense fallback={<Skeleton type="feed" />}>
138 <AsyncActivitySection itemCount={itemCount} />
139 </Suspense>
140 </div>
141 </div>
142 </main>
143 );
144 }