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.
Files changed (34) hide show
  1. package/README.md +76 -238
  2. package/package.json +1 -1
  3. package/templates/latest/{Agent.md → AGENT.md} +21 -27
  4. package/templates/latest/README.md +15 -38
  5. package/templates/latest/clean.sh +1 -0
  6. package/templates/latest/package-lock.json +8 -8
  7. package/templates/latest/package.json +1 -1
  8. package/templates/latest/setup.sh +5 -4
  9. package/templates/latest/src/App.tsx +11 -7
  10. package/templates/latest/src/components/charts/AreaChart.tsx +80 -0
  11. package/templates/latest/src/components/charts/DonutChart.tsx +73 -0
  12. package/templates/latest/src/components/charts/SparkAreaChart.tsx +52 -0
  13. package/templates/latest/src/components/layout/dashboard-header.tsx +1 -1
  14. package/templates/latest/src/components/layout/dashboard-layout.tsx +19 -11
  15. package/templates/latest/src/components/{page → layout}/navbar.tsx +2 -0
  16. package/templates/latest/src/components/layout/sidebar.tsx +2 -1
  17. package/templates/latest/src/components/page/dashboard/DashboardMetrics.tsx +97 -0
  18. package/templates/latest/src/components/page/dashboard/PaymentMethodsAnalysis.tsx +182 -0
  19. package/templates/latest/src/components/page/dashboard/RevenueAnalytics.tsx +505 -0
  20. package/templates/latest/src/components/page/dashboard/SalesAnalytics.tsx +313 -0
  21. package/templates/latest/src/components/page/dashboard/TransactionsTable.tsx +256 -0
  22. package/templates/latest/src/components/page/dashboard/dashboard-utils.ts +147 -0
  23. package/templates/latest/src/components/page/dashboard/dashboard.tsx +185 -0
  24. package/templates/latest/src/components/ui/bluecopa-logo.tsx +3 -0
  25. package/templates/latest/src/components/ui/label.tsx +0 -2
  26. package/templates/latest/src/components/ui/select.tsx +0 -2
  27. package/templates/latest/src/pages/Dashboard.tsx +1 -1
  28. package/templates/latest/src/single-spa.tsx +38 -28
  29. package/templates/latest/tailwind.config.js +1 -2
  30. package/templates/latest/tsconfig.app.json +6 -0
  31. package/templates/latest/tsconfig.json +6 -6
  32. package/templates/latest/tsconfig.node.json +5 -1
  33. package/templates/latest/vite.config.ts +1 -1
  34. package/templates/latest/src/components/page/dashboard.tsx +0 -1506
@@ -42,10 +42,11 @@ else
42
42
  fi
43
43
 
44
44
  echo "🎨 Setting up Tailwind CSS..."
45
- npx tailwindcss init -p 2>/dev/null || echo "✅ Tailwind already configured"
46
-
47
- echo "🔍 Running type check..."
48
- npm run type-check
45
+ if [ ! -f tailwind.config.js ] && [ ! -f tailwind.config.cjs ]; then
46
+ npx tailwindcss init -p
47
+ else
48
+ echo "✅ Tailwind already configured"
49
+ fi
49
50
 
50
51
  echo "✅ Setup complete!"
51
52
  echo ""
@@ -1,15 +1,19 @@
1
- import { Routes, Route } from 'react-router-dom'
1
+ import { Routes, Route, Navigate } from 'react-router-dom'
2
+ import { Suspense, lazy } from 'react'
2
3
  import QueryProvider from '@/providers/query-provider'
3
4
  import Home from '@/pages/Home'
4
- import Dashboard from '@/pages/Dashboard'
5
+ const Dashboard = lazy(() => import('@/pages/Dashboard'))
5
6
 
