@burdenoff/microfe-proserviceworld 2026.512.1 → 2026.624.1
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/dist/proserviceworld/pages/AdminReportsPage.js +28 -28
- package/dist/proserviceworld/pages/AdminReportsPage.js.map +1 -1
- package/dist/proserviceworld/pages/AdminUsersPage.js +19 -19
- package/dist/proserviceworld/pages/AdminUsersPage.js.map +1 -1
- package/dist/proserviceworld/pages/AnalyticsPage.js +27 -27
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/BookingsPage.js +24 -24
- package/dist/proserviceworld/pages/BookingsPage.js.map +1 -1
- package/dist/proserviceworld/pages/DashboardPage.js +33 -33
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +29 -29
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/MessagesPage.js +15 -15
- package/dist/proserviceworld/pages/MessagesPage.js.map +1 -1
- package/dist/proserviceworld/pages/PaymentsPage.js +19 -19
- package/dist/proserviceworld/pages/PaymentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProjectsPage.js +36 -36
- package/dist/proserviceworld/pages/ProjectsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.js +36 -36
- package/dist/proserviceworld/pages/ProviderDetailPage.js.map +1 -1
- package/dist/proserviceworld/pages/ProvidersPage.js +19 -19
- package/dist/proserviceworld/pages/ProvidersPage.js.map +1 -1
- package/dist/proserviceworld/pages/ReviewsPage.js +22 -22
- package/dist/proserviceworld/pages/ReviewsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.js +35 -35
- package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ServicesPage.js +14 -14
- package/dist/proserviceworld/pages/ServicesPage.js.map +1 -1
- package/dist/proserviceworld/pages/VerificationPage.js +23 -23
- package/dist/proserviceworld/pages/VerificationPage.js.map +1 -1
- package/package.json +1 -1
- package/dist/dev/main.d.ts +0 -2
- package/dist/dev/main.d.ts.map +0 -1
- package/dist/generated/global-operations.d.ts +0 -2
- package/dist/generated/global-operations.d.ts.map +0 -1
- package/dist/generated/global-types.d.ts +0 -18199
- package/dist/generated/global-types.d.ts.map +0 -1
- package/dist/generated/wspace-operations.d.ts +0 -2
- package/dist/generated/wspace-operations.d.ts.map +0 -1
- package/dist/generated/wspace-types.d.ts +0 -43046
- package/dist/generated/wspace-types.d.ts.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +0 -1
- package/dist/proserviceworld/ProServiceWorldRoot.d.ts +0 -5
- package/dist/proserviceworld/ProServiceWorldRoot.d.ts.map +0 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.d.ts +0 -2
- package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +0 -1
- package/dist/proserviceworld/index.d.ts +0 -5
- package/dist/proserviceworld/index.d.ts.map +0 -1
- package/dist/proserviceworld/pages/AdminReportsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/AdminReportsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/AdminUsersPage.d.ts +0 -2
- package/dist/proserviceworld/pages/AdminUsersPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/AnalyticsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/AnalyticsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/BookingsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/BookingsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/DashboardPage.d.ts +0 -2
- package/dist/proserviceworld/pages/DashboardPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/DocumentsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/DocumentsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/MessagesPage.d.ts +0 -2
- package/dist/proserviceworld/pages/MessagesPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/PaymentsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/PaymentsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ProjectsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ProjectsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ProviderDetailPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ProvidersPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ProvidersPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ReviewsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ReviewsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ServiceDetailsPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/ServicesPage.d.ts +0 -2
- package/dist/proserviceworld/pages/ServicesPage.d.ts.map +0 -1
- package/dist/proserviceworld/pages/VerificationPage.d.ts +0 -2
- package/dist/proserviceworld/pages/VerificationPage.d.ts.map +0 -1
- package/dist/proserviceworld/providers/ProServiceWorldProvider.d.ts +0 -6
- package/dist/proserviceworld/providers/ProServiceWorldProvider.d.ts.map +0 -1
- package/dist/proserviceworld/store/proserviceworldStore.d.ts +0 -32
- package/dist/proserviceworld/store/proserviceworldStore.d.ts.map +0 -1
- package/dist/proserviceworld/types/index.d.ts +0 -338
- package/dist/proserviceworld/types/index.d.ts.map +0 -1
- package/dist/proserviceworld/utils/cn.d.ts +0 -4
- package/dist/proserviceworld/utils/cn.d.ts.map +0 -1
- package/dist/proserviceworld/utils/i18n.d.ts +0 -6
- package/dist/proserviceworld/utils/i18n.d.ts.map +0 -1
- package/dist/proserviceworld/utils/navigation.d.ts +0 -2
- package/dist/proserviceworld/utils/navigation.d.ts.map +0 -1
|
@@ -43,7 +43,7 @@ function w() {
|
|
|
43
43
|
className: "text-2xl font-semibold",
|
|
44
44
|
children: "Admin Reports"
|
|
45
45
|
}), /* @__PURE__ */ g("p", {
|
|
46
|
-
className: "text-sm text-
|
|
46
|
+
className: "text-sm text-text-secondary",
|
|
47
47
|
children: "Platform analytics, performance metrics, and operational reports."
|
|
48
48
|
})] }), /* @__PURE__ */ _("div", {
|
|
49
49
|
className: "flex gap-2",
|
|
@@ -64,12 +64,12 @@ function w() {
|
|
|
64
64
|
className: "flex gap-1 border-b",
|
|
65
65
|
children: N.map(({ key: e, label: t, icon: n }) => /* @__PURE__ */ _("button", {
|
|
66
66
|
onClick: () => A(e),
|
|
67
|
-
className: `flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${k === e ? "border-primary text-primary" : "border-transparent text-
|
|
67
|
+
className: `flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${k === e ? "border-primary text-primary" : "border-transparent text-text-secondary hover:text-text-primary"}`,
|
|
68
68
|
children: [/* @__PURE__ */ g(n, { className: "h-4 w-4" }), t]
|
|
69
69
|
}, e))
|
|
70
70
|
}), /* @__PURE__ */ _("div", {
|
|
71
71
|
className: "flex items-center gap-2",
|
|
72
|
-
children: [/* @__PURE__ */ g(a, { className: "h-4 w-4 text-
|
|
72
|
+
children: [/* @__PURE__ */ g(a, { className: "h-4 w-4 text-text-secondary" }), /* @__PURE__ */ g("div", {
|
|
73
73
|
className: "flex rounded border",
|
|
74
74
|
children: [
|
|
75
75
|
"7d",
|
|
@@ -78,7 +78,7 @@ function w() {
|
|
|
78
78
|
"1y"
|
|
79
79
|
].map((e) => /* @__PURE__ */ g("button", {
|
|
80
80
|
onClick: () => M(e),
|
|
81
|
-
className: `px-3 py-1 text-xs font-medium ${j === e ? "bg-primary text-primary-
|
|
81
|
+
className: `px-3 py-1 text-xs font-medium ${j === e ? "bg-action-primary-bg text-action-primary-text" : "text-text-secondary hover:bg-bg-sunken/50"}`,
|
|
82
82
|
children: e
|
|
83
83
|
}, e))
|
|
84
84
|
})]
|
|
@@ -93,8 +93,8 @@ function w() {
|
|
|
93
93
|
className: "flex items-center justify-between",
|
|
94
94
|
children: [/* @__PURE__ */ _("div", {
|
|
95
95
|
className: "flex items-center gap-2",
|
|
96
|
-
children: [/* @__PURE__ */ g(F[t % F.length] ?? i, { className: "h-4 w-4 text-
|
|
97
|
-
className: "text-sm font-medium text-
|
|
96
|
+
children: [/* @__PURE__ */ g(F[t % F.length] ?? i, { className: "h-4 w-4 text-text-secondary" }), /* @__PURE__ */ g("p", {
|
|
97
|
+
className: "text-sm font-medium text-text-secondary",
|
|
98
98
|
children: e.label
|
|
99
99
|
})]
|
|
100
100
|
}), P(e.changeType)]
|
|
@@ -132,13 +132,13 @@ function w() {
|
|
|
132
132
|
className: "flex items-center gap-3",
|
|
133
133
|
children: [
|
|
134
134
|
/* @__PURE__ */ g("span", {
|
|
135
|
-
className: "w-10 text-xs text-
|
|
135
|
+
className: "w-10 text-xs text-text-secondary",
|
|
136
136
|
children: e.name
|
|
137
137
|
}),
|
|
138
138
|
/* @__PURE__ */ g("div", {
|
|
139
|
-
className: "flex-1 h-5 bg-
|
|
139
|
+
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
140
140
|
children: /* @__PURE__ */ g("div", {
|
|
141
|
-
className: "h-5 bg-primary rounded",
|
|
141
|
+
className: "h-5 bg-action-primary-bg rounded",
|
|
142
142
|
style: { width: `${e.value / I * 100}%` }
|
|
143
143
|
})
|
|
144
144
|
}),
|
|
@@ -168,11 +168,11 @@ function w() {
|
|
|
168
168
|
className: "flex items-center gap-3",
|
|
169
169
|
children: [
|
|
170
170
|
/* @__PURE__ */ g("span", {
|
|
171
|
-
className: "w-32 truncate text-xs text-
|
|
171
|
+
className: "w-32 truncate text-xs text-text-secondary",
|
|
172
172
|
children: e.name
|
|
173
173
|
}),
|
|
174
174
|
/* @__PURE__ */ g("div", {
|
|
175
|
-
className: "flex-1 h-5 bg-
|
|
175
|
+
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
176
176
|
children: /* @__PURE__ */ g("div", {
|
|
177
177
|
className: "h-5 bg-blue-500 rounded",
|
|
178
178
|
style: { width: `${e.value / L * 100}%` }
|
|
@@ -195,7 +195,7 @@ function w() {
|
|
|
195
195
|
children: /* @__PURE__ */ _("table", {
|
|
196
196
|
className: "w-full text-sm",
|
|
197
197
|
children: [/* @__PURE__ */ g("thead", { children: /* @__PURE__ */ _("tr", {
|
|
198
|
-
className: "border-b text-left text-
|
|
198
|
+
className: "border-b text-left text-text-secondary",
|
|
199
199
|
children: [
|
|
200
200
|
/* @__PURE__ */ g("th", {
|
|
201
201
|
className: "p-3",
|
|
@@ -221,7 +221,7 @@ function w() {
|
|
|
221
221
|
}) }), /* @__PURE__ */ g("tbody", { children: D.map((e) => {
|
|
222
222
|
let t = D.reduce((e, t) => e + t.revenue, 0), n = Math.round(e.revenue / t * 100), r = Math.round(e.revenue / e.clients);
|
|
223
223
|
return /* @__PURE__ */ _("tr", {
|
|
224
|
-
className: "border-b hover:bg-
|
|
224
|
+
className: "border-b hover:bg-bg-sunken/50",
|
|
225
225
|
children: [
|
|
226
226
|
/* @__PURE__ */ g("td", {
|
|
227
227
|
className: "p-3 font-medium",
|
|
@@ -240,7 +240,7 @@ function w() {
|
|
|
240
240
|
]
|
|
241
241
|
}),
|
|
242
242
|
/* @__PURE__ */ _("td", {
|
|
243
|
-
className: "p-3 text-right text-
|
|
243
|
+
className: "p-3 text-right text-text-secondary",
|
|
244
244
|
children: ["$", r.toLocaleString()]
|
|
245
245
|
}),
|
|
246
246
|
/* @__PURE__ */ g("td", {
|
|
@@ -248,9 +248,9 @@ function w() {
|
|
|
248
248
|
children: /* @__PURE__ */ _("div", {
|
|
249
249
|
className: "flex items-center gap-2",
|
|
250
250
|
children: [/* @__PURE__ */ g("div", {
|
|
251
|
-
className: "h-2 flex-1 overflow-hidden rounded-full bg-
|
|
251
|
+
className: "h-2 flex-1 overflow-hidden rounded-full bg-bg-sunken",
|
|
252
252
|
children: /* @__PURE__ */ g("div", {
|
|
253
|
-
className: "h-2 rounded-full bg-primary",
|
|
253
|
+
className: "h-2 rounded-full bg-action-primary-bg",
|
|
254
254
|
style: { width: `${n}%` }
|
|
255
255
|
})
|
|
256
256
|
}), /* @__PURE__ */ _("span", {
|
|
@@ -285,7 +285,7 @@ function w() {
|
|
|
285
285
|
children: e.replace("_", " ")
|
|
286
286
|
}),
|
|
287
287
|
/* @__PURE__ */ g("div", {
|
|
288
|
-
className: "flex-1 h-5 bg-
|
|
288
|
+
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
289
289
|
children: /* @__PURE__ */ g("div", {
|
|
290
290
|
className: `h-5 rounded ${{
|
|
291
291
|
client: "bg-blue-500",
|
|
@@ -324,7 +324,7 @@ function w() {
|
|
|
324
324
|
children: e
|
|
325
325
|
}),
|
|
326
326
|
/* @__PURE__ */ g("div", {
|
|
327
|
-
className: "flex-1 h-5 bg-
|
|
327
|
+
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
328
328
|
children: /* @__PURE__ */ g("div", {
|
|
329
329
|
className: `h-5 rounded ${{
|
|
330
330
|
active: "bg-green-500",
|
|
@@ -360,13 +360,13 @@ function w() {
|
|
|
360
360
|
children: [/* @__PURE__ */ _("div", {
|
|
361
361
|
className: "flex items-center gap-3",
|
|
362
362
|
children: [/* @__PURE__ */ g("span", {
|
|
363
|
-
className: "flex h-8 w-8 items-center justify-center rounded-full bg-primary/10 text-sm font-bold text-primary",
|
|
363
|
+
className: "flex h-8 w-8 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-bold text-primary",
|
|
364
364
|
children: t + 1
|
|
365
365
|
}), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
366
366
|
className: "font-medium",
|
|
367
367
|
children: e.name
|
|
368
368
|
}), /* @__PURE__ */ g("p", {
|
|
369
|
-
className: "text-xs text-
|
|
369
|
+
className: "text-xs text-text-secondary",
|
|
370
370
|
children: e.email
|
|
371
371
|
})] })]
|
|
372
372
|
}), /* @__PURE__ */ _("div", {
|
|
@@ -375,7 +375,7 @@ function w() {
|
|
|
375
375
|
className: "font-bold",
|
|
376
376
|
children: ["$", e.totalSpent.toLocaleString()]
|
|
377
377
|
}), /* @__PURE__ */ _("p", {
|
|
378
|
-
className: "text-xs text-
|
|
378
|
+
className: "text-xs text-text-secondary",
|
|
379
379
|
children: [e.bookingsCount, " bookings"]
|
|
380
380
|
})]
|
|
381
381
|
})]
|
|
@@ -401,7 +401,7 @@ function w() {
|
|
|
401
401
|
className: "font-medium",
|
|
402
402
|
children: [e.name, " 2025"]
|
|
403
403
|
}), /* @__PURE__ */ _("div", {
|
|
404
|
-
className: "mt-1 flex items-center gap-4 text-sm text-
|
|
404
|
+
className: "mt-1 flex items-center gap-4 text-sm text-text-secondary",
|
|
405
405
|
children: [/* @__PURE__ */ _("span", { children: [
|
|
406
406
|
"Current: $",
|
|
407
407
|
(e.value / 1e3).toFixed(1),
|
|
@@ -422,10 +422,10 @@ function w() {
|
|
|
422
422
|
}), /* @__PURE__ */ _("div", {
|
|
423
423
|
className: "mt-3 flex gap-1 h-4",
|
|
424
424
|
children: [/* @__PURE__ */ g("div", {
|
|
425
|
-
className: "h-4 rounded bg-primary/30",
|
|
425
|
+
className: "h-4 rounded bg-action-primary-bg/30",
|
|
426
426
|
style: { width: `${(e.prev ?? 0) / I * 100}%` }
|
|
427
427
|
}), /* @__PURE__ */ g("div", {
|
|
428
|
-
className: "h-4 rounded bg-primary",
|
|
428
|
+
className: "h-4 rounded bg-action-primary-bg",
|
|
429
429
|
style: { width: `${Math.max(0, (e.value - (e.prev ?? 0)) / I * 100)}%` }
|
|
430
430
|
})]
|
|
431
431
|
})]
|
|
@@ -443,7 +443,7 @@ function w() {
|
|
|
443
443
|
children: [...E].sort((e, t) => t.value - e.value).map((e, t) => /* @__PURE__ */ _("div", {
|
|
444
444
|
className: "flex items-center gap-3",
|
|
445
445
|
children: [/* @__PURE__ */ _("span", {
|
|
446
|
-
className: "w-6 text-sm font-bold text-
|
|
446
|
+
className: "w-6 text-sm font-bold text-text-secondary",
|
|
447
447
|
children: ["#", t + 1]
|
|
448
448
|
}), /* @__PURE__ */ _("div", {
|
|
449
449
|
className: "flex-1",
|
|
@@ -454,9 +454,9 @@ function w() {
|
|
|
454
454
|
children: e.name
|
|
455
455
|
}), /* @__PURE__ */ _("span", { children: [e.value, " bookings"] })]
|
|
456
456
|
}), /* @__PURE__ */ g("div", {
|
|
457
|
-
className: "mt-1 h-2 overflow-hidden rounded-full bg-
|
|
457
|
+
className: "mt-1 h-2 overflow-hidden rounded-full bg-bg-sunken",
|
|
458
458
|
children: /* @__PURE__ */ g("div", {
|
|
459
|
-
className: "h-2 rounded-full bg-primary",
|
|
459
|
+
className: "h-2 rounded-full bg-action-primary-bg",
|
|
460
460
|
style: { width: `${e.value / L * 100}%` }
|
|
461
461
|
})
|
|
462
462
|
})]
|
|
@@ -492,7 +492,7 @@ function w() {
|
|
|
492
492
|
className: "flex items-center justify-between rounded-lg border p-3",
|
|
493
493
|
children: [/* @__PURE__ */ _("div", {
|
|
494
494
|
className: "flex items-center gap-3",
|
|
495
|
-
children: [/* @__PURE__ */ g(n, { className: "h-5 w-5 text-
|
|
495
|
+
children: [/* @__PURE__ */ g(n, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ g("span", {
|
|
496
496
|
className: "text-sm font-medium",
|
|
497
497
|
children: e
|
|
498
498
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AdminReportsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AdminReportsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Badge, Button, Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport {\n TrendingUp,\n TrendingDown,\n Minus,\n Download,\n FileText,\n BarChart3,\n PieChart,\n Calendar,\n Filter,\n Users,\n DollarSign,\n ShoppingCart,\n Star,\n AlertTriangle,\n ArrowUpRight,\n Shield,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\n\ntype ReportTab = 'overview' | 'users' | 'revenue' | 'bookings';\ntype TimeRange = '7d' | '30d' | '90d' | '1y';\n\nexport function AdminReportsPage() {\n const { adminReportMetrics, revenueData, servicePerformance, geographicData, adminUsers } =\n useProServiceWorldStore();\n const [activeTab, setActiveTab] = useState<ReportTab>('overview');\n const [timeRange, setTimeRange] = useState<TimeRange>('30d');\n\n const tabs: { key: ReportTab; label: string; icon: typeof BarChart3 }[] = [\n { key: 'overview', label: 'Overview', icon: BarChart3 },\n { key: 'users', label: 'Users', icon: Users },\n { key: 'revenue', label: 'Revenue', icon: DollarSign },\n { key: 'bookings', label: 'Bookings', icon: ShoppingCart },\n ];\n\n const changeIcon = (type: string) => {\n if (type === 'positive') return <TrendingUp className=\"h-4 w-4 text-green-500\" />;\n if (type === 'negative') return <TrendingDown className=\"h-4 w-4 text-red-500\" />;\n return <Minus className=\"h-4 w-4 text-gray-400\" />;\n };\n\n const metricIcons = [Users, Shield, ShoppingCart, DollarSign, Star, AlertTriangle];\n\n const maxRevenue = Math.max(...revenueData.map((d) => d.value));\n const maxServiceBookings = Math.max(...servicePerformance.map((d) => d.value));\n\n // Role distribution\n const roleCounts = adminUsers.reduce<Record<string, number>>((acc, u) => {\n acc[u.role] = (acc[u.role] ?? 0) + 1;\n return acc;\n }, {});\n\n // Status distribution\n const statusCounts = adminUsers.reduce<Record<string, number>>((acc, u) => {\n acc[u.status] = (acc[u.status] ?? 0) + 1;\n return acc;\n }, {});\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Admin Reports</h1>\n <p className=\"text-sm text-muted-foreground\">\n Platform analytics, performance metrics, and operational reports.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <FileText className=\"mr-2 h-4 w-4\" /> Generate Report\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n <Download className=\"mr-2 h-4 w-4\" /> Export CSV\n </Button>\n </div>\n </div>\n\n {/* Time Range + Tabs */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex gap-1 border-b\">\n {tabs.map(({ key, label, icon: Icon }) => (\n <button\n key={key}\n onClick={() => setActiveTab(key)}\n className={`flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === key\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {label}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <div className=\"flex rounded border\">\n {(['7d', '30d', '90d', '1y'] as TimeRange[]).map((range) => (\n <button\n key={range}\n onClick={() => setTimeRange(range)}\n className={`px-3 py-1 text-xs font-medium ${\n timeRange === range\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted/50'\n }`}\n >\n {range}\n </button>\n ))}\n </div>\n </div>\n </div>\n\n {/* Metric Cards */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {adminReportMetrics.map((metric, i) => {\n const MetricIcon = metricIcons[i % metricIcons.length] ?? BarChart3;\n return (\n <Card key={i}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <MetricIcon className=\"h-4 w-4 text-muted-foreground\" />\n <p className=\"text-sm font-medium text-muted-foreground\">{metric.label}</p>\n </div>\n {changeIcon(metric.changeType)}\n </div>\n <p className=\"mt-2 text-3xl font-bold\">{metric.value}</p>\n <p\n className={`mt-1 flex items-center gap-1 text-xs ${\n metric.changeType === 'positive'\n ? 'text-green-600'\n : metric.changeType === 'negative'\n ? 'text-red-600'\n : 'text-gray-500'\n }`}\n >\n <ArrowUpRight className=\"h-3 w-3\" />\n {metric.change} vs last period\n </p>\n </CardContent>\n </Card>\n );\n })}\n </div>\n\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Revenue trend */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Platform Revenue</CardTitle>\n <Badge variant=\"secondary\">Monthly</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {revenueData.map((point) => (\n <div key={point.name} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-xs text-muted-foreground\">{point.name}</span>\n <div className=\"flex-1 h-5 bg-muted rounded overflow-hidden\">\n <div\n className=\"h-5 bg-primary rounded\"\n style={{ width: `${(point.value / maxRevenue) * 100}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-xs font-medium\">\n ${(point.value / 1000).toFixed(1)}k\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Service breakdown */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Service Bookings</CardTitle>\n <Badge variant=\"secondary\">Total</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .map((service) => (\n <div key={service.name} className=\"flex items-center gap-3\">\n <span className=\"w-32 truncate text-xs text-muted-foreground\">\n {service.name}\n </span>\n <div className=\"flex-1 h-5 bg-muted rounded overflow-hidden\">\n <div\n className=\"h-5 bg-blue-500 rounded\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs font-medium\">{service.value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Geographic revenue */}\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle className=\"text-base\">Revenue by Region</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b text-left text-muted-foreground\">\n <th className=\"p-3\">Region</th>\n <th className=\"p-3 text-right\">Clients</th>\n <th className=\"p-3 text-right\">Revenue</th>\n <th className=\"p-3 text-right\">Avg. per Client</th>\n <th className=\"p-3\">Share</th>\n </tr>\n </thead>\n <tbody>\n {geographicData.map((geo) => {\n const totalRevenue = geographicData.reduce((s, g) => s + g.revenue, 0);\n const pct = Math.round((geo.revenue / totalRevenue) * 100);\n const avgPerClient = Math.round(geo.revenue / geo.clients);\n return (\n <tr key={geo.country} className=\"border-b hover:bg-muted/50\">\n <td className=\"p-3 font-medium\">{geo.country}</td>\n <td className=\"p-3 text-right\">{geo.clients}</td>\n <td className=\"p-3 text-right font-medium\">\n ${(geo.revenue / 1000).toFixed(0)}k\n </td>\n <td className=\"p-3 text-right text-muted-foreground\">\n ${avgPerClient.toLocaleString()}\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-2 rounded-full bg-primary\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-10 text-right text-xs\">{pct}%</span>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Users Tab */}\n {activeTab === 'users' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">User Role Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(roleCounts).map(([role, count]) => {\n const pct = Math.round((count / adminUsers.length) * 100);\n const roleColors: Record<string, string> = {\n client: 'bg-blue-500',\n provider: 'bg-green-500',\n admin: 'bg-red-500',\n team_member: 'bg-purple-500',\n };\n return (\n <div key={role} className=\"flex items-center gap-3\">\n <span className=\"w-24 text-sm font-medium capitalize\">\n {role.replace('_', ' ')}\n </span>\n <div className=\"flex-1 h-5 bg-muted rounded overflow-hidden\">\n <div\n className={`h-5 rounded ${roleColors[role] ?? 'bg-gray-400'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm\">\n {count} ({pct}%)\n </span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">User Status Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(statusCounts).map(([status, count]) => {\n const pct = Math.round((count / adminUsers.length) * 100);\n const statusColors: Record<string, string> = {\n active: 'bg-green-500',\n suspended: 'bg-orange-500',\n pending: 'bg-yellow-500',\n banned: 'bg-red-500',\n };\n return (\n <div key={status} className=\"flex items-center gap-3\">\n <span className=\"w-24 text-sm font-medium capitalize\">{status}</span>\n <div className=\"flex-1 h-5 bg-muted rounded overflow-hidden\">\n <div\n className={`h-5 rounded ${statusColors[status] ?? 'bg-gray-400'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm\">\n {count} ({pct}%)\n </span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n {/* Top users by spend */}\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle className=\"text-base\">Top Users by Spending</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n {[...adminUsers]\n .filter((u) => u.totalSpent > 0)\n .sort((a, b) => b.totalSpent - a.totalSpent)\n .slice(0, 5)\n .map((user, idx) => (\n <div\n key={user.id}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-primary/10 text-sm font-bold text-primary\">\n {idx + 1}\n </span>\n <div>\n <p className=\"font-medium\">{user.name}</p>\n <p className=\"text-xs text-muted-foreground\">{user.email}</p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"font-bold\">${user.totalSpent.toLocaleString()}</p>\n <p className=\"text-xs text-muted-foreground\">\n {user.bookingsCount} bookings\n </p>\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Revenue Tab */}\n {activeTab === 'revenue' && (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Monthly Revenue Comparison</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {revenueData.map((point) => {\n const growth = point.prev\n ? (((point.value - point.prev) / point.prev) * 100).toFixed(1)\n : '0';\n const isPositive = point.prev ? point.value >= point.prev : true;\n return (\n <div key={point.name} className=\"rounded-lg border p-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"font-medium\">{point.name} 2025</p>\n <div className=\"mt-1 flex items-center gap-4 text-sm text-muted-foreground\">\n <span>Current: ${(point.value / 1000).toFixed(1)}k</span>\n <span>Previous: ${((point.prev ?? 0) / 1000).toFixed(1)}k</span>\n </div>\n </div>\n <Badge variant={isPositive ? 'default' : 'destructive'}>\n {isPositive ? '+' : ''}\n {growth}%\n </Badge>\n </div>\n <div className=\"mt-3 flex gap-1 h-4\">\n <div\n className=\"h-4 rounded bg-primary/30\"\n style={{ width: `${((point.prev ?? 0) / maxRevenue) * 100}%` }}\n />\n <div\n className=\"h-4 rounded bg-primary\"\n style={{\n width: `${Math.max(0, ((point.value - (point.prev ?? 0)) / maxRevenue) * 100)}%`,\n }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Bookings Tab */}\n {activeTab === 'bookings' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Bookings by Service Category</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .map((service, idx) => (\n <div key={service.name} className=\"flex items-center gap-3\">\n <span className=\"w-6 text-sm font-bold text-muted-foreground\">#{idx + 1}</span>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium\">{service.name}</span>\n <span>{service.value} bookings</span>\n </div>\n <div className=\"mt-1 h-2 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-2 rounded-full bg-primary\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Quick Stats</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {[\n {\n label: 'Total Bookings',\n value: servicePerformance.reduce((s, p) => s + p.value, 0).toLocaleString(),\n icon: ShoppingCart,\n },\n { label: 'Avg. Booking Value', value: '$3,804', icon: DollarSign },\n { label: 'Completion Rate', value: '96.5%', icon: TrendingUp },\n { label: 'Client Satisfaction', value: '4.7/5.0', icon: Star },\n ].map(({ label, value, icon: Icon }) => (\n <div\n key={label}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <Icon className=\"h-5 w-5 text-muted-foreground\" />\n <span className=\"text-sm font-medium\">{label}</span>\n </div>\n <span className=\"text-lg font-bold\">{value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"AdminReportsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AdminReportsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Badge, Button, Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport {\n TrendingUp,\n TrendingDown,\n Minus,\n Download,\n FileText,\n BarChart3,\n PieChart,\n Calendar,\n Filter,\n Users,\n DollarSign,\n ShoppingCart,\n Star,\n AlertTriangle,\n ArrowUpRight,\n Shield,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\n\ntype ReportTab = 'overview' | 'users' | 'revenue' | 'bookings';\ntype TimeRange = '7d' | '30d' | '90d' | '1y';\n\nexport function AdminReportsPage() {\n const { adminReportMetrics, revenueData, servicePerformance, geographicData, adminUsers } =\n useProServiceWorldStore();\n const [activeTab, setActiveTab] = useState<ReportTab>('overview');\n const [timeRange, setTimeRange] = useState<TimeRange>('30d');\n\n const tabs: { key: ReportTab; label: string; icon: typeof BarChart3 }[] = [\n { key: 'overview', label: 'Overview', icon: BarChart3 },\n { key: 'users', label: 'Users', icon: Users },\n { key: 'revenue', label: 'Revenue', icon: DollarSign },\n { key: 'bookings', label: 'Bookings', icon: ShoppingCart },\n ];\n\n const changeIcon = (type: string) => {\n if (type === 'positive') return <TrendingUp className=\"h-4 w-4 text-green-500\" />;\n if (type === 'negative') return <TrendingDown className=\"h-4 w-4 text-red-500\" />;\n return <Minus className=\"h-4 w-4 text-gray-400\" />;\n };\n\n const metricIcons = [Users, Shield, ShoppingCart, DollarSign, Star, AlertTriangle];\n\n const maxRevenue = Math.max(...revenueData.map((d) => d.value));\n const maxServiceBookings = Math.max(...servicePerformance.map((d) => d.value));\n\n // Role distribution\n const roleCounts = adminUsers.reduce<Record<string, number>>((acc, u) => {\n acc[u.role] = (acc[u.role] ?? 0) + 1;\n return acc;\n }, {});\n\n // Status distribution\n const statusCounts = adminUsers.reduce<Record<string, number>>((acc, u) => {\n acc[u.status] = (acc[u.status] ?? 0) + 1;\n return acc;\n }, {});\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Admin Reports</h1>\n <p className=\"text-sm text-text-secondary\">\n Platform analytics, performance metrics, and operational reports.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\">\n <FileText className=\"mr-2 h-4 w-4\" /> Generate Report\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n <Download className=\"mr-2 h-4 w-4\" /> Export CSV\n </Button>\n </div>\n </div>\n\n {/* Time Range + Tabs */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex gap-1 border-b\">\n {tabs.map(({ key, label, icon: Icon }) => (\n <button\n key={key}\n onClick={() => setActiveTab(key)}\n className={`flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === key\n ? 'border-primary text-primary'\n : 'border-transparent text-text-secondary hover:text-text-primary'\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {label}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-text-secondary\" />\n <div className=\"flex rounded border\">\n {(['7d', '30d', '90d', '1y'] as TimeRange[]).map((range) => (\n <button\n key={range}\n onClick={() => setTimeRange(range)}\n className={`px-3 py-1 text-xs font-medium ${\n timeRange === range\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken/50'\n }`}\n >\n {range}\n </button>\n ))}\n </div>\n </div>\n </div>\n\n {/* Metric Cards */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {adminReportMetrics.map((metric, i) => {\n const MetricIcon = metricIcons[i % metricIcons.length] ?? BarChart3;\n return (\n <Card key={i}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <MetricIcon className=\"h-4 w-4 text-text-secondary\" />\n <p className=\"text-sm font-medium text-text-secondary\">{metric.label}</p>\n </div>\n {changeIcon(metric.changeType)}\n </div>\n <p className=\"mt-2 text-3xl font-bold\">{metric.value}</p>\n <p\n className={`mt-1 flex items-center gap-1 text-xs ${\n metric.changeType === 'positive'\n ? 'text-green-600'\n : metric.changeType === 'negative'\n ? 'text-red-600'\n : 'text-gray-500'\n }`}\n >\n <ArrowUpRight className=\"h-3 w-3\" />\n {metric.change} vs last period\n </p>\n </CardContent>\n </Card>\n );\n })}\n </div>\n\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Revenue trend */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Platform Revenue</CardTitle>\n <Badge variant=\"secondary\">Monthly</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {revenueData.map((point) => (\n <div key={point.name} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-xs text-text-secondary\">{point.name}</span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-5 bg-action-primary-bg rounded\"\n style={{ width: `${(point.value / maxRevenue) * 100}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-xs font-medium\">\n ${(point.value / 1000).toFixed(1)}k\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Service breakdown */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Service Bookings</CardTitle>\n <Badge variant=\"secondary\">Total</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .map((service) => (\n <div key={service.name} className=\"flex items-center gap-3\">\n <span className=\"w-32 truncate text-xs text-text-secondary\">\n {service.name}\n </span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className=\"h-5 bg-blue-500 rounded\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs font-medium\">{service.value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Geographic revenue */}\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle className=\"text-base\">Revenue by Region</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b text-left text-text-secondary\">\n <th className=\"p-3\">Region</th>\n <th className=\"p-3 text-right\">Clients</th>\n <th className=\"p-3 text-right\">Revenue</th>\n <th className=\"p-3 text-right\">Avg. per Client</th>\n <th className=\"p-3\">Share</th>\n </tr>\n </thead>\n <tbody>\n {geographicData.map((geo) => {\n const totalRevenue = geographicData.reduce((s, g) => s + g.revenue, 0);\n const pct = Math.round((geo.revenue / totalRevenue) * 100);\n const avgPerClient = Math.round(geo.revenue / geo.clients);\n return (\n <tr key={geo.country} className=\"border-b hover:bg-bg-sunken/50\">\n <td className=\"p-3 font-medium\">{geo.country}</td>\n <td className=\"p-3 text-right\">{geo.clients}</td>\n <td className=\"p-3 text-right font-medium\">\n ${(geo.revenue / 1000).toFixed(0)}k\n </td>\n <td className=\"p-3 text-right text-text-secondary\">\n ${avgPerClient.toLocaleString()}\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-10 text-right text-xs\">{pct}%</span>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Users Tab */}\n {activeTab === 'users' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">User Role Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(roleCounts).map(([role, count]) => {\n const pct = Math.round((count / adminUsers.length) * 100);\n const roleColors: Record<string, string> = {\n client: 'bg-blue-500',\n provider: 'bg-green-500',\n admin: 'bg-red-500',\n team_member: 'bg-purple-500',\n };\n return (\n <div key={role} className=\"flex items-center gap-3\">\n <span className=\"w-24 text-sm font-medium capitalize\">\n {role.replace('_', ' ')}\n </span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-5 rounded ${roleColors[role] ?? 'bg-gray-400'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm\">\n {count} ({pct}%)\n </span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">User Status Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(statusCounts).map(([status, count]) => {\n const pct = Math.round((count / adminUsers.length) * 100);\n const statusColors: Record<string, string> = {\n active: 'bg-green-500',\n suspended: 'bg-orange-500',\n pending: 'bg-yellow-500',\n banned: 'bg-red-500',\n };\n return (\n <div key={status} className=\"flex items-center gap-3\">\n <span className=\"w-24 text-sm font-medium capitalize\">{status}</span>\n <div className=\"flex-1 h-5 bg-bg-sunken rounded overflow-hidden\">\n <div\n className={`h-5 rounded ${statusColors[status] ?? 'bg-gray-400'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm\">\n {count} ({pct}%)\n </span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n {/* Top users by spend */}\n <Card className=\"lg:col-span-2\">\n <CardHeader>\n <CardTitle className=\"text-base\">Top Users by Spending</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-2\">\n {[...adminUsers]\n .filter((u) => u.totalSpent > 0)\n .sort((a, b) => b.totalSpent - a.totalSpent)\n .slice(0, 5)\n .map((user, idx) => (\n <div\n key={user.id}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-full bg-action-primary-bg/10 text-sm font-bold text-primary\">\n {idx + 1}\n </span>\n <div>\n <p className=\"font-medium\">{user.name}</p>\n <p className=\"text-xs text-text-secondary\">{user.email}</p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"font-bold\">${user.totalSpent.toLocaleString()}</p>\n <p className=\"text-xs text-text-secondary\">\n {user.bookingsCount} bookings\n </p>\n </div>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Revenue Tab */}\n {activeTab === 'revenue' && (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Monthly Revenue Comparison</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {revenueData.map((point) => {\n const growth = point.prev\n ? (((point.value - point.prev) / point.prev) * 100).toFixed(1)\n : '0';\n const isPositive = point.prev ? point.value >= point.prev : true;\n return (\n <div key={point.name} className=\"rounded-lg border p-4\">\n <div className=\"flex items-center justify-between\">\n <div>\n <p className=\"font-medium\">{point.name} 2025</p>\n <div className=\"mt-1 flex items-center gap-4 text-sm text-text-secondary\">\n <span>Current: ${(point.value / 1000).toFixed(1)}k</span>\n <span>Previous: ${((point.prev ?? 0) / 1000).toFixed(1)}k</span>\n </div>\n </div>\n <Badge variant={isPositive ? 'default' : 'destructive'}>\n {isPositive ? '+' : ''}\n {growth}%\n </Badge>\n </div>\n <div className=\"mt-3 flex gap-1 h-4\">\n <div\n className=\"h-4 rounded bg-action-primary-bg/30\"\n style={{ width: `${((point.prev ?? 0) / maxRevenue) * 100}%` }}\n />\n <div\n className=\"h-4 rounded bg-action-primary-bg\"\n style={{\n width: `${Math.max(0, ((point.value - (point.prev ?? 0)) / maxRevenue) * 100)}%`,\n }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Bookings Tab */}\n {activeTab === 'bookings' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Bookings by Service Category</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .map((service, idx) => (\n <div key={service.name} className=\"flex items-center gap-3\">\n <span className=\"w-6 text-sm font-bold text-text-secondary\">#{idx + 1}</span>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"font-medium\">{service.name}</span>\n <span>{service.value} bookings</span>\n </div>\n <div className=\"mt-1 h-2 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-2 rounded-full bg-action-primary-bg\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Quick Stats</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {[\n {\n label: 'Total Bookings',\n value: servicePerformance.reduce((s, p) => s + p.value, 0).toLocaleString(),\n icon: ShoppingCart,\n },\n { label: 'Avg. Booking Value', value: '$3,804', icon: DollarSign },\n { label: 'Completion Rate', value: '96.5%', icon: TrendingUp },\n { label: 'Client Satisfaction', value: '4.7/5.0', icon: Star },\n ].map(({ label, value, icon: Icon }) => (\n <div\n key={label}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <Icon className=\"h-5 w-5 text-text-secondary\" />\n <span className=\"text-sm font-medium\">{label}</span>\n </div>\n <span className=\"text-lg font-bold\">{value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -69,7 +69,7 @@ function E() {
|
|
|
69
69
|
className: "text-2xl font-semibold",
|
|
70
70
|
children: "User Management"
|
|
71
71
|
}), /* @__PURE__ */ _("p", {
|
|
72
|
-
className: "text-sm text-
|
|
72
|
+
className: "text-sm text-text-secondary",
|
|
73
73
|
children: "Admin panel to manage platform users, roles, and access."
|
|
74
74
|
})] }), /* @__PURE__ */ v("div", {
|
|
75
75
|
className: "flex gap-2",
|
|
@@ -97,7 +97,7 @@ function E() {
|
|
|
97
97
|
className: "text-2xl font-bold",
|
|
98
98
|
children: P
|
|
99
99
|
}), /* @__PURE__ */ _("p", {
|
|
100
|
-
className: "text-xs text-
|
|
100
|
+
className: "text-xs text-text-secondary",
|
|
101
101
|
children: "Total Users"
|
|
102
102
|
})] })]
|
|
103
103
|
})
|
|
@@ -113,7 +113,7 @@ function E() {
|
|
|
113
113
|
className: "text-2xl font-bold",
|
|
114
114
|
children: F
|
|
115
115
|
}), /* @__PURE__ */ _("p", {
|
|
116
|
-
className: "text-xs text-
|
|
116
|
+
className: "text-xs text-text-secondary",
|
|
117
117
|
children: "Active Users"
|
|
118
118
|
})] })]
|
|
119
119
|
})
|
|
@@ -129,7 +129,7 @@ function E() {
|
|
|
129
129
|
className: "text-2xl font-bold",
|
|
130
130
|
children: I
|
|
131
131
|
}), /* @__PURE__ */ _("p", {
|
|
132
|
-
className: "text-xs text-
|
|
132
|
+
className: "text-xs text-text-secondary",
|
|
133
133
|
children: "Providers"
|
|
134
134
|
})] })]
|
|
135
135
|
})
|
|
@@ -145,7 +145,7 @@ function E() {
|
|
|
145
145
|
className: "text-2xl font-bold",
|
|
146
146
|
children: L
|
|
147
147
|
}), /* @__PURE__ */ _("p", {
|
|
148
|
-
className: "text-xs text-
|
|
148
|
+
className: "text-xs text-text-secondary",
|
|
149
149
|
children: "Verified"
|
|
150
150
|
})] })]
|
|
151
151
|
})
|
|
@@ -156,7 +156,7 @@ function E() {
|
|
|
156
156
|
className: "flex flex-col gap-4 sm:flex-row sm:items-center",
|
|
157
157
|
children: [/* @__PURE__ */ v("div", {
|
|
158
158
|
className: "relative flex-1",
|
|
159
|
-
children: [/* @__PURE__ */ _(d, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-
|
|
159
|
+
children: [/* @__PURE__ */ _(d, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ _(C, {
|
|
160
160
|
placeholder: "Search by name or email...",
|
|
161
161
|
value: a,
|
|
162
162
|
onChange: (e) => E(e.target.value),
|
|
@@ -165,9 +165,9 @@ function E() {
|
|
|
165
165
|
}), /* @__PURE__ */ v("div", {
|
|
166
166
|
className: "flex items-center gap-2",
|
|
167
167
|
children: [
|
|
168
|
-
/* @__PURE__ */ _(s, { className: "h-4 w-4 text-
|
|
168
|
+
/* @__PURE__ */ _(s, { className: "h-4 w-4 text-text-secondary" }),
|
|
169
169
|
/* @__PURE__ */ v("select", {
|
|
170
|
-
className: "rounded border bg-
|
|
170
|
+
className: "rounded border bg-bg-surface px-2 py-1 text-sm",
|
|
171
171
|
value: D,
|
|
172
172
|
onChange: (e) => O(e.target.value),
|
|
173
173
|
children: [
|
|
@@ -194,7 +194,7 @@ function E() {
|
|
|
194
194
|
]
|
|
195
195
|
}),
|
|
196
196
|
/* @__PURE__ */ v("select", {
|
|
197
|
-
className: "rounded border bg-
|
|
197
|
+
className: "rounded border bg-bg-surface px-2 py-1 text-sm",
|
|
198
198
|
value: k,
|
|
199
199
|
onChange: (e) => A(e.target.value),
|
|
200
200
|
children: [
|
|
@@ -224,7 +224,7 @@ function E() {
|
|
|
224
224
|
})]
|
|
225
225
|
}),
|
|
226
226
|
j.size > 0 && /* @__PURE__ */ v("div", {
|
|
227
|
-
className: "flex items-center gap-3 rounded-lg bg-primary/5 border border-primary/20 p-3",
|
|
227
|
+
className: "flex items-center gap-3 rounded-lg bg-action-primary-bg/5 border border-primary/20 p-3",
|
|
228
228
|
children: [
|
|
229
229
|
/* @__PURE__ */ v("span", {
|
|
230
230
|
className: "text-sm font-medium",
|
|
@@ -254,7 +254,7 @@ function E() {
|
|
|
254
254
|
children: /* @__PURE__ */ v("table", {
|
|
255
255
|
className: "w-full text-sm",
|
|
256
256
|
children: [/* @__PURE__ */ _("thead", { children: /* @__PURE__ */ v("tr", {
|
|
257
|
-
className: "border-b text-left text-
|
|
257
|
+
className: "border-b text-left text-text-secondary",
|
|
258
258
|
children: [
|
|
259
259
|
/* @__PURE__ */ _("th", {
|
|
260
260
|
className: "p-3",
|
|
@@ -310,12 +310,12 @@ function E() {
|
|
|
310
310
|
]
|
|
311
311
|
}) }), /* @__PURE__ */ _("tbody", { children: N.length === 0 ? /* @__PURE__ */ _("tr", { children: /* @__PURE__ */ _("td", {
|
|
312
312
|
colSpan: 11,
|
|
313
|
-
className: "p-8 text-center text-
|
|
313
|
+
className: "p-8 text-center text-text-secondary",
|
|
314
314
|
children: "No users match your filters."
|
|
315
315
|
}) }) : N.map((e) => {
|
|
316
316
|
let t = T[e.status], n = t.icon;
|
|
317
317
|
return /* @__PURE__ */ v("tr", {
|
|
318
|
-
className: "border-b hover:bg-
|
|
318
|
+
className: "border-b hover:bg-bg-sunken/50",
|
|
319
319
|
children: [
|
|
320
320
|
/* @__PURE__ */ _("td", {
|
|
321
321
|
className: "p-3",
|
|
@@ -331,13 +331,13 @@ function E() {
|
|
|
331
331
|
children: /* @__PURE__ */ v("div", {
|
|
332
332
|
className: "flex items-center gap-3",
|
|
333
333
|
children: [/* @__PURE__ */ _("div", {
|
|
334
|
-
className: "flex h-8 w-8 items-center justify-center rounded-full bg-primary/10 text-xs font-semibold text-primary",
|
|
334
|
+
className: "flex h-8 w-8 items-center justify-center rounded-full bg-action-primary-bg/10 text-xs font-semibold text-primary",
|
|
335
335
|
children: e.name.charAt(0)
|
|
336
336
|
}), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
|
|
337
337
|
className: "font-medium",
|
|
338
338
|
children: e.name
|
|
339
339
|
}), /* @__PURE__ */ _("p", {
|
|
340
|
-
className: "text-xs text-
|
|
340
|
+
className: "text-xs text-text-secondary",
|
|
341
341
|
children: e.email
|
|
342
342
|
})] })]
|
|
343
343
|
})
|
|
@@ -364,11 +364,11 @@ function E() {
|
|
|
364
364
|
children: e.verified ? /* @__PURE__ */ _(i, { className: "h-4 w-4 text-green-500" }) : /* @__PURE__ */ _(g, { className: "h-4 w-4 text-gray-300" })
|
|
365
365
|
}),
|
|
366
366
|
/* @__PURE__ */ _("td", {
|
|
367
|
-
className: "p-3 text-
|
|
367
|
+
className: "p-3 text-text-secondary",
|
|
368
368
|
children: e.joinedDate
|
|
369
369
|
}),
|
|
370
370
|
/* @__PURE__ */ _("td", {
|
|
371
|
-
className: "p-3 text-
|
|
371
|
+
className: "p-3 text-text-secondary",
|
|
372
372
|
children: e.lastActive
|
|
373
373
|
}),
|
|
374
374
|
/* @__PURE__ */ _("td", {
|
|
@@ -388,7 +388,7 @@ function E() {
|
|
|
388
388
|
children: e.rating
|
|
389
389
|
})]
|
|
390
390
|
}) : /* @__PURE__ */ _("span", {
|
|
391
|
-
className: "text-xs text-
|
|
391
|
+
className: "text-xs text-text-secondary",
|
|
392
392
|
children: "-"
|
|
393
393
|
})
|
|
394
394
|
}),
|
|
@@ -412,7 +412,7 @@ function E() {
|
|
|
412
412
|
}) })]
|
|
413
413
|
})
|
|
414
414
|
}), /* @__PURE__ */ v("div", {
|
|
415
|
-
className: "mt-4 flex items-center justify-between border-t pt-4 text-sm text-
|
|
415
|
+
className: "mt-4 flex items-center justify-between border-t pt-4 text-sm text-text-secondary",
|
|
416
416
|
children: [/* @__PURE__ */ v("span", { children: [
|
|
417
417
|
"Showing ",
|
|
418
418
|
N.length,
|