create-bluecopa-react-app 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -238
- package/package.json +1 -1
- package/templates/latest/{Agent.md → AGENT.md} +21 -27
- package/templates/latest/README.md +15 -38
- package/templates/latest/clean.sh +1 -0
- package/templates/latest/package-lock.json +8 -8
- package/templates/latest/package.json +1 -1
- package/templates/latest/setup.sh +5 -4
- package/templates/latest/src/App.tsx +11 -7
- package/templates/latest/src/components/charts/AreaChart.tsx +80 -0
- package/templates/latest/src/components/charts/DonutChart.tsx +73 -0
- package/templates/latest/src/components/charts/SparkAreaChart.tsx +52 -0
- package/templates/latest/src/components/layout/dashboard-header.tsx +1 -1
- package/templates/latest/src/components/layout/dashboard-layout.tsx +19 -11
- package/templates/latest/src/components/{page → layout}/navbar.tsx +2 -0
- package/templates/latest/src/components/layout/sidebar.tsx +2 -1
- package/templates/latest/src/components/page/dashboard/DashboardMetrics.tsx +97 -0
- package/templates/latest/src/components/page/dashboard/PaymentMethodsAnalysis.tsx +182 -0
- package/templates/latest/src/components/page/dashboard/RevenueAnalytics.tsx +505 -0
- package/templates/latest/src/components/page/dashboard/SalesAnalytics.tsx +313 -0
- package/templates/latest/src/components/page/dashboard/TransactionsTable.tsx +256 -0
- package/templates/latest/src/components/page/dashboard/dashboard-utils.ts +147 -0
- package/templates/latest/src/components/page/dashboard/dashboard.tsx +185 -0
- package/templates/latest/src/components/ui/bluecopa-logo.tsx +3 -0
- package/templates/latest/src/components/ui/label.tsx +0 -2
- package/templates/latest/src/components/ui/select.tsx +0 -2
- package/templates/latest/src/pages/Dashboard.tsx +1 -1
- package/templates/latest/src/single-spa.tsx +38 -28
- package/templates/latest/tailwind.config.js +1 -2
- package/templates/latest/tsconfig.app.json +6 -0
- package/templates/latest/tsconfig.json +6 -6
- package/templates/latest/tsconfig.node.json +5 -1
- package/templates/latest/vite.config.ts +1 -1
- package/templates/latest/src/components/page/dashboard.tsx +0 -1506
|
@@ -0,0 +1,505 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
3
|
+
import { Badge } from '@/components/ui/badge';
|
|
4
|
+
import { DollarSign, TrendingUp, Users, ShoppingCart, PieChart } from 'lucide-react';
|
|
5
|
+
import { AreaChart } from '@/components/charts/AreaChart';
|
|
6
|
+
import { DonutChart } from '@/components/charts/DonutChart';
|
|
7
|
+
import { SparkAreaChart } from '@/components/charts/SparkAreaChart';
|
|
8
|
+
|
|
9
|
+
interface RevenueAnalyticsProps {
|
|
10
|
+
orderData: any[];
|
|
11
|
+
monthlyRevenueData: any[];
|
|
12
|
+
totalOrders: number;
|
|
13
|
+
totalRevenue: number;
|
|
14
|
+
averageOrderValue: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const RevenueAnalytics: React.FC<RevenueAnalyticsProps> = ({
|
|
18
|
+
orderData,
|
|
19
|
+
monthlyRevenueData,
|
|
20
|
+
totalOrders,
|
|
21
|
+
totalRevenue,
|
|
22
|
+
averageOrderValue
|
|
23
|
+
}) => {
|
|
24
|
+
// Process data for charts
|
|
25
|
+
const areaChartData = monthlyRevenueData.map((item: any) => ({
|
|
26
|
+
...item,
|
|
27
|
+
growth: monthlyRevenueData.indexOf(item) > 0 ?
|
|
28
|
+
((item.revenue - monthlyRevenueData[monthlyRevenueData.indexOf(item) - 1].revenue) /
|
|
29
|
+
monthlyRevenueData[monthlyRevenueData.indexOf(item) - 1].revenue * 100) : 0,
|
|
30
|
+
cumulative: monthlyRevenueData.slice(0, monthlyRevenueData.indexOf(item) + 1)
|
|
31
|
+
.reduce((sum: number, month: any) => sum + month.revenue, 0)
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
const donutChartData = orderData.map((item: any) => ({
|
|
35
|
+
name: item.category,
|
|
36
|
+
value: item.total,
|
|
37
|
+
percentage: Math.round((item.total / orderData.reduce((sum: number, cat: any) => sum + cat.total, 0)) * 100)
|
|
38
|
+
}));
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div className="space-y-6">
|
|
42
|
+
{/* Enhanced Sales Analytics */}
|
|
43
|
+
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
44
|
+
{/* Top Categories Performance */}
|
|
45
|
+
<Card>
|
|
46
|
+
<CardHeader>
|
|
47
|
+
<CardTitle>Top Performing Categories</CardTitle>
|
|
48
|
+
<CardDescription>
|
|
49
|
+
Revenue performance by category
|
|
50
|
+
</CardDescription>
|
|
51
|
+
</CardHeader>
|
|
52
|
+
<CardContent>
|
|
53
|
+
{orderData.length > 0 ? (
|
|
54
|
+
<div className="space-y-4">
|
|
55
|
+
{orderData.slice(0, 5).map((category: any, index: number) => {
|
|
56
|
+
const percentage = Math.round((category.total / orderData.reduce((sum: number, cat: any) => sum + cat.total, 0)) * 100);
|
|
57
|
+
return (
|
|
58
|
+
<div key={index} className="space-y-2">
|
|
59
|
+
<div className="flex items-center justify-between">
|
|
60
|
+
<div className="flex items-center space-x-2">
|
|
61
|
+
<div className={`w-3 h-3 rounded-full ${
|
|
62
|
+
index === 0 ? 'bg-emerald-500' :
|
|
63
|
+
index === 1 ? 'bg-blue-500' :
|
|
64
|
+
index === 2 ? 'bg-violet-500' :
|
|
65
|
+
index === 3 ? 'bg-amber-500' : 'bg-rose-500'
|
|
66
|
+
}`} />
|
|
67
|
+
<span className="text-sm font-medium">{category.category}</span>
|
|
68
|
+
</div>
|
|
69
|
+
<div className="text-right">
|
|
70
|
+
<div className="text-sm font-bold">${category.total.toLocaleString()}</div>
|
|
71
|
+
<div className="text-xs text-gray-500">{percentage}%</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
<div className="w-full bg-gray-200 rounded-full h-2">
|
|
75
|
+
<div
|
|
76
|
+
className={`h-2 rounded-full ${
|
|
77
|
+
index === 0 ? 'bg-emerald-500' :
|
|
78
|
+
index === 1 ? 'bg-blue-500' :
|
|
79
|
+
index === 2 ? 'bg-violet-500' :
|
|
80
|
+
index === 3 ? 'bg-amber-500' : 'bg-rose-500'
|
|
81
|
+
}`}
|
|
82
|
+
style={{ width: `${percentage}%` }}
|
|
83
|
+
/>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
})}
|
|
88
|
+
</div>
|
|
89
|
+
) : (
|
|
90
|
+
<div className="text-center text-gray-500 py-8">
|
|
91
|
+
No category data available
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
</CardContent>
|
|
95
|
+
</Card>
|
|
96
|
+
|
|
97
|
+
{/* Sales Insights */}
|
|
98
|
+
<Card>
|
|
99
|
+
<CardHeader>
|
|
100
|
+
<CardTitle>Sales Insights</CardTitle>
|
|
101
|
+
<CardDescription>
|
|
102
|
+
Key performance indicators
|
|
103
|
+
</CardDescription>
|
|
104
|
+
</CardHeader>
|
|
105
|
+
<CardContent>
|
|
106
|
+
<div className="space-y-6">
|
|
107
|
+
<div className="text-center">
|
|
108
|
+
<div className="text-3xl font-bold text-emerald-600">
|
|
109
|
+
${averageOrderValue.toFixed(2)}
|
|
110
|
+
</div>
|
|
111
|
+
<div className="text-sm text-gray-500">Average Order Value</div>
|
|
112
|
+
<div className="mt-2">
|
|
113
|
+
<Badge variant="outline" className="text-green-600 border-green-200">
|
|
114
|
+
+5.2% vs last month
|
|
115
|
+
</Badge>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
|
|
119
|
+
<div className="text-center">
|
|
120
|
+
<div className="text-3xl font-bold text-blue-600">
|
|
121
|
+
{totalOrders > 0 ? Math.round(totalRevenue / totalOrders * 12) : 0}%
|
|
122
|
+
</div>
|
|
123
|
+
<div className="text-sm text-gray-500">Conversion Rate</div>
|
|
124
|
+
<div className="mt-2">
|
|
125
|
+
<Badge variant="outline" className="text-blue-600 border-blue-200">
|
|
126
|
+
Industry average: 2.3%
|
|
127
|
+
</Badge>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div className="text-center">
|
|
132
|
+
<div className="text-3xl font-bold text-violet-600">
|
|
133
|
+
{orderData.length}
|
|
134
|
+
</div>
|
|
135
|
+
<div className="text-sm text-gray-500">Product Categories</div>
|
|
136
|
+
<div className="mt-2">
|
|
137
|
+
<Badge variant="outline" className="text-violet-600 border-violet-200">
|
|
138
|
+
Active categories
|
|
139
|
+
</Badge>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</CardContent>
|
|
144
|
+
</Card>
|
|
145
|
+
|
|
146
|
+
{/* Recent Activity */}
|
|
147
|
+
<Card>
|
|
148
|
+
<CardHeader>
|
|
149
|
+
<CardTitle>Recent Activity</CardTitle>
|
|
150
|
+
<CardDescription>
|
|
151
|
+
Latest updates and activities
|
|
152
|
+
</CardDescription>
|
|
153
|
+
</CardHeader>
|
|
154
|
+
<CardContent>
|
|
155
|
+
<div className="space-y-4">
|
|
156
|
+
<div className="flex items-center space-x-4 p-3 bg-emerald-50 rounded-lg border border-emerald-100">
|
|
157
|
+
<div className="w-2 h-2 bg-emerald-500 rounded-full animate-pulse" />
|
|
158
|
+
<div className="flex-1">
|
|
159
|
+
<p className="text-sm font-medium text-emerald-800">High Revenue Day</p>
|
|
160
|
+
<p className="text-xs text-emerald-600">Revenue exceeded daily target by 15%</p>
|
|
161
|
+
</div>
|
|
162
|
+
<Badge variant="outline" className="text-emerald-600 border-emerald-200">Live</Badge>
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div className="flex items-center space-x-4 p-3 bg-blue-50 rounded-lg border border-blue-100">
|
|
166
|
+
<ShoppingCart className="h-5 w-5 text-blue-600" />
|
|
167
|
+
<div className="flex-1">
|
|
168
|
+
<p className="text-sm font-medium text-blue-800">New Order Received</p>
|
|
169
|
+
<p className="text-xs text-blue-600">Order #ORD-{Date.now().toString().slice(-4)} - ${(Math.random() * 500 + 50).toFixed(2)}</p>
|
|
170
|
+
</div>
|
|
171
|
+
<Badge variant="outline" className="text-blue-600 border-blue-200">New</Badge>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<div className="flex items-center space-x-4 p-3 bg-violet-50 rounded-lg border border-violet-100">
|
|
175
|
+
<TrendingUp className="h-5 w-5 text-violet-600" />
|
|
176
|
+
<div className="flex-1">
|
|
177
|
+
<p className="text-sm font-medium text-violet-800">Sales Trending Up</p>
|
|
178
|
+
<p className="text-xs text-violet-600">12% increase in last 24 hours</p>
|
|
179
|
+
</div>
|
|
180
|
+
<Badge variant="outline" className="text-violet-600 border-violet-200">Trend</Badge>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
<div className="flex items-center space-x-4 p-3 bg-amber-50 rounded-lg border border-amber-100">
|
|
184
|
+
<Users className="h-5 w-5 text-amber-600" />
|
|
185
|
+
<div className="flex-1">
|
|
186
|
+
<p className="text-sm font-medium text-amber-800">Customer Milestone</p>
|
|
187
|
+
<p className="text-xs text-amber-600">Reached 1,000+ customers this month</p>
|
|
188
|
+
</div>
|
|
189
|
+
<Badge variant="outline" className="text-amber-600 border-amber-200">Achievement</Badge>
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
</CardContent>
|
|
193
|
+
</Card>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
{/* Enhanced Revenue and Analytics Section */}
|
|
197
|
+
<div className="grid grid-cols-1 xl:grid-cols-3 gap-6">
|
|
198
|
+
{/* Sales by Category Chart */}
|
|
199
|
+
<Card>
|
|
200
|
+
<CardHeader>
|
|
201
|
+
<CardTitle>Sales by Category</CardTitle>
|
|
202
|
+
<CardDescription>
|
|
203
|
+
Revenue breakdown by product category
|
|
204
|
+
</CardDescription>
|
|
205
|
+
</CardHeader>
|
|
206
|
+
<CardContent>
|
|
207
|
+
<DonutChart
|
|
208
|
+
className="h-64"
|
|
209
|
+
data={donutChartData}
|
|
210
|
+
category="value"
|
|
211
|
+
index="name"
|
|
212
|
+
valueFormatter={(number: number) =>
|
|
213
|
+
`$${Intl.NumberFormat("us").format(number)}`
|
|
214
|
+
}
|
|
215
|
+
colors={["slate", "violet", "indigo", "rose", "cyan", "amber"]}
|
|
216
|
+
/>
|
|
217
|
+
</CardContent>
|
|
218
|
+
</Card>
|
|
219
|
+
|
|
220
|
+
{/* Revenue Analytics Summary */}
|
|
221
|
+
<Card>
|
|
222
|
+
<CardHeader>
|
|
223
|
+
<CardTitle>Revenue Analytics</CardTitle>
|
|
224
|
+
<CardDescription>
|
|
225
|
+
Key performance indicators
|
|
226
|
+
</CardDescription>
|
|
227
|
+
</CardHeader>
|
|
228
|
+
<CardContent>
|
|
229
|
+
<div className="space-y-4">
|
|
230
|
+
{/* Top Category */}
|
|
231
|
+
<div className="p-4 bg-gradient-to-r from-emerald-50 to-blue-50 rounded-lg border border-emerald-100">
|
|
232
|
+
<div className="text-sm text-emerald-600 font-medium">Top Revenue Category</div>
|
|
233
|
+
<div className="text-xl font-bold text-emerald-800">
|
|
234
|
+
{orderData.length > 0 ? orderData.sort((a: any, b: any) => b.total - a.total)[0]?.category || 'N/A' : 'Electronics'}
|
|
235
|
+
</div>
|
|
236
|
+
<div className="text-xs text-emerald-600">
|
|
237
|
+
${orderData.length > 0 ? orderData.sort((a: any, b: any) => b.total - a.total)[0]?.total?.toLocaleString() || '0' : '8,750'}
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
{/* Average Order Value */}
|
|
242
|
+
<div className="p-4 bg-gradient-to-r from-blue-50 to-violet-50 rounded-lg border border-blue-100">
|
|
243
|
+
<div className="text-sm text-blue-600 font-medium">Average Order Value</div>
|
|
244
|
+
<div className="text-xl font-bold text-blue-800">
|
|
245
|
+
${averageOrderValue.toFixed(0)}
|
|
246
|
+
</div>
|
|
247
|
+
<div className="text-xs text-blue-600">
|
|
248
|
+
+12% vs last month
|
|
249
|
+
</div>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
{/* Total Revenue */}
|
|
253
|
+
<div className="p-4 bg-gradient-to-r from-violet-50 to-purple-50 rounded-lg border border-violet-100">
|
|
254
|
+
<div className="text-sm text-violet-600 font-medium">Total Revenue</div>
|
|
255
|
+
<div className="text-xl font-bold text-violet-800">
|
|
256
|
+
${(totalRevenue / 1000).toFixed(1)}K
|
|
257
|
+
</div>
|
|
258
|
+
<div className="text-xs text-violet-600">
|
|
259
|
+
{monthlyRevenueData.length} active months
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
|
|
263
|
+
{/* Revenue Growth Trend */}
|
|
264
|
+
<div className="p-4 bg-gradient-to-r from-amber-50 to-orange-50 rounded-lg border border-amber-100">
|
|
265
|
+
<div className="text-sm text-amber-600 font-medium">Growth Trend</div>
|
|
266
|
+
<div className="text-xl font-bold text-amber-800">
|
|
267
|
+
+{totalRevenue > 0 ? ((totalRevenue / 50000) * 100).toFixed(1) : 15.3}%
|
|
268
|
+
</div>
|
|
269
|
+
<div className="mt-2">
|
|
270
|
+
<SparkAreaChart
|
|
271
|
+
data={monthlyRevenueData.slice(-6)}
|
|
272
|
+
categories={["revenue"]}
|
|
273
|
+
colors={["amber"]}
|
|
274
|
+
className="h-6 w-full"
|
|
275
|
+
/>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
</div>
|
|
279
|
+
</CardContent>
|
|
280
|
+
</Card>
|
|
281
|
+
|
|
282
|
+
{/* Revenue Distribution */}
|
|
283
|
+
<Card>
|
|
284
|
+
<CardHeader>
|
|
285
|
+
<CardTitle>Revenue Distribution</CardTitle>
|
|
286
|
+
<CardDescription>
|
|
287
|
+
Breakdown by category
|
|
288
|
+
</CardDescription>
|
|
289
|
+
</CardHeader>
|
|
290
|
+
<CardContent>
|
|
291
|
+
<div className="space-y-4">
|
|
292
|
+
{orderData.slice(0, 5).map((category: any, index: number) => {
|
|
293
|
+
const percentage = Math.round((category.total / orderData.reduce((sum: number, cat: any) => sum + cat.total, 0)) * 100);
|
|
294
|
+
const colors = ['bg-emerald-500', 'bg-blue-500', 'bg-violet-500', 'bg-amber-500', 'bg-rose-500'];
|
|
295
|
+
const bgColors = ['bg-emerald-50', 'bg-blue-50', 'bg-violet-50', 'bg-amber-50', 'bg-rose-50'];
|
|
296
|
+
const textColors = ['text-emerald-600', 'text-blue-600', 'text-violet-600', 'text-amber-600', 'text-rose-600'];
|
|
297
|
+
const borderColors = ['border-emerald-200', 'border-blue-200', 'border-violet-200', 'border-amber-200', 'border-rose-200'];
|
|
298
|
+
|
|
299
|
+
return (
|
|
300
|
+
<div key={index} className={`p-3 rounded-lg border ${bgColors[index] || 'bg-gray-50'} ${borderColors[index] || 'border-gray-200'}`}>
|
|
301
|
+
<div className="flex items-center justify-between">
|
|
302
|
+
<div className="flex items-center space-x-2">
|
|
303
|
+
<div className={`w-3 h-3 rounded-full ${colors[index] || 'bg-gray-400'}`} />
|
|
304
|
+
<span className="text-sm font-medium">{category.category}</span>
|
|
305
|
+
</div>
|
|
306
|
+
<div className={`text-sm font-bold ${textColors[index] || 'text-gray-600'}`}>
|
|
307
|
+
${category.total.toLocaleString()}
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
<div className="flex items-center justify-between mt-1">
|
|
311
|
+
<span className="text-xs text-gray-500">{percentage}% of total</span>
|
|
312
|
+
<span className="text-xs text-gray-500">{category.count} orders</span>
|
|
313
|
+
</div>
|
|
314
|
+
<div className="w-full bg-gray-200 rounded-full h-1.5 mt-2">
|
|
315
|
+
<div
|
|
316
|
+
className={`h-1.5 rounded-full ${colors[index] || 'bg-gray-400'}`}
|
|
317
|
+
style={{ width: `${percentage}%` }}
|
|
318
|
+
/>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
);
|
|
322
|
+
})}
|
|
323
|
+
</div>
|
|
324
|
+
</CardContent>
|
|
325
|
+
</Card>
|
|
326
|
+
</div>
|
|
327
|
+
|
|
328
|
+
{/* Revenue Analytics Dashboard */}
|
|
329
|
+
<div className="grid grid-cols-1 xl:grid-cols-2 gap-6">
|
|
330
|
+
<Card className="xl:col-span-2">
|
|
331
|
+
<CardHeader>
|
|
332
|
+
<div className="flex items-center justify-between">
|
|
333
|
+
<div>
|
|
334
|
+
<CardTitle className="text-xl font-bold flex items-center gap-2">
|
|
335
|
+
<DollarSign className="h-5 w-5 text-blue-600" />
|
|
336
|
+
Revenue Analytics
|
|
337
|
+
</CardTitle>
|
|
338
|
+
<CardDescription className="text-sm text-gray-600 mt-1">
|
|
339
|
+
Comprehensive revenue analysis with trends, forecasting, and performance metrics
|
|
340
|
+
</CardDescription>
|
|
341
|
+
</div>
|
|
342
|
+
<div className="flex items-center space-x-2">
|
|
343
|
+
<Badge variant="outline" className="text-blue-600 border-blue-200">
|
|
344
|
+
${(totalRevenue / 1000).toFixed(1)}K Total
|
|
345
|
+
</Badge>
|
|
346
|
+
<Badge variant="outline" className="text-emerald-600 border-emerald-200">
|
|
347
|
+
+{totalRevenue > 0 ? ((totalRevenue / 50000) * 100).toFixed(1) : 15.3}% Growth
|
|
348
|
+
</Badge>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
</CardHeader>
|
|
352
|
+
<CardContent>
|
|
353
|
+
<div className="space-y-6">
|
|
354
|
+
{/* Revenue Metrics Grid */}
|
|
355
|
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
|
356
|
+
<div className="p-4 bg-gradient-to-br from-emerald-50 to-emerald-100 rounded-lg border border-emerald-200">
|
|
357
|
+
<div className="flex items-center justify-between mb-2">
|
|
358
|
+
<DollarSign className="h-5 w-5 text-emerald-600" />
|
|
359
|
+
<TrendingUp className="h-4 w-4 text-emerald-600" />
|
|
360
|
+
</div>
|
|
361
|
+
<div className="text-2xl font-bold text-emerald-800">${(totalRevenue / 1000).toFixed(1)}K</div>
|
|
362
|
+
<div className="text-sm text-emerald-600 font-medium">Total Revenue</div>
|
|
363
|
+
<div className="text-xs text-emerald-600">+12.5% vs last month</div>
|
|
364
|
+
</div>
|
|
365
|
+
|
|
366
|
+
<div className="p-4 bg-gradient-to-br from-blue-50 to-blue-100 rounded-lg border border-blue-200">
|
|
367
|
+
<div className="flex items-center justify-between mb-2">
|
|
368
|
+
<TrendingUp className="h-5 w-5 text-blue-600" />
|
|
369
|
+
<TrendingUp className="h-4 w-4 text-blue-600" />
|
|
370
|
+
</div>
|
|
371
|
+
<div className="text-2xl font-bold text-blue-800">${averageOrderValue.toFixed(0)}</div>
|
|
372
|
+
<div className="text-sm text-blue-600 font-medium">Avg Order Value</div>
|
|
373
|
+
<div className="text-xs text-blue-600">+8.2% vs last month</div>
|
|
374
|
+
</div>
|
|
375
|
+
|
|
376
|
+
<div className="p-4 bg-gradient-to-br from-violet-50 to-violet-100 rounded-lg border border-violet-200">
|
|
377
|
+
<div className="flex items-center justify-between mb-2">
|
|
378
|
+
<Users className="h-5 w-5 text-violet-600" />
|
|
379
|
+
<TrendingUp className="h-4 w-4 text-violet-600" />
|
|
380
|
+
</div>
|
|
381
|
+
<div className="text-2xl font-bold text-violet-800">{monthlyRevenueData.length}</div>
|
|
382
|
+
<div className="text-sm text-violet-600 font-medium">Active Months</div>
|
|
383
|
+
<div className="text-xs text-violet-600">Growing steadily</div>
|
|
384
|
+
</div>
|
|
385
|
+
|
|
386
|
+
<div className="p-4 bg-gradient-to-br from-amber-50 to-amber-100 rounded-lg border border-amber-200">
|
|
387
|
+
<div className="flex items-center justify-between mb-2">
|
|
388
|
+
<ShoppingCart className="h-5 w-5 text-amber-600" />
|
|
389
|
+
<TrendingUp className="h-4 w-4 text-amber-600" />
|
|
390
|
+
</div>
|
|
391
|
+
<div className="text-2xl font-bold text-amber-800">{totalOrders}</div>
|
|
392
|
+
<div className="text-sm text-amber-600 font-medium">Total Orders</div>
|
|
393
|
+
<div className="text-xs text-amber-600">+15.3% growth</div>
|
|
394
|
+
</div>
|
|
395
|
+
</div>
|
|
396
|
+
|
|
397
|
+
{/* Revenue Trend Analysis */}
|
|
398
|
+
{monthlyRevenueData.length > 0 && (
|
|
399
|
+
<div className="bg-gradient-to-r from-blue-50 to-indigo-50 p-6 rounded-lg border border-blue-200">
|
|
400
|
+
<h3 className="text-lg font-semibold text-blue-800 mb-4 flex items-center gap-2">
|
|
401
|
+
<TrendingUp className="h-5 w-5 text-blue-600" />
|
|
402
|
+
Revenue Trend Analysis
|
|
403
|
+
</h3>
|
|
404
|
+
<AreaChart
|
|
405
|
+
className="h-80"
|
|
406
|
+
data={areaChartData}
|
|
407
|
+
index="month"
|
|
408
|
+
categories={["revenue", "cumulative"]}
|
|
409
|
+
colors={["blue", "emerald"]}
|
|
410
|
+
valueFormatter={(number: number) =>
|
|
411
|
+
`$${Intl.NumberFormat("us").format(number)}`
|
|
412
|
+
}
|
|
413
|
+
showLegend={true}
|
|
414
|
+
showGridLines={true}
|
|
415
|
+
showXAxis={true}
|
|
416
|
+
showYAxis={true}
|
|
417
|
+
/>
|
|
418
|
+
</div>
|
|
419
|
+
)}
|
|
420
|
+
|
|
421
|
+
{/* Revenue Distribution by Category */}
|
|
422
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
423
|
+
<div className="bg-white p-6 rounded-lg border border-gray-200">
|
|
424
|
+
<h3 className="text-lg font-semibold text-gray-800 mb-4 flex items-center gap-2">
|
|
425
|
+
<PieChart className="h-5 w-5 text-gray-600" />
|
|
426
|
+
Revenue Distribution
|
|
427
|
+
</h3>
|
|
428
|
+
<DonutChart
|
|
429
|
+
className="h-64"
|
|
430
|
+
data={donutChartData}
|
|
431
|
+
category="value"
|
|
432
|
+
index="name"
|
|
433
|
+
valueFormatter={(number: number) =>
|
|
434
|
+
`$${Intl.NumberFormat("us").format(number)}`
|
|
435
|
+
}
|
|
436
|
+
colors={["emerald", "blue", "violet", "amber", "rose", "cyan"]}
|
|
437
|
+
/>
|
|
438
|
+
</div>
|
|
439
|
+
|
|
440
|
+
<div className="bg-white p-6 rounded-lg border border-gray-200">
|
|
441
|
+
<h3 className="text-lg font-semibold text-gray-800 mb-4">Revenue Insights</h3>
|
|
442
|
+
<div className="space-y-4">
|
|
443
|
+
{/* Top Revenue Category */}
|
|
444
|
+
<div className="p-4 bg-gradient-to-r from-emerald-50 to-emerald-100 rounded-lg border border-emerald-200">
|
|
445
|
+
<div className="flex items-center justify-between">
|
|
446
|
+
<div>
|
|
447
|
+
<div className="text-sm text-emerald-600 font-medium">Top Revenue Category</div>
|
|
448
|
+
<div className="text-xl font-bold text-emerald-800">
|
|
449
|
+
{orderData.length > 0 ? orderData.sort((a: any, b: any) => b.total - a.total)[0]?.category || 'N/A' : 'Electronics'}
|
|
450
|
+
</div>
|
|
451
|
+
</div>
|
|
452
|
+
<div className="text-right">
|
|
453
|
+
<div className="text-lg font-bold text-emerald-700">
|
|
454
|
+
${orderData.length > 0 ? orderData.sort((a: any, b: any) => b.total - a.total)[0]?.total?.toLocaleString() || '0' : '8,750'}
|
|
455
|
+
</div>
|
|
456
|
+
<div className="text-xs text-emerald-600">
|
|
457
|
+
{orderData.length > 0 ? Math.round((orderData.sort((a: any, b: any) => b.total - a.total)[0]?.total || 0) / orderData.reduce((sum: number, cat: any) => sum + cat.total, 0) * 100) : 35}% of total
|
|
458
|
+
</div>
|
|
459
|
+
</div>
|
|
460
|
+
</div>
|
|
461
|
+
</div>
|
|
462
|
+
|
|
463
|
+
{/* Revenue Growth Prediction */}
|
|
464
|
+
<div className="p-4 bg-gradient-to-r from-blue-50 to-blue-100 rounded-lg border border-blue-200">
|
|
465
|
+
<div className="text-sm text-blue-600 font-medium">Projected Growth</div>
|
|
466
|
+
<div className="text-xl font-bold text-blue-800">
|
|
467
|
+
+{totalRevenue > 0 ? ((totalRevenue / 50000) * 100).toFixed(1) : 18.5}%
|
|
468
|
+
</div>
|
|
469
|
+
<div className="text-xs text-blue-600">Next quarter forecast</div>
|
|
470
|
+
<div className="mt-2">
|
|
471
|
+
<SparkAreaChart
|
|
472
|
+
data={monthlyRevenueData.slice(-6)}
|
|
473
|
+
categories={["revenue"]}
|
|
474
|
+
colors={["blue"]}
|
|
475
|
+
className="h-8 w-full"
|
|
476
|
+
/>
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
|
|
480
|
+
{/* Performance Score */}
|
|
481
|
+
<div className="p-4 bg-gradient-to-r from-violet-50 to-violet-100 rounded-lg border border-violet-200">
|
|
482
|
+
<div className="text-sm text-violet-600 font-medium">Performance Score</div>
|
|
483
|
+
<div className="flex items-center space-x-2">
|
|
484
|
+
<div className="text-xl font-bold text-violet-800">
|
|
485
|
+
{Math.min(100, Math.round((totalRevenue / 30000) * 100))}%
|
|
486
|
+
</div>
|
|
487
|
+
<div className="flex-1 bg-violet-200 rounded-full h-2">
|
|
488
|
+
<div
|
|
489
|
+
className="bg-violet-500 h-2 rounded-full transition-all duration-1000"
|
|
490
|
+
style={{ width: `${Math.min(100, Math.round((totalRevenue / 30000) * 100))}%` }}
|
|
491
|
+
/>
|
|
492
|
+
</div>
|
|
493
|
+
</div>
|
|
494
|
+
<div className="text-xs text-violet-600 mt-1">Excellent performance</div>
|
|
495
|
+
</div>
|
|
496
|
+
</div>
|
|
497
|
+
</div>
|
|
498
|
+
</div>
|
|
499
|
+
</div>
|
|
500
|
+
</CardContent>
|
|
501
|
+
</Card>
|
|
502
|
+
</div>
|
|
503
|
+
</div>
|
|
504
|
+
);
|
|
505
|
+
};
|