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,313 @@
|
|
|
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 { ShoppingCart, TrendingUp } from 'lucide-react';
|
|
5
|
+
import {
|
|
6
|
+
ResponsiveContainer,
|
|
7
|
+
AreaChart as RechartsAreaChart,
|
|
8
|
+
BarChart as RechartsBarChart,
|
|
9
|
+
Area,
|
|
10
|
+
Bar,
|
|
11
|
+
XAxis,
|
|
12
|
+
YAxis,
|
|
13
|
+
CartesianGrid,
|
|
14
|
+
Tooltip,
|
|
15
|
+
Legend,
|
|
16
|
+
Cell
|
|
17
|
+
} from 'recharts';
|
|
18
|
+
import { DonutChart } from '@/components/charts/DonutChart';
|
|
19
|
+
|
|
20
|
+
interface SalesAnalyticsProps {
|
|
21
|
+
orderData: any[];
|
|
22
|
+
totalOrders: number;
|
|
23
|
+
totalRevenue: number;
|
|
24
|
+
averageOrderValue: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const SalesAnalytics: React.FC<SalesAnalyticsProps> = ({
|
|
28
|
+
orderData,
|
|
29
|
+
}) => {
|
|
30
|
+
// Process data for charts
|
|
31
|
+
const areaChartData = orderData.map((item: any, index: number) => ({
|
|
32
|
+
category: item.category,
|
|
33
|
+
revenue: item.total,
|
|
34
|
+
orders: item.count * 10, // Scale for visualization
|
|
35
|
+
trend: item.total * (1 + (index * 0.1)) // Mock trend data
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
const barChartData = orderData.map((item: any) => ({
|
|
39
|
+
category: item.category,
|
|
40
|
+
revenue: item.total,
|
|
41
|
+
orders: item.count,
|
|
42
|
+
avgOrderValue: Math.round(item.total / item.count),
|
|
43
|
+
performance: (item.total / orderData.reduce((sum: number, cat: any) => sum + cat.total, 0)) * 100
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
const donutChartData = orderData.map((item: any) => ({
|
|
47
|
+
category: item.category,
|
|
48
|
+
sales: item.total
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="space-y-6">
|
|
53
|
+
{/* Sales Analytics Row */}
|
|
54
|
+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
55
|
+
{/* Enhanced Sales by Category */}
|
|
56
|
+
<Card>
|
|
57
|
+
<CardHeader className="pb-4">
|
|
58
|
+
<div className="flex items-center justify-between">
|
|
59
|
+
<div>
|
|
60
|
+
<CardTitle className="text-lg font-bold">Sales by Category</CardTitle>
|
|
61
|
+
<CardDescription className="text-sm text-gray-600">
|
|
62
|
+
Performance breakdown by product categories
|
|
63
|
+
</CardDescription>
|
|
64
|
+
</div>
|
|
65
|
+
<Badge variant="outline" className="text-emerald-600 border-emerald-200">
|
|
66
|
+
{orderData.length} Categories
|
|
67
|
+
</Badge>
|
|
68
|
+
</div>
|
|
69
|
+
</CardHeader>
|
|
70
|
+
<CardContent>
|
|
71
|
+
{orderData.length > 0 ? (
|
|
72
|
+
<div className="space-y-4">
|
|
73
|
+
{/* Category Trend Overview */}
|
|
74
|
+
<div className="grid grid-cols-3 gap-4 p-4 bg-gradient-to-r from-emerald-50 to-blue-50 rounded-lg border border-emerald-100">
|
|
75
|
+
<div className="text-center">
|
|
76
|
+
<div className="text-xl font-bold text-emerald-700">
|
|
77
|
+
{orderData.length}
|
|
78
|
+
</div>
|
|
79
|
+
<div className="text-xs text-emerald-600 font-medium">Categories</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div className="text-center">
|
|
82
|
+
<div className="text-xl font-bold text-blue-700">
|
|
83
|
+
${Math.max(...orderData.map((item: any) => item.total)).toLocaleString()}
|
|
84
|
+
</div>
|
|
85
|
+
<div className="text-xs text-blue-600 font-medium">Top Revenue</div>
|
|
86
|
+
</div>
|
|
87
|
+
<div className="text-center">
|
|
88
|
+
<div className="text-xl font-bold text-violet-700">
|
|
89
|
+
{Math.round(orderData.reduce((sum: number, item: any) => sum + item.count, 0) / orderData.length)}
|
|
90
|
+
</div>
|
|
91
|
+
<div className="text-xs text-violet-600 font-medium">Avg Orders</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
{/* Area Chart for Category Trends */}
|
|
96
|
+
<div className="h-80">
|
|
97
|
+
<ResponsiveContainer width="100%" height="100%">
|
|
98
|
+
<RechartsAreaChart
|
|
99
|
+
data={areaChartData}
|
|
100
|
+
margin={{ top: 20, right: 30, left: 20, bottom: 5 }}
|
|
101
|
+
>
|
|
102
|
+
<defs>
|
|
103
|
+
<linearGradient id="revenueGradient" x1="0" y1="0" x2="0" y2="1">
|
|
104
|
+
<stop offset="5%" stopColor="#10B981" stopOpacity={0.8}/>
|
|
105
|
+
<stop offset="95%" stopColor="#10B981" stopOpacity={0.1}/>
|
|
106
|
+
</linearGradient>
|
|
107
|
+
<linearGradient id="ordersGradient" x1="0" y1="0" x2="0" y2="1">
|
|
108
|
+
<stop offset="5%" stopColor="#3B82F6" stopOpacity={0.8}/>
|
|
109
|
+
<stop offset="95%" stopColor="#3B82F6" stopOpacity={0.1}/>
|
|
110
|
+
</linearGradient>
|
|
111
|
+
</defs>
|
|
112
|
+
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
|
|
113
|
+
<XAxis
|
|
114
|
+
dataKey="category"
|
|
115
|
+
tick={{ fontSize: 12 }}
|
|
116
|
+
angle={-45}
|
|
117
|
+
textAnchor="end"
|
|
118
|
+
height={80}
|
|
119
|
+
/>
|
|
120
|
+
<YAxis tick={{ fontSize: 12 }} />
|
|
121
|
+
<Tooltip
|
|
122
|
+
formatter={(value: any, name: string) => [
|
|
123
|
+
name === 'revenue' ? `$${value.toLocaleString()}` : `${Math.round(value / 10)} orders`,
|
|
124
|
+
name === 'revenue' ? 'Revenue' : 'Orders'
|
|
125
|
+
]}
|
|
126
|
+
labelStyle={{ color: '#374151' }}
|
|
127
|
+
contentStyle={{
|
|
128
|
+
backgroundColor: '#fff',
|
|
129
|
+
border: '1px solid #e5e7eb',
|
|
130
|
+
borderRadius: '8px',
|
|
131
|
+
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)'
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
134
|
+
<Legend />
|
|
135
|
+
<Area
|
|
136
|
+
type="monotone"
|
|
137
|
+
dataKey="revenue"
|
|
138
|
+
stroke="#10B981"
|
|
139
|
+
fillOpacity={1}
|
|
140
|
+
fill="url(#revenueGradient)"
|
|
141
|
+
strokeWidth={2}
|
|
142
|
+
/>
|
|
143
|
+
<Area
|
|
144
|
+
type="monotone"
|
|
145
|
+
dataKey="orders"
|
|
146
|
+
stroke="#3B82F6"
|
|
147
|
+
fillOpacity={1}
|
|
148
|
+
fill="url(#ordersGradient)"
|
|
149
|
+
strokeWidth={2}
|
|
150
|
+
/>
|
|
151
|
+
</RechartsAreaChart>
|
|
152
|
+
</ResponsiveContainer>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
) : (
|
|
156
|
+
<div className="text-center text-gray-500 py-12">
|
|
157
|
+
<ShoppingCart className="h-12 w-12 mx-auto text-gray-300 mb-4" />
|
|
158
|
+
<p className="text-lg font-medium">No category data available</p>
|
|
159
|
+
<p className="text-sm">Start selling to see category breakdown</p>
|
|
160
|
+
</div>
|
|
161
|
+
)}
|
|
162
|
+
</CardContent>
|
|
163
|
+
</Card>
|
|
164
|
+
|
|
165
|
+
{/* Sales by Category Chart */}
|
|
166
|
+
<Card>
|
|
167
|
+
<CardHeader>
|
|
168
|
+
<CardTitle>Sales by Category</CardTitle>
|
|
169
|
+
<CardDescription>
|
|
170
|
+
Revenue breakdown by product category
|
|
171
|
+
</CardDescription>
|
|
172
|
+
</CardHeader>
|
|
173
|
+
<CardContent>
|
|
174
|
+
<DonutChart
|
|
175
|
+
className="h-64"
|
|
176
|
+
data={donutChartData}
|
|
177
|
+
category="sales"
|
|
178
|
+
index="category"
|
|
179
|
+
valueFormatter={(number: number) =>
|
|
180
|
+
`$${Intl.NumberFormat("us").format(number)}`
|
|
181
|
+
}
|
|
182
|
+
colors={["slate", "violet", "indigo", "rose", "cyan", "amber"]}
|
|
183
|
+
/>
|
|
184
|
+
</CardContent>
|
|
185
|
+
</Card>
|
|
186
|
+
</div>
|
|
187
|
+
|
|
188
|
+
{/* Top Performing Categories Analytics */}
|
|
189
|
+
<Card>
|
|
190
|
+
<CardHeader>
|
|
191
|
+
<div className="flex items-center justify-between">
|
|
192
|
+
<div>
|
|
193
|
+
<CardTitle className="text-xl font-bold flex items-center gap-2">
|
|
194
|
+
<TrendingUp className="h-5 w-5 text-emerald-600" />
|
|
195
|
+
Top Performing Categories
|
|
196
|
+
</CardTitle>
|
|
197
|
+
<CardDescription className="text-sm text-gray-600 mt-1">
|
|
198
|
+
Comprehensive category performance analysis with revenue and order metrics
|
|
199
|
+
</CardDescription>
|
|
200
|
+
</div>
|
|
201
|
+
<Badge variant="outline" className="text-emerald-600 border-emerald-200">
|
|
202
|
+
{orderData.length} Active Categories
|
|
203
|
+
</Badge>
|
|
204
|
+
</div>
|
|
205
|
+
</CardHeader>
|
|
206
|
+
<CardContent>
|
|
207
|
+
{orderData.length > 0 ? (
|
|
208
|
+
<div className="space-y-6">
|
|
209
|
+
{/* Category Performance Overview */}
|
|
210
|
+
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
|
|
211
|
+
{orderData.slice(0, 4).map((category: any, index: number) => {
|
|
212
|
+
const colors = ['bg-emerald-50 text-emerald-700 border-emerald-200', 'bg-blue-50 text-blue-700 border-blue-200',
|
|
213
|
+
'bg-violet-50 text-violet-700 border-violet-200', 'bg-amber-50 text-amber-700 border-amber-200'];
|
|
214
|
+
const iconColors = ['text-emerald-500', 'text-blue-500', 'text-violet-500', 'text-amber-500'];
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<div key={index} className={`p-4 rounded-lg border ${colors[index] || 'bg-gray-50 text-gray-700 border-gray-200'}`}>
|
|
218
|
+
<div className="flex items-center justify-between mb-2">
|
|
219
|
+
<ShoppingCart className={`h-4 w-4 ${iconColors[index] || 'text-gray-500'}`} />
|
|
220
|
+
<span className="text-xs font-medium">#{index + 1}</span>
|
|
221
|
+
</div>
|
|
222
|
+
<div className="text-lg font-bold">${category.total.toLocaleString()}</div>
|
|
223
|
+
<div className="text-xs font-medium mb-1">{category.category}</div>
|
|
224
|
+
<div className="text-xs opacity-75">{category.count} orders</div>
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
})}
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
{/* Enhanced Bar Chart for Category Performance */}
|
|
231
|
+
<div className="bg-gradient-to-r from-slate-50 to-gray-50 p-6 rounded-lg border border-slate-200">
|
|
232
|
+
<h3 className="text-lg font-semibold text-slate-800 mb-4 flex items-center gap-2">
|
|
233
|
+
📊 Revenue by Category Performance
|
|
234
|
+
</h3>
|
|
235
|
+
<div className="h-80">
|
|
236
|
+
<ResponsiveContainer width="100%" height="100%">
|
|
237
|
+
<RechartsBarChart
|
|
238
|
+
data={barChartData}
|
|
239
|
+
margin={{ top: 20, right: 30, left: 20, bottom: 60 }}
|
|
240
|
+
>
|
|
241
|
+
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
|
|
242
|
+
<XAxis
|
|
243
|
+
dataKey="category"
|
|
244
|
+
tick={{ fontSize: 12, fill: '#64748b' }}
|
|
245
|
+
angle={-45}
|
|
246
|
+
textAnchor="end"
|
|
247
|
+
height={80}
|
|
248
|
+
/>
|
|
249
|
+
<YAxis tick={{ fontSize: 12, fill: '#64748b' }} />
|
|
250
|
+
<Tooltip
|
|
251
|
+
formatter={(value: any, name: string) => [
|
|
252
|
+
name === 'revenue' ? `$${value.toLocaleString()}` : value,
|
|
253
|
+
name === 'revenue' ? 'Revenue' : name === 'orders' ? 'Orders' : 'Avg Order Value'
|
|
254
|
+
]}
|
|
255
|
+
labelStyle={{ color: '#374151', fontWeight: 'bold' }}
|
|
256
|
+
contentStyle={{
|
|
257
|
+
backgroundColor: '#fff',
|
|
258
|
+
border: '1px solid #e5e7eb',
|
|
259
|
+
borderRadius: '8px',
|
|
260
|
+
boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)'
|
|
261
|
+
}}
|
|
262
|
+
/>
|
|
263
|
+
<Legend />
|
|
264
|
+
<Bar dataKey="revenue" name="Revenue" radius={[4, 4, 0, 0]}>
|
|
265
|
+
{orderData.map((_entry: any, index: number) => {
|
|
266
|
+
const colors = ['#10B981', '#3B82F6', '#8B5CF6', '#F59E0B', '#F43F5E', '#06B6D4', '#64748B', '#6366F1'];
|
|
267
|
+
return <Cell key={`cell-${index}`} fill={colors[index % colors.length]} />;
|
|
268
|
+
})}
|
|
269
|
+
</Bar>
|
|
270
|
+
<Bar dataKey="orders" name="Orders" radius={[4, 4, 0, 0]} fill="#94A3B8" fillOpacity={0.7} />
|
|
271
|
+
</RechartsBarChart>
|
|
272
|
+
</ResponsiveContainer>
|
|
273
|
+
</div>
|
|
274
|
+
</div>
|
|
275
|
+
|
|
276
|
+
{/* Category Performance Details */}
|
|
277
|
+
<div className="space-y-3">
|
|
278
|
+
<h3 className="text-sm font-semibold text-gray-700">Detailed Performance Breakdown</h3>
|
|
279
|
+
{orderData.map((category: any, index: number) => {
|
|
280
|
+
const percentage = Math.round((category.total / orderData.reduce((sum: number, cat: any) => sum + cat.total, 0)) * 100);
|
|
281
|
+
const avgOrder = Math.round(category.total / category.count);
|
|
282
|
+
const colors = ['bg-emerald-500', 'bg-blue-500', 'bg-violet-500', 'bg-amber-500', 'bg-rose-500', 'bg-cyan-500', 'bg-indigo-500'];
|
|
283
|
+
|
|
284
|
+
return (
|
|
285
|
+
<div key={index} className="flex items-center justify-between p-4 bg-white rounded-lg border border-gray-100 hover:shadow-sm transition-shadow">
|
|
286
|
+
<div className="flex items-center space-x-4">
|
|
287
|
+
<div className={`w-4 h-4 rounded-full ${colors[index] || 'bg-gray-400'}`} />
|
|
288
|
+
<div>
|
|
289
|
+
<div className="font-medium text-gray-900">{category.category}</div>
|
|
290
|
+
<div className="text-sm text-gray-500">{category.count} orders • Avg: ${avgOrder}</div>
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
<div className="text-right">
|
|
294
|
+
<div className="font-bold text-gray-900">${category.total.toLocaleString()}</div>
|
|
295
|
+
<div className="text-sm text-gray-500">{percentage}% of total</div>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
);
|
|
299
|
+
})}
|
|
300
|
+
</div>
|
|
301
|
+
</div>
|
|
302
|
+
) : (
|
|
303
|
+
<div className="text-center text-gray-500 py-16">
|
|
304
|
+
<ShoppingCart className="h-16 w-16 mx-auto text-gray-300 mb-4" />
|
|
305
|
+
<p className="text-lg font-medium">No category data available</p>
|
|
306
|
+
<p className="text-sm">Start selling products to see category performance</p>
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
</CardContent>
|
|
310
|
+
</Card>
|
|
311
|
+
</div>
|
|
312
|
+
);
|
|
313
|
+
};
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
3
|
+
import { Badge } from '@/components/ui/badge';
|
|
4
|
+
|
|
5
|
+
// Simple table components since they're not in the UI library
|
|
6
|
+
const Table = ({ children, className = "" }: { children: React.ReactNode, className?: string }) => (
|
|
7
|
+
<div className={`overflow-x-auto ${className}`}>
|
|
8
|
+
<table className="min-w-full divide-y divide-gray-200">
|
|
9
|
+
{children}
|
|
10
|
+
</table>
|
|
11
|
+
</div>
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const TableHead = ({ children }: { children: React.ReactNode }) => (
|
|
15
|
+
<thead className="bg-gray-50">{children}</thead>
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const TableBody = ({ children }: { children: React.ReactNode }) => (
|
|
19
|
+
<tbody className="bg-white divide-y divide-gray-200">{children}</tbody>
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
const TableRow = ({ children }: { children: React.ReactNode }) => (
|
|
23
|
+
<tr className="hover:bg-gray-50">{children}</tr>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const TableHeaderCell = ({ children }: { children: React.ReactNode }) => (
|
|
27
|
+
<th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
|
28
|
+
{children}
|
|
29
|
+
</th>
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const TableCell = ({ children, className = "" }: { children: React.ReactNode, className?: string }) => (
|
|
33
|
+
<td className={`px-6 py-4 whitespace-nowrap text-sm text-gray-900 ${className}`}>
|
|
34
|
+
{children}
|
|
35
|
+
</td>
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
interface Transaction {
|
|
39
|
+
id: string;
|
|
40
|
+
customer: string;
|
|
41
|
+
amount: number;
|
|
42
|
+
category: string;
|
|
43
|
+
payment_method: string;
|
|
44
|
+
date: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface TransactionsTableProps {
|
|
48
|
+
recentTransactions: Transaction[];
|
|
49
|
+
totalOrders: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const TransactionsTable: React.FC<TransactionsTableProps> = ({
|
|
53
|
+
recentTransactions,
|
|
54
|
+
totalOrders
|
|
55
|
+
}) => {
|
|
56
|
+
return (
|
|
57
|
+
<div className="space-y-6">
|
|
58
|
+
{/* Enhanced Recent Transactions Table */}
|
|
59
|
+
<Card>
|
|
60
|
+
<CardHeader>
|
|
61
|
+
<div className="flex items-center justify-between">
|
|
62
|
+
<div>
|
|
63
|
+
<CardTitle className="text-lg font-bold flex items-center gap-2">
|
|
64
|
+
📋 Recent Transactions
|
|
65
|
+
</CardTitle>
|
|
66
|
+
<CardDescription>
|
|
67
|
+
Latest customer transactions with comprehensive details and analytics
|
|
68
|
+
</CardDescription>
|
|
69
|
+
</div>
|
|
70
|
+
<div className="flex items-center space-x-2">
|
|
71
|
+
<Badge variant="outline" className="text-blue-600 border-blue-200">
|
|
72
|
+
{recentTransactions.length} Recent
|
|
73
|
+
</Badge>
|
|
74
|
+
<Badge variant="outline" className="text-emerald-600 border-emerald-200">
|
|
75
|
+
${recentTransactions.reduce((sum: number, t: any) => sum + t.amount, 0).toLocaleString()} Total
|
|
76
|
+
</Badge>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</CardHeader>
|
|
80
|
+
<CardContent>
|
|
81
|
+
{recentTransactions.length > 0 ? (
|
|
82
|
+
<div className="space-y-4">
|
|
83
|
+
{/* Transaction Summary Cards */}
|
|
84
|
+
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 p-4 bg-gradient-to-r from-gray-50 to-slate-50 rounded-lg border border-gray-100">
|
|
85
|
+
<div className="text-center">
|
|
86
|
+
<div className="text-xl font-bold text-gray-700">
|
|
87
|
+
{recentTransactions.length}
|
|
88
|
+
</div>
|
|
89
|
+
<div className="text-xs text-gray-600 font-medium">Total Transactions</div>
|
|
90
|
+
</div>
|
|
91
|
+
<div className="text-center">
|
|
92
|
+
<div className="text-xl font-bold text-emerald-700">
|
|
93
|
+
${Math.round(recentTransactions.reduce((sum: number, t: any) => sum + t.amount, 0) / recentTransactions.length).toLocaleString()}
|
|
94
|
+
</div>
|
|
95
|
+
<div className="text-xs text-emerald-600 font-medium">Average Value</div>
|
|
96
|
+
</div>
|
|
97
|
+
<div className="text-center">
|
|
98
|
+
<div className="text-xl font-bold text-blue-700">
|
|
99
|
+
${Math.max(...recentTransactions.map((t: any) => t.amount)).toLocaleString()}
|
|
100
|
+
</div>
|
|
101
|
+
<div className="text-xs text-blue-600 font-medium">Highest Transaction</div>
|
|
102
|
+
</div>
|
|
103
|
+
<div className="text-center">
|
|
104
|
+
<div className="text-xl font-bold text-violet-700">
|
|
105
|
+
{new Set(recentTransactions.map((t: any) => t.category)).size}
|
|
106
|
+
</div>
|
|
107
|
+
<div className="text-xs text-violet-600 font-medium">Categories</div>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
{/* Enhanced Data Table */}
|
|
112
|
+
<div className="overflow-hidden rounded-lg border border-gray-200 shadow-sm">
|
|
113
|
+
<Table className="min-w-full">
|
|
114
|
+
<TableHead>
|
|
115
|
+
<TableRow>
|
|
116
|
+
<TableHeaderCell>Transaction ID</TableHeaderCell>
|
|
117
|
+
<TableHeaderCell>Customer</TableHeaderCell>
|
|
118
|
+
<TableHeaderCell>Category</TableHeaderCell>
|
|
119
|
+
<TableHeaderCell>Amount</TableHeaderCell>
|
|
120
|
+
<TableHeaderCell>Payment Method</TableHeaderCell>
|
|
121
|
+
<TableHeaderCell>Date</TableHeaderCell>
|
|
122
|
+
<TableHeaderCell>Status</TableHeaderCell>
|
|
123
|
+
</TableRow>
|
|
124
|
+
</TableHead>
|
|
125
|
+
<TableBody>
|
|
126
|
+
{recentTransactions.map((transaction: any) => (
|
|
127
|
+
<TableRow key={transaction.id}>
|
|
128
|
+
<TableCell className="font-mono text-sm font-medium text-gray-900">
|
|
129
|
+
#{transaction.id}
|
|
130
|
+
</TableCell>
|
|
131
|
+
<TableCell className="font-medium text-gray-900">
|
|
132
|
+
<div className="flex items-center space-x-2">
|
|
133
|
+
<div className="w-8 h-8 bg-gradient-to-br from-blue-400 to-blue-600 rounded-full flex items-center justify-center text-white text-xs font-bold">
|
|
134
|
+
{transaction.customer.split(' ').map((n: string) => n[0]).join('')}
|
|
135
|
+
</div>
|
|
136
|
+
<span>{transaction.customer}</span>
|
|
137
|
+
</div>
|
|
138
|
+
</TableCell>
|
|
139
|
+
<TableCell>
|
|
140
|
+
<Badge
|
|
141
|
+
variant="outline"
|
|
142
|
+
className={`text-xs ${
|
|
143
|
+
transaction.category === 'Electronics' ? 'text-blue-600 border-blue-200 bg-blue-50' :
|
|
144
|
+
transaction.category === 'Clothing' ? 'text-emerald-600 border-emerald-200 bg-emerald-50' :
|
|
145
|
+
transaction.category === 'Sports' ? 'text-violet-600 border-violet-200 bg-violet-50' :
|
|
146
|
+
transaction.category === 'Books' ? 'text-amber-600 border-amber-200 bg-amber-50' :
|
|
147
|
+
'text-rose-600 border-rose-200 bg-rose-50'
|
|
148
|
+
}`}
|
|
149
|
+
>
|
|
150
|
+
{transaction.category}
|
|
151
|
+
</Badge>
|
|
152
|
+
</TableCell>
|
|
153
|
+
<TableCell className="font-bold text-right">
|
|
154
|
+
<div className={`text-lg ${
|
|
155
|
+
transaction.amount > 1500 ? 'text-emerald-600' :
|
|
156
|
+
transaction.amount > 1000 ? 'text-blue-600' :
|
|
157
|
+
transaction.amount > 500 ? 'text-amber-600' :
|
|
158
|
+
'text-gray-600'
|
|
159
|
+
}`}>
|
|
160
|
+
${transaction.amount.toLocaleString()}
|
|
161
|
+
</div>
|
|
162
|
+
</TableCell>
|
|
163
|
+
<TableCell>
|
|
164
|
+
<div className="flex items-center space-x-2">
|
|
165
|
+
<div className={`w-2 h-2 rounded-full ${
|
|
166
|
+
transaction.payment_method === 'Credit Card' ? 'bg-blue-500' :
|
|
167
|
+
transaction.payment_method === 'PayPal' ? 'bg-yellow-500' :
|
|
168
|
+
transaction.payment_method === 'Debit Card' ? 'bg-emerald-500' :
|
|
169
|
+
'bg-gray-500'
|
|
170
|
+
}`} />
|
|
171
|
+
<span className="text-sm">{transaction.payment_method}</span>
|
|
172
|
+
</div>
|
|
173
|
+
</TableCell>
|
|
174
|
+
<TableCell className="text-gray-500 text-sm">
|
|
175
|
+
{transaction.date}
|
|
176
|
+
</TableCell>
|
|
177
|
+
<TableCell>
|
|
178
|
+
<Badge
|
|
179
|
+
variant="outline"
|
|
180
|
+
className="text-xs text-emerald-600 border-emerald-200 bg-emerald-50"
|
|
181
|
+
>
|
|
182
|
+
Completed
|
|
183
|
+
</Badge>
|
|
184
|
+
</TableCell>
|
|
185
|
+
</TableRow>
|
|
186
|
+
))}
|
|
187
|
+
</TableBody>
|
|
188
|
+
</Table>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
{/* Table Footer with Pagination Info */}
|
|
192
|
+
<div className="flex items-center justify-between p-4 bg-gray-50 rounded-lg border border-gray-100">
|
|
193
|
+
<div className="text-sm text-gray-600">
|
|
194
|
+
Showing <span className="font-medium">{recentTransactions.length}</span> of <span className="font-medium">{totalOrders}</span> transactions
|
|
195
|
+
</div>
|
|
196
|
+
<div className="text-sm text-gray-600">
|
|
197
|
+
Total Value: <span className="font-bold text-emerald-600">${recentTransactions.reduce((sum: number, t: any) => sum + t.amount, 0).toLocaleString()}</span>
|
|
198
|
+
</div>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
) : (
|
|
202
|
+
<div className="text-center text-gray-500 py-12">
|
|
203
|
+
<div className="w-16 h-16 mx-auto mb-4 bg-gray-100 rounded-full flex items-center justify-center">
|
|
204
|
+
📊
|
|
205
|
+
</div>
|
|
206
|
+
<p className="text-lg font-medium">No transaction data available</p>
|
|
207
|
+
<p className="text-sm">Start processing transactions to see them here</p>
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
</CardContent>
|
|
211
|
+
</Card>
|
|
212
|
+
|
|
213
|
+
{/* Advanced Data Table Section */}
|
|
214
|
+
<Card>
|
|
215
|
+
<CardHeader>
|
|
216
|
+
<CardTitle>Recent Transactions</CardTitle>
|
|
217
|
+
<CardDescription>
|
|
218
|
+
Latest transaction data with comprehensive details
|
|
219
|
+
</CardDescription>
|
|
220
|
+
</CardHeader>
|
|
221
|
+
<CardContent>
|
|
222
|
+
<Table>
|
|
223
|
+
<TableHead>
|
|
224
|
+
<TableRow>
|
|
225
|
+
<TableHeaderCell>Transaction ID</TableHeaderCell>
|
|
226
|
+
<TableHeaderCell>Customer</TableHeaderCell>
|
|
227
|
+
<TableHeaderCell>Category</TableHeaderCell>
|
|
228
|
+
<TableHeaderCell>Amount</TableHeaderCell>
|
|
229
|
+
<TableHeaderCell>Payment Method</TableHeaderCell>
|
|
230
|
+
<TableHeaderCell>Date</TableHeaderCell>
|
|
231
|
+
</TableRow>
|
|
232
|
+
</TableHead>
|
|
233
|
+
<TableBody>
|
|
234
|
+
{recentTransactions.map((transaction: any) => (
|
|
235
|
+
<TableRow key={transaction.id}>
|
|
236
|
+
<TableCell className="font-medium">{transaction.id}</TableCell>
|
|
237
|
+
<TableCell>{transaction.customer}</TableCell>
|
|
238
|
+
<TableCell>
|
|
239
|
+
<Badge variant="outline" className="text-xs">
|
|
240
|
+
{transaction.category}
|
|
241
|
+
</Badge>
|
|
242
|
+
</TableCell>
|
|
243
|
+
<TableCell className="font-semibold text-emerald-600">
|
|
244
|
+
${transaction.amount.toLocaleString()}
|
|
245
|
+
</TableCell>
|
|
246
|
+
<TableCell>{transaction.payment_method}</TableCell>
|
|
247
|
+
<TableCell className="text-gray-500">{transaction.date}</TableCell>
|
|
248
|
+
</TableRow>
|
|
249
|
+
))}
|
|
250
|
+
</TableBody>
|
|
251
|
+
</Table>
|
|
252
|
+
</CardContent>
|
|
253
|
+
</Card>
|
|
254
|
+
</div>
|
|
255
|
+
);
|
|
256
|
+
};
|