6
7
  export default function App() {
7
8
  return (
8
9
  <QueryProvider>
9
- <Routes>
10
- <Route path="/" element={<Home />} />
11
- <Route path="/dashboard" element={<Dashboard />} />
12
- </Routes>
10
+ <Suspense fallback={<div className="p-4 text-sm text-muted-foreground">Loading…</div>}>
11
+ <Routes>
12
+ <Route path="/" element={<Home />} />
13
+ <Route path="/dashboard" element={<Dashboard />} />
14
+ <Route path="*" element={<Navigate to="/" replace />} />
15
+ </Routes>
16
+ </Suspense>
13
17
  </QueryProvider>
14
18
  )
15
- }
19
+ }
@@ -0,0 +1,80 @@
1
+ import React from 'react';
2
+ import {
3
+ ResponsiveContainer,
4
+ AreaChart as RechartsAreaChart,
5
+ Area,
6
+ XAxis,
7
+ YAxis,
8
+ CartesianGrid,
9
+ Tooltip,
10
+ Legend
11
+ } from 'recharts';
12
+
13
+ interface AreaChartProps {
14
+ data: any[];
15
+ index: string;
16
+ categories: string[];
17
+ colors?: string[];
18
+ className?: string;
19
+ valueFormatter?: (value: any) => any;
20
+ showLegend?: boolean;
21
+ showGridLines?: boolean;
22
+ showXAxis?: boolean;
23
+ showYAxis?: boolean;
24
+ yAxisWidth?: number;
25
+ }
26
+
27
+ export const AreaChart: React.FC<AreaChartProps> = ({
28
+ data,
29
+ index,
30
+ categories,
31
+ colors = ['#3B82F6'],
32
+ className = "h-80",
33
+ valueFormatter,
34
+ showLegend = true,
35
+ showGridLines = true,
36
+ showXAxis = true,
37
+ showYAxis = true,
38
+ yAxisWidth
39
+ }) => {
40
+ const colorMap: { [key: string]: string } = {
41
+ blue: '#3B82F6',
42
+ emerald: '#10B981',
43
+ violet: '#8B5CF6',
44
+ rose: '#F43F5E',
45
+ amber: '#F59E0B',
46
+ cyan: '#06B6D4',
47
+ slate: '#64748B',
48
+ indigo: '#6366F1'
49
+ };
50
+
51
+ const getColor = (color: string, index: number) => {
52
+ return colorMap[color] || colors[index] || '#3B82F6';
53
+ };
54
+
55
+ return (
56
+ <div className={className}>
57
+ <ResponsiveContainer width="100%" height="100%">
58
+ <RechartsAreaChart data={data} margin={{ top: 20, right: 30, left: 20, bottom: 5 }}>
59
+ {showGridLines && <CartesianGrid strokeDasharray="3 3" />}
60
+ {showXAxis && <XAxis dataKey={index} />}
61
+ {showYAxis && <YAxis width={yAxisWidth || 60} />}
62
+ <Tooltip
63
+ formatter={(value: any) => valueFormatter ? valueFormatter(value) : value}
64
+ />
65
+ {showLegend && <Legend />}
66
+ {categories?.map((category: string, idx: number) => (
67
+ <Area
68
+ key={category}
69
+ type="monotone"
70
+ dataKey={category}
71
+ stroke={getColor(colors[idx], idx)}
72
+ fill={getColor(colors[idx], idx)}
73
+ fillOpacity={0.3}
74
+ />
75
+ ))}
76
+ </RechartsAreaChart>
77
+ </ResponsiveContainer>
78
+ </div>
79
+ );
80
+ };
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import {
3
+ ResponsiveContainer,
4
+ PieChart,
5
+ Pie,
6
+ Cell,
7
+ Tooltip,
8
+ Legend
9
+ } from 'recharts';
10
+
11
+ interface DonutChartProps {
12
+ data: any[];
13
+ category: string;
14
+ index: string;
15
+ colors?: string[];
16
+ className?: string;
17
+ valueFormatter?: (value: any) => any;
18
+ variant?: "donut" | "pie";
19
+ }
20
+
21
+ export const DonutChart: React.FC<DonutChartProps> = ({
22
+ data,
23
+ category,
24
+ index,
25
+ colors = ['#3B82F6'],
26
+ className = "h-64",
27
+ valueFormatter,
28
+ variant = "donut"
29
+ }) => {
30
+ const colorMap: { [key: string]: string } = {
31
+ blue: '#3B82F6',
32
+ emerald: '#10B981',
33
+ violet: '#8B5CF6',
34
+ rose: '#F43F5E',
35
+ amber: '#F59E0B',
36
+ cyan: '#06B6D4',
37
+ slate: '#64748B',
38
+ indigo: '#6366F1'
39
+ };
40
+
41
+ const getColor = (color: string, index: number) => {
42
+ return colorMap[color] || colors[index] || '#3B82F6';
43
+ };
44
+
45
+ const COLORS = colors.map((color: string, idx: number) => getColor(color, idx));
46
+
47
+ return (
48
+ <div className={className}>
49
+ <ResponsiveContainer width="100%" height="100%">
50
+ <PieChart>
51
+ <Pie
52
+ data={data}
53
+ cx="50%"
54
+ cy="50%"
55
+ innerRadius={variant === "donut" ? 60 : 0}
56
+ outerRadius={80}
57
+ paddingAngle={5}
58
+ dataKey={category}
59
+ nameKey={index}
60
+ >
61
+ {data.map((_entry: any, index: number) => (
62
+ <Cell key={`cell-${index}`} fill={COLORS[index % COLORS.length]} />
63
+ ))}
64
+ </Pie>
65
+ <Tooltip
66
+ formatter={(value: any) => valueFormatter ? valueFormatter(value) : value}
67
+ />
68
+ <Legend />
69
+ </PieChart>
70
+ </ResponsiveContainer>
71
+ </div>
72
+ );
73
+ };
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import {
3
+ ResponsiveContainer,
4
+ AreaChart as RechartsAreaChart,
5
+ Area
6
+ } from 'recharts';
7
+
8
+ interface SparkAreaChartProps {
9
+ data: any[];
10
+ categories: string[];
11
+ colors?: string[];
12
+ className?: string;
13
+ }
14
+
15
+ export const SparkAreaChart: React.FC<SparkAreaChartProps> = ({
16
+ data,
17
+ categories,
18
+ colors = ['#3B82F6'],
19
+ className = "h-10"
20
+ }) => {
21
+ const colorMap: { [key: string]: string } = {
22
+ blue: '#3B82F6',
23
+ emerald: '#10B981',
24
+ violet: '#8B5CF6',
25
+ rose: '#F43F5E',
26
+ amber: '#F59E0B',
27
+ cyan: '#06B6D4'
28
+ };
29
+
30
+ const getColor = (color: string, index: number) => {
31
+ return colorMap[color] || colors[index] || '#3B82F6';
32
+ };
33
+
34
+ return (
35
+ <div className={className}>
36
+ <ResponsiveContainer width="100%" height="100%">
37
+ <RechartsAreaChart data={data} margin={{ top: 0, right: 0, left: 0, bottom: 0 }}>
38
+ {categories?.map((category: string, idx: number) => (
39
+ <Area
40
+ key={category}
41
+ type="monotone"
42
+ dataKey={category}
43
+ stroke={getColor(colors[idx], idx)}
44
+ fill={getColor(colors[idx], idx)}
45
+ fillOpacity={0.6}
46
+ />
47
+ ))}
48
+ </RechartsAreaChart>
49
+ </ResponsiveContainer>
50
+ </div>
51
+ );
52
+ };
@@ -87,7 +87,7 @@ export default function DashboardHeader({ title, subtitle }: DashboardHeaderProp
87
87
  </div>
88
88
 
89
89
  {/* Notifications */}
90
- <Button variant="ghost" size="sm" className="relative">
90
+ <Button variant="ghost" size="sm" className="relative" aria-label="Open notifications, 3 unread">
91
91
  <Bell className="h-5 w-5" />
92
92
  <span className="absolute -top-1 -right-1 h-4 w-4 bg-red-500 text-white text-xs rounded-full flex items-center justify-center">
93
93
  3
@@ -1,29 +1,37 @@
1
- import { ReactNode } from 'react'
2
- import Sidebar from './sidebar'
3
- import DashboardHeader from './dashboard-header'
1
+ import { ReactNode } from "react";
2
+ import Sidebar from "./sidebar";
3
+ import DashboardHeader from "./dashboard-header";
4
4
 
5
5
  interface DashboardLayoutProps {
6
- children: ReactNode
7
- title?: string
8
- subtitle?: string
6
+ children: ReactNode;
7
+ title?: string;
8
+ subtitle?: string;
9
9
  }
10
10
 
11
- export default function DashboardLayout({ children, title, subtitle }: DashboardLayoutProps) {
11
+ export default function DashboardLayout({
12
+ children,
13
+ title,
14
+ subtitle,
15
+ }: DashboardLayoutProps) {
12
16
  return (
13
17
  <div className="flex h-screen bg-gray-50">
14
18
  {/* Sidebar */}
15
19
  <Sidebar />
16
-
20
+
17
21
  {/* Main content area */}
18
22
  <div className="flex-1 flex flex-col overflow-hidden">
19
23
  {/* Header */}
20
24
  <DashboardHeader title={title} subtitle={subtitle} />
21
-
25
+
22
26
  {/* Main content */}
23
- <main className="flex-1 overflow-y-auto p-6">
27
+ <main
28
+ id="main-content"
29
+ role="main"
30
+ className="flex-1 overflow-y-auto p-6"
31
+ >
24
32
  {children}
25
33
  </main>
26
34
  </div>
27
35
  </div>
28
- )
36
+ );
29
37
  }
@@ -60,6 +60,8 @@ export default function Navbar() {
60
60
  type="button"
61
61
  className="inline-flex items-center justify-center p-2 rounded-md text-blue-200 hover:text-white hover:bg-blue-700"
62
62
  onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
63
+ aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
64
+ aria-expanded={mobileMenuOpen}
63
65
  >
64
66
  {mobileMenuOpen ? (
65
67
  <X className="h-6 w-6" />
@@ -22,7 +22,7 @@ export default function Sidebar() {
22
22
  </div>
23
23
 
24
24
  {/* Navigation */}
25
- <nav className="flex-1 space-y-1 px-3 py-4">
25
+ <nav className="flex-1 space-y-1 px-3 py-4" aria-label="Main navigation">
26
26
  {navigation.map((item) => {
27
27
  const isActive = location.pathname === item.href;
28
28
  return (
@@ -35,6 +35,7 @@ export default function Sidebar() {
35
35
  ? "bg-blue-50 text-blue-700 border-r-2 border-blue-600"
36
36
  : "text-gray-700 hover:bg-gray-50 hover:text-gray-900"
37
37
  )}
38
+ aria-current={isActive ? "page" : undefined}
38
39
  >
39
40
  <item.icon
40
41
  className={cn(
@@ -0,0 +1,97 @@
1
+ import React from 'react';
2
+ import { Card, CardContent, CardTitle } from '@/components/ui/card';
3
+ import { ArrowUpRight, ArrowDownRight } from 'lucide-react';
4
+ import { SparkAreaChart } from '@/components/charts/SparkAreaChart';
5
+
6
+ interface Metric {
7
+ title: string;
8
+ value: string;
9
+ change: string;
10
+ trend: 'up' | 'down';
11
+ icon: string;
12
+ color: string;
13
+ }
14
+
15
+ interface DashboardMetricsProps {
16
+ ecommerceMetrics: Metric[];
17
+ monthlyRevenueData: any[];
18
+ }
19
+
20
+ const iconMap: { [key: string]: React.ElementType } = {
21
+ DollarSign: ({ className }: { className?: string }) => (
22
+ <svg xmlns="http://www.w3.org/2000/svg" className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
23
+ <line x1="12" y1="1" x2="12" y2="23"></line>
24
+ <path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"></path>
25
+ </svg>
26
+ ),
27
+ ShoppingCart: ({ className }: { className?: string }) => (
28
+ <svg xmlns="http://www.w3.org/2000/svg" className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
29
+ <circle cx="9" cy="21" r="1"></circle>
30
+ <circle cx="20" cy="21" r="1"></circle>
31
+ <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
32
+ </svg>
33
+ ),
34
+ TrendingUp: ({ className }: { className?: string }) => (
35
+ <svg xmlns="http://www.w3.org/2000/svg" className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
36
+ <polyline points="23 6 13.5 15.5 8.5 10.5 1 18"></polyline>
37
+ <polyline points="17 6 23 6 23 12"></polyline>
38
+ </svg>
39
+ ),
40
+ Users: ({ className }: { className?: string }) => (
41
+ <svg xmlns="http://www.w3.org/2000/svg" className={className} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
42
+ <path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
43
+ <circle cx="9" cy="7" r="4"></circle>
44
+ <path d="M23 21v-2a4 4 0 0 0-3-3.87"></path>
45
+ <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
46
+ </svg>
47
+ )
48
+ };
49
+
50
+ export const DashboardMetrics: React.FC<DashboardMetricsProps> = ({
51
+ ecommerceMetrics,
52
+ monthlyRevenueData
53
+ }) => {
54
+ return (
55
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
56
+ {ecommerceMetrics.map((metric, index) => {
57
+ const IconComponent = iconMap[metric.icon];
58
+ return (
59
+ <Card key={index}>
60
+ <CardContent className="pt-6">
61
+ <div className="flex items-center justify-between space-y-0 pb-2">
62
+ <CardTitle className="text-sm font-medium text-gray-600">
63
+ {metric.title}
64
+ </CardTitle>
65
+ {IconComponent && <IconComponent className={`h-4 w-4 ${metric.color}`} />}
66
+ </div>
67
+ <div className="flex items-center space-x-2">
68
+ <div className="text-2xl font-bold">{metric.value}</div>
69
+ <div className={`flex items-center text-sm ${
70
+ metric.trend === 'up' ? 'text-green-600' : 'text-red-600'
71
+ }`}>
72
+ {metric.trend === 'up' ? (
73
+ <ArrowUpRight className="h-4 w-4" />
74
+ ) : (
75
+ <ArrowDownRight className="h-4 w-4" />
76
+ )}
77
+ {metric.change}
78
+ </div>
79
+ </div>
80
+ {/* Mini trend chart */}
81
+ {monthlyRevenueData.length > 0 && (
82
+ <div className="mt-4">
83
+ <SparkAreaChart
84
+ data={monthlyRevenueData.slice(-6)} // Last 6 months
85
+ categories={[index === 0 ? "revenue" : "orders"]}
86
+ colors={[index === 0 ? "emerald" : index === 1 ? "blue" : index === 2 ? "violet" : "amber"]}
87
+ className="h-10 w-full"
88
+ />
89
+ </div>
90
+ )}
91
+ </CardContent>
92
+ </Card>
93
+ );
94
+ })}
95
+ </div>
96
+ );
97
+ };
@@ -0,0 +1,182 @@
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 } from 'lucide-react';
5
+ import {
6
+ ResponsiveContainer,
7
+ PieChart,
8
+ Pie,
9
+ Cell,
10
+ Tooltip,
11
+ Legend
12
+ } from 'recharts';
13
+ import { DonutChart } from '@/components/charts/DonutChart';
14
+
15
+ interface PaymentMethodsAnalysisProps {
16
+ paymentMethodData: any[];
17
+ }
18
+
19
+ export const PaymentMethodsAnalysis: React.FC<PaymentMethodsAnalysisProps> = ({
20
+ paymentMethodData
21
+ }) => {
22
+ // Process data for charts
23
+ const donutChartData = paymentMethodData.map((item: any) => ({
24
+ method: item.method,
25
+ count: item.count
26
+ }));
27
+
28
+ const pieChartData = paymentMethodData.map((item: any) => ({
29
+ name: item.method,
30
+ value: item.total,
31
+ count: item.count,
32
+ percentage: Math.round((item.total / paymentMethodData.reduce((sum: number, m: any) => sum + m.total, 0)) * 100)
33
+ }));
34
+
35
+ return (
36
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
37
+ {/* Enhanced Payment Method Distribution */}
38
+ <Card>
39
+ <CardHeader className="pb-4">
40
+ <div className="flex items-center justify-between">
41
+ <div>
42
+ <CardTitle className="text-lg font-bold">Payment Methods</CardTitle>
43
+ <CardDescription className="text-sm text-gray-600">
44
+ Transaction distribution by payment type
45
+ </CardDescription>
46
+ </div>
47
+ <Badge variant="outline" className="text-blue-600 border-blue-200">
48
+ {paymentMethodData.length} Methods
49
+ </Badge>
50
+ </div>
51
+ </CardHeader>
52
+ <CardContent>
53
+ {paymentMethodData.length > 0 ? (
54
+ <div className="space-y-6">
55
+ {/* Enhanced Payment Method Donut Chart */}
56
+ <div className="relative bg-gradient-to-br from-blue-50 to-indigo-50 p-6 rounded-lg border border-blue-100">
57
+ <h3 className="text-sm font-semibold text-blue-800 mb-4 text-center">Payment Distribution</h3>
58
+ <div className="h-80">
59
+ <ResponsiveContainer width="100%" height="100%">
60
+ <PieChart>
61
+ <defs>
62
+ <filter id="shadow" height="130%">
63
+ <feDropShadow dx="2" dy="2" stdDeviation="3" floodOpacity="0.2"/>
64
+ </filter>
65
+ </defs>
66
+ <Pie
67
+ data={pieChartData}
68
+ cx="50%"
69
+ cy="50%"
70
+ labelLine={false}
71
+ label={({ name, percentage }: any) => `${name}: ${percentage}%`}
72
+ outerRadius={100}
73
+ innerRadius={60}
74
+ paddingAngle={5}
75
+ dataKey="value"
76
+ filter="url(#shadow)"
77
+ >
78
+ {paymentMethodData.map((_entry: any, index: number) => {
79
+ const colors = ['#3B82F6', '#10B981', '#8B5CF6', '#F59E0B', '#F43F5E', '#06B6D4'];
80
+ return (
81
+ <Cell
82
+ key={`cell-${index}`}
83
+ fill={colors[index % colors.length]}
84
+ stroke="#fff"
85
+ strokeWidth={2}
86
+ />
87
+ );
88
+ })}
89
+ </Pie>
90
+ <Tooltip
91
+ formatter={(value: any, name: string, props: any) => [
92
+ `$${value.toLocaleString()}`,
93
+ `${name} (${props.payload.count} transactions)`
94
+ ]}
95
+ labelStyle={{ color: '#374151', fontWeight: 'bold' }}
96
+ contentStyle={{
97
+ backgroundColor: '#fff',
98
+ border: '1px solid #e5e7eb',
99
+ borderRadius: '8px',
100
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)'
101
+ }}
102
+ />
103
+ <Legend
104
+ verticalAlign="bottom"
105
+ height={36}
106
+ iconType="circle"
107
+ wrapperStyle={{ fontSize: '12px', paddingTop: '20px' }}
108
+ />
109
+ </PieChart>
110
+ </ResponsiveContainer>
111
+ </div>
112
+ </div>
113
+
114
+ {/* Enhanced Payment Method Details */}
115
+ <div className="space-y-3">
116
+ <h3 className="text-sm font-semibold text-gray-700">Payment Method Breakdown</h3>
117
+ {paymentMethodData.map((method: any, index: number) => {
118
+ const colors = ['bg-blue-500', 'bg-emerald-500', 'bg-violet-500', 'bg-amber-500', 'bg-rose-500', 'bg-cyan-500'];
119
+ const textColors = ['text-blue-600', 'text-emerald-600', 'text-violet-600', 'text-amber-600', 'text-rose-600', 'text-cyan-600'];
120
+ const bgColors = ['bg-blue-50', 'bg-emerald-50', 'bg-violet-50', 'bg-amber-50', 'bg-rose-50', 'bg-cyan-50'];
121
+ const percentage = Math.round((method.total / paymentMethodData.reduce((sum: number, m: any) => sum + m.total, 0)) * 100);
122
+
123
+ return (
124
+ <div key={index} className={`flex items-center justify-between p-4 rounded-lg border hover:shadow-sm transition-all ${bgColors[index] || 'bg-gray-50'}`}>
125
+ <div className="flex items-center space-x-4">
126
+ <div className={`w-4 h-4 rounded-full ${colors[index] || 'bg-gray-400'}`} />
127
+ <div>
128
+ <div className="font-semibold text-gray-900">{method.method}</div>
129
+ <div className="text-sm text-gray-500">{method.count} transactions</div>
130
+ </div>
131
+ </div>
132
+ <div className="text-right">
133
+ <div className={`text-lg font-bold ${textColors[index] || 'text-gray-600'}`}>
134
+ ${method.total.toLocaleString()}
135
+ </div>
136
+ <div className="text-sm text-gray-500">{percentage}% of total</div>
137
+ <div className="w-20 bg-gray-200 rounded-full h-1.5 mt-1">
138
+ <div
139
+ className={`h-1.5 rounded-full ${colors[index] || 'bg-gray-400'}`}
140
+ style={{ width: `${percentage}%` }}
141
+ />
142
+ </div>
143
+ </div>
144
+ </div>
145
+ );
146
+ })}
147
+ </div>
148
+ </div>
149
+ ) : (
150
+ <div className="text-center text-gray-500 py-12">
151
+ <DollarSign className="h-12 w-12 mx-auto text-gray-300 mb-4" />
152
+ <p className="text-lg font-medium">No payment data available</p>
153
+ <p className="text-sm">Process payments to see distribution</p>
154
+ </div>
155
+ )}
156
+ </CardContent>
157
+ </Card>
158
+
159
+ {/* Payment Method Distribution */}
160
+ <Card>
161
+ <CardHeader>
162
+ <CardTitle>Payment Method Distribution</CardTitle>
163
+ <CardDescription>
164
+ Transaction count by payment method
165
+ </CardDescription>
166
+ </CardHeader>
167
+ <CardContent>
168
+ <DonutChart
169
+ className="h-64"
170
+ data={donutChartData}
171
+ category="count"
172
+ index="method"
173
+ valueFormatter={(number: number) =>
174
+ `${number} transactions`
175
+ }
176
+ colors={["emerald", "blue", "violet", "rose", "amber"]}
177
+ />
178
+ </CardContent>
179
+ </Card>
180
+ </div>
181
+ );
182
+ };