@burdenoff/microfe-proserviceworld 2026.626.2 → 2026.706.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/index.js +2 -3
- package/dist/node_modules/clsx/dist/clsx.js +18 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/dist/proserviceworld/ProServiceWorldRoot.js +92 -136
- package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -1
- package/dist/proserviceworld/ProServiceWorldRoutes.js +18 -26
- package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -1
- package/dist/proserviceworld/components/ui.js +273 -0
- package/dist/proserviceworld/components/ui.js.map +1 -0
- package/dist/proserviceworld/hooks/useFiles.js +45 -0
- package/dist/proserviceworld/hooks/useFiles.js.map +1 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js +416 -0
- package/dist/proserviceworld/hooks/useProServiceWorldApi.js.map +1 -0
- package/dist/proserviceworld/index.js +0 -1
- package/dist/proserviceworld/pages/AnalyticsPage.js +163 -475
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
- package/dist/proserviceworld/pages/ClientDetailPage.js +238 -0
- package/dist/proserviceworld/pages/ClientDetailPage.js.map +1 -0
- package/dist/proserviceworld/pages/ClientsPage.js +295 -0
- package/dist/proserviceworld/pages/ClientsPage.js.map +1 -0
- package/dist/proserviceworld/pages/CompliancePage.js +307 -0
- package/dist/proserviceworld/pages/CompliancePage.js.map +1 -0
- package/dist/proserviceworld/pages/ConflictsPage.js +234 -0
- package/dist/proserviceworld/pages/ConflictsPage.js.map +1 -0
- package/dist/proserviceworld/pages/DashboardPage.js +179 -632
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
- package/dist/proserviceworld/pages/DocumentsPage.js +235 -539
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
- package/dist/proserviceworld/pages/EngagementDetailPage.js +444 -0
- package/dist/proserviceworld/pages/EngagementDetailPage.js.map +1 -0
- package/dist/proserviceworld/pages/EngagementsPage.js +256 -0
- package/dist/proserviceworld/pages/EngagementsPage.js.map +1 -0
- package/dist/proserviceworld/pages/InvoicesPage.js +303 -0
- package/dist/proserviceworld/pages/InvoicesPage.js.map +1 -0
- package/dist/proserviceworld/pages/MattersPage.js +275 -0
- package/dist/proserviceworld/pages/MattersPage.js.map +1 -0
- package/dist/proserviceworld/pages/PracticeAreasPage.js +176 -0
- package/dist/proserviceworld/pages/PracticeAreasPage.js.map +1 -0
- package/dist/proserviceworld/pages/TimePage.js +310 -0
- package/dist/proserviceworld/pages/TimePage.js.map +1 -0
- package/dist/proserviceworld/utils/cn.js +10 -0
- package/dist/proserviceworld/utils/cn.js.map +1 -0
- package/dist/proserviceworld/utils/nav.js +11 -0
- package/dist/proserviceworld/utils/nav.js.map +1 -0
- package/package.json +3 -2
- package/dist/proserviceworld/pages/AdminReportsPage.js +0 -579
- package/dist/proserviceworld/pages/AdminReportsPage.js.map +0 -1
- package/dist/proserviceworld/pages/AdminUsersPage.js +0 -468
- package/dist/proserviceworld/pages/AdminUsersPage.js.map +0 -1
- package/dist/proserviceworld/pages/BookingsPage.js +0 -446
- package/dist/proserviceworld/pages/BookingsPage.js.map +0 -1
- package/dist/proserviceworld/pages/MessagesPage.js +0 -238
- package/dist/proserviceworld/pages/MessagesPage.js.map +0 -1
- package/dist/proserviceworld/pages/PaymentsPage.js +0 -537
- package/dist/proserviceworld/pages/PaymentsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ProjectsPage.js +0 -627
- package/dist/proserviceworld/pages/ProjectsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ProviderDetailPage.js +0 -538
- package/dist/proserviceworld/pages/ProviderDetailPage.js.map +0 -1
- package/dist/proserviceworld/pages/ProvidersPage.js +0 -347
- package/dist/proserviceworld/pages/ProvidersPage.js.map +0 -1
- package/dist/proserviceworld/pages/ReviewsPage.js +0 -356
- package/dist/proserviceworld/pages/ReviewsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ServiceDetailsPage.js +0 -631
- package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +0 -1
- package/dist/proserviceworld/pages/ServicesPage.js +0 -344
- package/dist/proserviceworld/pages/ServicesPage.js.map +0 -1
- package/dist/proserviceworld/pages/VerificationPage.js +0 -370
- package/dist/proserviceworld/pages/VerificationPage.js.map +0 -1
- package/dist/proserviceworld/store/proserviceworldStore.js +0 -2490
- package/dist/proserviceworld/store/proserviceworldStore.js.map +0 -1
- package/dist/proserviceworld/utils/i18n.js +0 -15
- package/dist/proserviceworld/utils/i18n.js.map +0 -1
|
@@ -1,579 +0,0 @@
|
|
|
1
|
-
import { useProServiceWorldStore as e } from "../store/proserviceworldStore.js";
|
|
2
|
-
import { useState as t } from "react";
|
|
3
|
-
import { AlertTriangle as n, ArrowUpRight as r, BarChart3 as i, Calendar as a, DollarSign as o, Download as s, FileText as c, Minus as l, Shield as u, ShoppingCart as d, Star as f, TrendingDown as p, TrendingUp as m, Users as h } from "lucide-react";
|
|
4
|
-
import { jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
5
|
-
import { AuroraBackground as v, Badge as y, CTAOverflowMenu as b, Card as x, CardContent as S, CardHeader as C, CardTitle as w, GlassCard as T, GradientText as E, NextSteps as D, PagePurpose as O } from "@burdenoff/fe-libs/ui";
|
|
6
|
-
//#region src/proserviceworld/pages/AdminReportsPage.tsx
|
|
7
|
-
function k() {
|
|
8
|
-
let { adminReportMetrics: k, revenueData: A, servicePerformance: j, geographicData: M, adminUsers: N } = e(), [P, F] = t("overview"), [I, L] = t("30d"), R = [
|
|
9
|
-
{
|
|
10
|
-
key: "overview",
|
|
11
|
-
label: "Overview",
|
|
12
|
-
icon: i
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
key: "users",
|
|
16
|
-
label: "Users",
|
|
17
|
-
icon: h
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
key: "revenue",
|
|
21
|
-
label: "Revenue",
|
|
22
|
-
icon: o
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
key: "bookings",
|
|
26
|
-
label: "Bookings",
|
|
27
|
-
icon: d
|
|
28
|
-
}
|
|
29
|
-
], z = (e) => e === "positive" ? /* @__PURE__ */ g(m, { className: "h-4 w-4 text-status-success-text" }) : e === "negative" ? /* @__PURE__ */ g(p, { className: "h-4 w-4 text-status-error-text" }) : /* @__PURE__ */ g(l, { className: "h-4 w-4 text-text-secondary" }), B = (e) => e === "positive" ? "text-status-success-text" : e === "negative" ? "text-status-error-text" : "text-text-secondary", V = [
|
|
30
|
-
h,
|
|
31
|
-
u,
|
|
32
|
-
d,
|
|
33
|
-
o,
|
|
34
|
-
f,
|
|
35
|
-
n
|
|
36
|
-
], H = Math.max(...A.map((e) => e.value)), U = Math.max(...j.map((e) => e.value)), W = N.reduce((e, t) => (e[t.role] = (e[t.role] ?? 0) + 1, e), {}), G = N.reduce((e, t) => (e[t.status] = (e[t.status] ?? 0) + 1, e), {}), K = {
|
|
37
|
-
client: "bg-chart-1",
|
|
38
|
-
provider: "bg-chart-2",
|
|
39
|
-
admin: "bg-chart-3",
|
|
40
|
-
team_member: "bg-chart-4"
|
|
41
|
-
}, q = {
|
|
42
|
-
active: "bg-status-success-text",
|
|
43
|
-
suspended: "bg-status-warning-text",
|
|
44
|
-
pending: "bg-chart-5",
|
|
45
|
-
banned: "bg-status-error-text"
|
|
46
|
-
};
|
|
47
|
-
return /* @__PURE__ */ _("div", {
|
|
48
|
-
className: "space-y-6 p-6 bg-bg-canvas min-h-screen",
|
|
49
|
-
children: [
|
|
50
|
-
/* @__PURE__ */ _("div", {
|
|
51
|
-
className: "relative overflow-hidden rounded-card -mx-2 px-2 pt-1",
|
|
52
|
-
children: [/* @__PURE__ */ g(v, { intensity: "subtle" }), /* @__PURE__ */ _("div", {
|
|
53
|
-
className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
|
|
54
|
-
children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h1", {
|
|
55
|
-
className: "text-2xl font-semibold tracking-tight",
|
|
56
|
-
children: /* @__PURE__ */ g(E, { children: "Admin Reports" })
|
|
57
|
-
}), /* @__PURE__ */ g("p", {
|
|
58
|
-
className: "text-sm text-text-secondary",
|
|
59
|
-
children: "Platform analytics, performance metrics, and operational reports."
|
|
60
|
-
})] }), /* @__PURE__ */ g(b, {
|
|
61
|
-
primary: {
|
|
62
|
-
label: "Generate Report",
|
|
63
|
-
onSelect: () => {},
|
|
64
|
-
icon: /* @__PURE__ */ g(c, { className: "h-4 w-4" })
|
|
65
|
-
},
|
|
66
|
-
actions: [{
|
|
67
|
-
label: "Export CSV",
|
|
68
|
-
onSelect: () => {},
|
|
69
|
-
icon: /* @__PURE__ */ g(s, { className: "h-4 w-4" })
|
|
70
|
-
}]
|
|
71
|
-
})]
|
|
72
|
-
})]
|
|
73
|
-
}),
|
|
74
|
-
/* @__PURE__ */ g(O, {
|
|
75
|
-
icon: /* @__PURE__ */ g(i, { className: "h-4 w-4" }),
|
|
76
|
-
children: "The operational pulse of the platform. Track headline KPIs, dig into revenue, user, and booking trends across time ranges, and export reports for stakeholders — use it to spot what is growing, what needs attention, and where to act next."
|
|
77
|
-
}),
|
|
78
|
-
/* @__PURE__ */ g(D, {
|
|
79
|
-
storageKey: "proserviceworld-admin-reports",
|
|
80
|
-
steps: [
|
|
81
|
-
{
|
|
82
|
-
id: "revenue",
|
|
83
|
-
label: "Review revenue trends",
|
|
84
|
-
description: "Open the Revenue tab to compare months and spot growth or dips.",
|
|
85
|
-
icon: /* @__PURE__ */ g(o, { className: "h-4 w-4" }),
|
|
86
|
-
onClick: () => F("revenue")
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
id: "users",
|
|
90
|
-
label: "Check user health",
|
|
91
|
-
description: "See role and status distribution plus your top spenders.",
|
|
92
|
-
icon: /* @__PURE__ */ g(h, { className: "h-4 w-4" }),
|
|
93
|
-
onClick: () => F("users")
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
id: "export",
|
|
97
|
-
label: "Export a report",
|
|
98
|
-
description: "Generate a shareable report or CSV for stakeholders.",
|
|
99
|
-
icon: /* @__PURE__ */ g(s, { className: "h-4 w-4" }),
|
|
100
|
-
onClick: () => {}
|
|
101
|
-
}
|
|
102
|
-
]
|
|
103
|
-
}),
|
|
104
|
-
/* @__PURE__ */ _("div", {
|
|
105
|
-
className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
|
|
106
|
-
children: [/* @__PURE__ */ g("div", {
|
|
107
|
-
className: "flex gap-1 border-b border-border-seam",
|
|
108
|
-
children: R.map(({ key: e, label: t, icon: n }) => /* @__PURE__ */ _("button", {
|
|
109
|
-
onClick: () => F(e),
|
|
110
|
-
"aria-pressed": P === e,
|
|
111
|
-
className: `flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${P === e ? "border-accent-blue text-accent-blue" : "border-transparent text-text-secondary hover:text-text-primary"}`,
|
|
112
|
-
children: [/* @__PURE__ */ g(n, { className: "h-4 w-4" }), t]
|
|
113
|
-
}, e))
|
|
114
|
-
}), /* @__PURE__ */ _("div", {
|
|
115
|
-
className: "flex items-center gap-2",
|
|
116
|
-
children: [/* @__PURE__ */ g(a, { className: "h-4 w-4 text-text-secondary" }), /* @__PURE__ */ g("div", {
|
|
117
|
-
className: "flex rounded border border-border-seam",
|
|
118
|
-
children: [
|
|
119
|
-
"7d",
|
|
120
|
-
"30d",
|
|
121
|
-
"90d",
|
|
122
|
-
"1y"
|
|
123
|
-
].map((e) => /* @__PURE__ */ g("button", {
|
|
124
|
-
onClick: () => L(e),
|
|
125
|
-
"aria-pressed": I === e,
|
|
126
|
-
className: `px-3 py-1 text-xs font-medium tabular-nums focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${I === e ? "bg-accent-blue text-action-primary-text" : "text-text-secondary hover:bg-bg-sunken"}`,
|
|
127
|
-
children: e
|
|
128
|
-
}, e))
|
|
129
|
-
})]
|
|
130
|
-
})]
|
|
131
|
-
}),
|
|
132
|
-
/* @__PURE__ */ g("div", {
|
|
133
|
-
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
134
|
-
children: k.map((e, t) => {
|
|
135
|
-
let n = V[t % V.length] ?? i;
|
|
136
|
-
return /* @__PURE__ */ g(T, {
|
|
137
|
-
treatment: t === 0 ? "glass" : "metric",
|
|
138
|
-
glow: t === 0,
|
|
139
|
-
className: "border-border-seam",
|
|
140
|
-
children: /* @__PURE__ */ _(S, {
|
|
141
|
-
className: "pt-4",
|
|
142
|
-
children: [
|
|
143
|
-
/* @__PURE__ */ _("div", {
|
|
144
|
-
className: "flex items-center justify-between",
|
|
145
|
-
children: [/* @__PURE__ */ _("div", {
|
|
146
|
-
className: "flex items-center gap-2",
|
|
147
|
-
children: [/* @__PURE__ */ g(n, { className: "h-4 w-4 text-text-secondary" }), /* @__PURE__ */ g("p", {
|
|
148
|
-
className: "text-sm font-medium text-text-secondary",
|
|
149
|
-
children: e.label
|
|
150
|
-
})]
|
|
151
|
-
}), z(e.changeType)]
|
|
152
|
-
}),
|
|
153
|
-
/* @__PURE__ */ g("p", {
|
|
154
|
-
className: "mt-2 text-3xl font-bold tabular-nums",
|
|
155
|
-
children: e.value
|
|
156
|
-
}),
|
|
157
|
-
/* @__PURE__ */ _("p", {
|
|
158
|
-
className: `mt-1 flex items-center gap-1 text-xs ${B(e.changeType)}`,
|
|
159
|
-
children: [
|
|
160
|
-
/* @__PURE__ */ g(r, { className: "h-3 w-3" }),
|
|
161
|
-
e.change,
|
|
162
|
-
" vs last period"
|
|
163
|
-
]
|
|
164
|
-
})
|
|
165
|
-
]
|
|
166
|
-
})
|
|
167
|
-
}, t);
|
|
168
|
-
})
|
|
169
|
-
}),
|
|
170
|
-
P === "overview" && /* @__PURE__ */ _("div", {
|
|
171
|
-
className: "grid gap-6 lg:grid-cols-2",
|
|
172
|
-
children: [
|
|
173
|
-
/* @__PURE__ */ _(x, {
|
|
174
|
-
className: "border-border-seam",
|
|
175
|
-
children: [/* @__PURE__ */ g(C, { children: /* @__PURE__ */ _("div", {
|
|
176
|
-
className: "flex items-center justify-between",
|
|
177
|
-
children: [/* @__PURE__ */ g(w, {
|
|
178
|
-
className: "text-base",
|
|
179
|
-
children: "Platform Revenue"
|
|
180
|
-
}), /* @__PURE__ */ g(y, {
|
|
181
|
-
variant: "secondary",
|
|
182
|
-
children: "Monthly"
|
|
183
|
-
})]
|
|
184
|
-
}) }), /* @__PURE__ */ g(S, {
|
|
185
|
-
className: "space-y-3",
|
|
186
|
-
children: A.map((e) => /* @__PURE__ */ _("div", {
|
|
187
|
-
className: "flex items-center gap-3",
|
|
188
|
-
children: [
|
|
189
|
-
/* @__PURE__ */ g("span", {
|
|
190
|
-
className: "w-10 text-xs text-text-secondary",
|
|
191
|
-
children: e.name
|
|
192
|
-
}),
|
|
193
|
-
/* @__PURE__ */ g("div", {
|
|
194
|
-
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
195
|
-
children: /* @__PURE__ */ g("div", {
|
|
196
|
-
className: "h-5 bg-accent-blue rounded",
|
|
197
|
-
style: { width: `${e.value / H * 100}%` }
|
|
198
|
-
})
|
|
199
|
-
}),
|
|
200
|
-
/* @__PURE__ */ _("span", {
|
|
201
|
-
className: "w-16 text-right text-xs font-medium tabular-nums",
|
|
202
|
-
children: [
|
|
203
|
-
"$",
|
|
204
|
-
(e.value / 1e3).toFixed(1),
|
|
205
|
-
"k"
|
|
206
|
-
]
|
|
207
|
-
})
|
|
208
|
-
]
|
|
209
|
-
}, e.name))
|
|
210
|
-
})]
|
|
211
|
-
}),
|
|
212
|
-
/* @__PURE__ */ _(x, {
|
|
213
|
-
className: "border-border-seam",
|
|
214
|
-
children: [/* @__PURE__ */ g(C, { children: /* @__PURE__ */ _("div", {
|
|
215
|
-
className: "flex items-center justify-between",
|
|
216
|
-
children: [/* @__PURE__ */ g(w, {
|
|
217
|
-
className: "text-base",
|
|
218
|
-
children: "Service Bookings"
|
|
219
|
-
}), /* @__PURE__ */ g(y, {
|
|
220
|
-
variant: "secondary",
|
|
221
|
-
children: "Total"
|
|
222
|
-
})]
|
|
223
|
-
}) }), /* @__PURE__ */ g(S, {
|
|
224
|
-
className: "space-y-3",
|
|
225
|
-
children: [...j].sort((e, t) => t.value - e.value).map((e) => /* @__PURE__ */ _("div", {
|
|
226
|
-
className: "flex items-center gap-3",
|
|
227
|
-
children: [
|
|
228
|
-
/* @__PURE__ */ g("span", {
|
|
229
|
-
className: "w-32 truncate text-xs text-text-secondary",
|
|
230
|
-
children: e.name
|
|
231
|
-
}),
|
|
232
|
-
/* @__PURE__ */ g("div", {
|
|
233
|
-
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
234
|
-
children: /* @__PURE__ */ g("div", {
|
|
235
|
-
className: "h-5 bg-accent-purple rounded",
|
|
236
|
-
style: { width: `${e.value / U * 100}%` }
|
|
237
|
-
})
|
|
238
|
-
}),
|
|
239
|
-
/* @__PURE__ */ g("span", {
|
|
240
|
-
className: "w-12 text-right text-xs font-medium tabular-nums",
|
|
241
|
-
children: e.value
|
|
242
|
-
})
|
|
243
|
-
]
|
|
244
|
-
}, e.name))
|
|
245
|
-
})]
|
|
246
|
-
}),
|
|
247
|
-
/* @__PURE__ */ _(x, {
|
|
248
|
-
className: "lg:col-span-2 border-border-seam",
|
|
249
|
-
children: [/* @__PURE__ */ g(C, { children: /* @__PURE__ */ g(w, {
|
|
250
|
-
className: "text-base",
|
|
251
|
-
children: "Revenue by Region"
|
|
252
|
-
}) }), /* @__PURE__ */ g(S, { children: /* @__PURE__ */ g("div", {
|
|
253
|
-
className: "overflow-x-auto",
|
|
254
|
-
children: /* @__PURE__ */ _("table", {
|
|
255
|
-
className: "w-full text-sm",
|
|
256
|
-
children: [/* @__PURE__ */ g("thead", { children: /* @__PURE__ */ _("tr", {
|
|
257
|
-
className: "border-b border-border-seam text-left text-text-secondary",
|
|
258
|
-
children: [
|
|
259
|
-
/* @__PURE__ */ g("th", {
|
|
260
|
-
className: "p-3",
|
|
261
|
-
children: "Region"
|
|
262
|
-
}),
|
|
263
|
-
/* @__PURE__ */ g("th", {
|
|
264
|
-
className: "p-3 text-right",
|
|
265
|
-
children: "Clients"
|
|
266
|
-
}),
|
|
267
|
-
/* @__PURE__ */ g("th", {
|
|
268
|
-
className: "p-3 text-right",
|
|
269
|
-
children: "Revenue"
|
|
270
|
-
}),
|
|
271
|
-
/* @__PURE__ */ g("th", {
|
|
272
|
-
className: "p-3 text-right",
|
|
273
|
-
children: "Avg. per Client"
|
|
274
|
-
}),
|
|
275
|
-
/* @__PURE__ */ g("th", {
|
|
276
|
-
className: "p-3",
|
|
277
|
-
children: "Share"
|
|
278
|
-
})
|
|
279
|
-
]
|
|
280
|
-
}) }), /* @__PURE__ */ g("tbody", { children: M.map((e) => {
|
|
281
|
-
let t = M.reduce((e, t) => e + t.revenue, 0), n = Math.round(e.revenue / t * 100), r = Math.round(e.revenue / e.clients);
|
|
282
|
-
return /* @__PURE__ */ _("tr", {
|
|
283
|
-
className: "border-b border-border-seam hover:bg-bg-sunken",
|
|
284
|
-
children: [
|
|
285
|
-
/* @__PURE__ */ g("td", {
|
|
286
|
-
className: "p-3 font-medium",
|
|
287
|
-
children: e.country
|
|
288
|
-
}),
|
|
289
|
-
/* @__PURE__ */ g("td", {
|
|
290
|
-
className: "p-3 text-right tabular-nums",
|
|
291
|
-
children: e.clients
|
|
292
|
-
}),
|
|
293
|
-
/* @__PURE__ */ _("td", {
|
|
294
|
-
className: "p-3 text-right font-medium tabular-nums",
|
|
295
|
-
children: [
|
|
296
|
-
"$",
|
|
297
|
-
(e.revenue / 1e3).toFixed(0),
|
|
298
|
-
"k"
|
|
299
|
-
]
|
|
300
|
-
}),
|
|
301
|
-
/* @__PURE__ */ _("td", {
|
|
302
|
-
className: "p-3 text-right text-text-secondary tabular-nums",
|
|
303
|
-
children: ["$", r.toLocaleString()]
|
|
304
|
-
}),
|
|
305
|
-
/* @__PURE__ */ g("td", {
|
|
306
|
-
className: "p-3",
|
|
307
|
-
children: /* @__PURE__ */ _("div", {
|
|
308
|
-
className: "flex items-center gap-2",
|
|
309
|
-
children: [/* @__PURE__ */ g("div", {
|
|
310
|
-
className: "h-2 flex-1 overflow-hidden rounded-full bg-bg-sunken",
|
|
311
|
-
children: /* @__PURE__ */ g("div", {
|
|
312
|
-
className: "h-2 rounded-full bg-accent-blue",
|
|
313
|
-
style: { width: `${n}%` }
|
|
314
|
-
})
|
|
315
|
-
}), /* @__PURE__ */ _("span", {
|
|
316
|
-
className: "w-10 text-right text-xs tabular-nums",
|
|
317
|
-
children: [n, "%"]
|
|
318
|
-
})]
|
|
319
|
-
})
|
|
320
|
-
})
|
|
321
|
-
]
|
|
322
|
-
}, e.country);
|
|
323
|
-
}) })]
|
|
324
|
-
})
|
|
325
|
-
}) })]
|
|
326
|
-
})
|
|
327
|
-
]
|
|
328
|
-
}),
|
|
329
|
-
P === "users" && /* @__PURE__ */ _("div", {
|
|
330
|
-
className: "grid gap-6 lg:grid-cols-2",
|
|
331
|
-
children: [
|
|
332
|
-
/* @__PURE__ */ _(x, {
|
|
333
|
-
className: "border-border-seam",
|
|
334
|
-
children: [/* @__PURE__ */ g(C, { children: /* @__PURE__ */ g(w, {
|
|
335
|
-
className: "text-base",
|
|
336
|
-
children: "User Role Distribution"
|
|
337
|
-
}) }), /* @__PURE__ */ g(S, {
|
|
338
|
-
className: "space-y-3",
|
|
339
|
-
children: Object.entries(W).map(([e, t]) => {
|
|
340
|
-
let n = Math.round(t / N.length * 100);
|
|
341
|
-
return /* @__PURE__ */ _("div", {
|
|
342
|
-
className: "flex items-center gap-3",
|
|
343
|
-
children: [
|
|
344
|
-
/* @__PURE__ */ g("span", {
|
|
345
|
-
className: "w-24 text-sm font-medium capitalize",
|
|
346
|
-
children: e.replace("_", " ")
|
|
347
|
-
}),
|
|
348
|
-
/* @__PURE__ */ g("div", {
|
|
349
|
-
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
350
|
-
children: /* @__PURE__ */ g("div", {
|
|
351
|
-
className: `h-5 rounded ${K[e] ?? "bg-chart-6"}`,
|
|
352
|
-
style: { width: `${n}%` }
|
|
353
|
-
})
|
|
354
|
-
}),
|
|
355
|
-
/* @__PURE__ */ _("span", {
|
|
356
|
-
className: "w-16 text-right text-sm tabular-nums",
|
|
357
|
-
children: [
|
|
358
|
-
t,
|
|
359
|
-
" (",
|
|
360
|
-
n,
|
|
361
|
-
"%)"
|
|
362
|
-
]
|
|
363
|
-
})
|
|
364
|
-
]
|
|
365
|
-
}, e);
|
|
366
|
-
})
|
|
367
|
-
})]
|
|
368
|
-
}),
|
|
369
|
-
/* @__PURE__ */ _(x, {
|
|
370
|
-
className: "border-border-seam",
|
|
371
|
-
children: [/* @__PURE__ */ g(C, { children: /* @__PURE__ */ g(w, {
|
|
372
|
-
className: "text-base",
|
|
373
|
-
children: "User Status Distribution"
|
|
374
|
-
}) }), /* @__PURE__ */ g(S, {
|
|
375
|
-
className: "space-y-3",
|
|
376
|
-
children: Object.entries(G).map(([e, t]) => {
|
|
377
|
-
let n = Math.round(t / N.length * 100);
|
|
378
|
-
return /* @__PURE__ */ _("div", {
|
|
379
|
-
className: "flex items-center gap-3",
|
|
380
|
-
children: [
|
|
381
|
-
/* @__PURE__ */ g("span", {
|
|
382
|
-
className: "w-24 text-sm font-medium capitalize",
|
|
383
|
-
children: e
|
|
384
|
-
}),
|
|
385
|
-
/* @__PURE__ */ g("div", {
|
|
386
|
-
className: "flex-1 h-5 bg-bg-sunken rounded overflow-hidden",
|
|
387
|
-
children: /* @__PURE__ */ g("div", {
|
|
388
|
-
className: `h-5 rounded ${q[e] ?? "bg-chart-6"}`,
|
|
389
|
-
style: { width: `${n}%` }
|
|
390
|
-
})
|
|
391
|
-
}),
|
|
392
|
-
/* @__PURE__ */ _("span", {
|
|
393
|
-
className: "w-16 text-right text-sm tabular-nums",
|
|
394
|
-
children: [
|
|
395
|
-
t,
|
|
396
|
-
" (",
|
|
397
|
-
n,
|
|
398
|
-
"%)"
|
|
399
|
-
]
|
|
400
|
-
})
|
|
401
|
-
]
|
|
402
|
-
}, e);
|
|
403
|
-
})
|
|
404
|
-
})]
|
|
405
|
-
}),
|
|
406
|
-
/* @__PURE__ */ _(x, {
|
|
407
|
-
className: "lg:col-span-2 border-border-seam",
|
|
408
|
-
children: [/* @__PURE__ */ g(C, { children: /* @__PURE__ */ g(w, {
|
|
409
|
-
className: "text-base",
|
|
410
|
-
children: "Top Users by Spending"
|
|
411
|
-
}) }), /* @__PURE__ */ g(S, { children: /* @__PURE__ */ g("div", {
|
|
412
|
-
className: "space-y-2",
|
|
413
|
-
children: [...N].filter((e) => e.totalSpent > 0).sort((e, t) => t.totalSpent - e.totalSpent).slice(0, 5).map((e, t) => /* @__PURE__ */ _("div", {
|
|
414
|
-
className: "flex items-center justify-between rounded-lg border border-border-seam p-3",
|
|
415
|
-
children: [/* @__PURE__ */ _("div", {
|
|
416
|
-
className: "flex items-center gap-3",
|
|
417
|
-
children: [/* @__PURE__ */ g("span", {
|
|
418
|
-
className: "flex h-8 w-8 items-center justify-center rounded-full bg-accent-soft text-sm font-bold text-text-primary tabular-nums",
|
|
419
|
-
children: t + 1
|
|
420
|
-
}), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
|
|
421
|
-
className: "font-medium",
|
|
422
|
-
children: e.name
|
|
423
|
-
}), /* @__PURE__ */ g("p", {
|
|
424
|
-
className: "text-xs text-text-secondary",
|
|
425
|
-
children: e.email
|
|
426
|
-
})] })]
|
|
427
|
-
}), /* @__PURE__ */ _("div", {
|
|
428
|
-
className: "text-right",
|
|
429
|
-
children: [/* @__PURE__ */ _("p", {
|
|
430
|
-
className: "font-bold tabular-nums",
|
|
431
|
-
children: ["$", e.totalSpent.toLocaleString()]
|
|
432
|
-
}), /* @__PURE__ */ _("p", {
|
|
433
|
-
className: "text-xs text-text-secondary tabular-nums",
|
|
434
|
-
children: [e.bookingsCount, " bookings"]
|
|
435
|
-
})]
|
|
436
|
-
})]
|
|
437
|
-
}, e.id))
|
|
438
|
-
}) })]
|
|
439
|
-
})
|
|
440
|
-
]
|
|
441
|
-
}),
|
|
442
|
-
P === "revenue" && /* @__PURE__ */ g("div", {
|
|
443
|
-
className: "space-y-6",
|
|
444
|
-
children: /* @__PURE__ */ _(x, {
|
|
445
|
-
className: "border-border-seam",
|
|
446
|
-
children: [/* @__PURE__ */ g(C, { children: /* @__PURE__ */ g(w, {
|
|
447
|
-
className: "text-base",
|
|
448
|
-
children: "Monthly Revenue Comparison"
|
|
449
|
-
}) }), /* @__PURE__ */ g(S, {
|
|
450
|
-
className: "space-y-4",
|
|
451
|
-
children: A.map((e) => {
|
|
452
|
-
let t = e.prev ? ((e.value - e.prev) / e.prev * 100).toFixed(1) : "0", n = e.prev ? e.value >= e.prev : !0;
|
|
453
|
-
return /* @__PURE__ */ _("div", {
|
|
454
|
-
className: "rounded-lg border border-border-seam p-4",
|
|
455
|
-
children: [/* @__PURE__ */ _("div", {
|
|
456
|
-
className: "flex items-center justify-between",
|
|
457
|
-
children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ _("p", {
|
|
458
|
-
className: "font-medium",
|
|
459
|
-
children: [e.name, " 2025"]
|
|
460
|
-
}), /* @__PURE__ */ _("div", {
|
|
461
|
-
className: "mt-1 flex items-center gap-4 text-sm text-text-secondary tabular-nums",
|
|
462
|
-
children: [/* @__PURE__ */ _("span", { children: [
|
|
463
|
-
"Current: $",
|
|
464
|
-
(e.value / 1e3).toFixed(1),
|
|
465
|
-
"k"
|
|
466
|
-
] }), /* @__PURE__ */ _("span", { children: [
|
|
467
|
-
"Previous: $",
|
|
468
|
-
((e.prev ?? 0) / 1e3).toFixed(1),
|
|
469
|
-
"k"
|
|
470
|
-
] })]
|
|
471
|
-
})] }), /* @__PURE__ */ _(y, {
|
|
472
|
-
variant: n ? "default" : "destructive",
|
|
473
|
-
children: [
|
|
474
|
-
n ? "+" : "",
|
|
475
|
-
t,
|
|
476
|
-
"%"
|
|
477
|
-
]
|
|
478
|
-
})]
|
|
479
|
-
}), /* @__PURE__ */ _("div", {
|
|
480
|
-
className: "mt-3 flex gap-1 h-4",
|
|
481
|
-
children: [/* @__PURE__ */ g("div", {
|
|
482
|
-
className: "h-4 rounded bg-accent-blue-subtle",
|
|
483
|
-
style: { width: `${(e.prev ?? 0) / H * 100}%` }
|
|
484
|
-
}), /* @__PURE__ */ g("div", {
|
|
485
|
-
className: "h-4 rounded bg-accent-blue",
|
|
486
|
-
style: { width: `${Math.max(0, (e.value - (e.prev ?? 0)) / H * 100)}%` }
|
|
487
|
-
})]
|
|
488
|
-
})]
|
|
489
|
-
}, e.name);
|
|
490
|
-
})
|
|
491
|
-
})]
|
|
492
|
-
})
|
|
493
|
-
}),
|
|
494
|
-
P === "bookings" && /* @__PURE__ */ _("div", {
|
|
495
|
-
className: "grid gap-6 lg:grid-cols-2",
|
|
496
|
-
children: [/* @__PURE__ */ _(x, {
|
|
497
|
-
className: "border-border-seam",
|
|
498
|
-
children: [/* @__PURE__ */ g(C, { children: /* @__PURE__ */ g(w, {
|
|
499
|
-
className: "text-base",
|
|
500
|
-
children: "Bookings by Service Category"
|
|
501
|
-
}) }), /* @__PURE__ */ g(S, {
|
|
502
|
-
className: "space-y-3",
|
|
503
|
-
children: [...j].sort((e, t) => t.value - e.value).map((e, t) => /* @__PURE__ */ _("div", {
|
|
504
|
-
className: "flex items-center gap-3",
|
|
505
|
-
children: [/* @__PURE__ */ _("span", {
|
|
506
|
-
className: "w-6 text-sm font-bold text-text-secondary tabular-nums",
|
|
507
|
-
children: ["#", t + 1]
|
|
508
|
-
}), /* @__PURE__ */ _("div", {
|
|
509
|
-
className: "flex-1",
|
|
510
|
-
children: [/* @__PURE__ */ _("div", {
|
|
511
|
-
className: "flex items-center justify-between text-sm",
|
|
512
|
-
children: [/* @__PURE__ */ g("span", {
|
|
513
|
-
className: "font-medium",
|
|
514
|
-
children: e.name
|
|
515
|
-
}), /* @__PURE__ */ _("span", {
|
|
516
|
-
className: "tabular-nums",
|
|
517
|
-
children: [e.value, " bookings"]
|
|
518
|
-
})]
|
|
519
|
-
}), /* @__PURE__ */ g("div", {
|
|
520
|
-
className: "mt-1 h-2 overflow-hidden rounded-full bg-bg-sunken",
|
|
521
|
-
children: /* @__PURE__ */ g("div", {
|
|
522
|
-
className: "h-2 rounded-full bg-accent-blue",
|
|
523
|
-
style: { width: `${e.value / U * 100}%` }
|
|
524
|
-
})
|
|
525
|
-
})]
|
|
526
|
-
})]
|
|
527
|
-
}, e.name))
|
|
528
|
-
})]
|
|
529
|
-
}), /* @__PURE__ */ _(x, {
|
|
530
|
-
className: "border-border-seam",
|
|
531
|
-
children: [/* @__PURE__ */ g(C, { children: /* @__PURE__ */ g(w, {
|
|
532
|
-
className: "text-base",
|
|
533
|
-
children: "Quick Stats"
|
|
534
|
-
}) }), /* @__PURE__ */ g(S, {
|
|
535
|
-
className: "space-y-4",
|
|
536
|
-
children: [
|
|
537
|
-
{
|
|
538
|
-
label: "Total Bookings",
|
|
539
|
-
value: j.reduce((e, t) => e + t.value, 0).toLocaleString(),
|
|
540
|
-
icon: d
|
|
541
|
-
},
|
|
542
|
-
{
|
|
543
|
-
label: "Avg. Booking Value",
|
|
544
|
-
value: "$3,804",
|
|
545
|
-
icon: o
|
|
546
|
-
},
|
|
547
|
-
{
|
|
548
|
-
label: "Completion Rate",
|
|
549
|
-
value: "96.5%",
|
|
550
|
-
icon: m
|
|
551
|
-
},
|
|
552
|
-
{
|
|
553
|
-
label: "Client Satisfaction",
|
|
554
|
-
value: "4.7/5.0",
|
|
555
|
-
icon: f
|
|
556
|
-
}
|
|
557
|
-
].map(({ label: e, value: t, icon: n }) => /* @__PURE__ */ _("div", {
|
|
558
|
-
className: "flex items-center justify-between rounded-lg border border-border-seam p-3",
|
|
559
|
-
children: [/* @__PURE__ */ _("div", {
|
|
560
|
-
className: "flex items-center gap-3",
|
|
561
|
-
children: [/* @__PURE__ */ g(n, { className: "h-5 w-5 text-text-secondary" }), /* @__PURE__ */ g("span", {
|
|
562
|
-
className: "text-sm font-medium",
|
|
563
|
-
children: e
|
|
564
|
-
})]
|
|
565
|
-
}), /* @__PURE__ */ g("span", {
|
|
566
|
-
className: "text-lg font-bold tabular-nums",
|
|
567
|
-
children: t
|
|
568
|
-
})]
|
|
569
|
-
}, e))
|
|
570
|
-
})]
|
|
571
|
-
})]
|
|
572
|
-
})
|
|
573
|
-
]
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
//#endregion
|
|
577
|
-
export { k as AdminReportsPage };
|
|
578
|
-
|
|
579
|
-
//# sourceMappingURL=AdminReportsPage.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"AdminReportsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AdminReportsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport {\n Badge,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n GlassCard,\n AuroraBackground,\n GradientText,\n CTAOverflowMenu,\n PagePurpose,\n NextSteps,\n} from '@burdenoff/fe-libs/ui';\nimport {\n TrendingUp,\n TrendingDown,\n Minus,\n Download,\n FileText,\n BarChart3,\n Calendar,\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')\n return <TrendingUp className=\"h-4 w-4 text-status-success-text\" />;\n if (type === 'negative')\n return <TrendingDown className=\"h-4 w-4 text-status-error-text\" />;\n return <Minus className=\"h-4 w-4 text-text-secondary\" />;\n };\n\n const changeColor = (type: string) =>\n type === 'positive'\n ? 'text-status-success-text'\n : type === 'negative'\n ? 'text-status-error-text'\n : 'text-text-secondary';\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 const roleColors: Record<string, string> = {\n client: 'bg-chart-1',\n provider: 'bg-chart-2',\n admin: 'bg-chart-3',\n team_member: 'bg-chart-4',\n };\n\n const statusColors: Record<string, string> = {\n active: 'bg-status-success-text',\n suspended: 'bg-status-warning-text',\n pending: 'bg-chart-5',\n banned: 'bg-status-error-text',\n };\n\n return (\n <div className=\"space-y-6 p-6 bg-bg-canvas min-h-screen\">\n {/* Hero header — the ONE aurora backdrop for this page. */}\n <div className=\"relative overflow-hidden rounded-card -mx-2 px-2 pt-1\">\n <AuroraBackground intensity=\"subtle\" />\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 tracking-tight\">\n <GradientText>Admin Reports</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Platform analytics, performance metrics, and operational reports.\n </p>\n </div>\n <CTAOverflowMenu\n primary={{\n label: 'Generate Report',\n onSelect: () => {},\n icon: <FileText className=\"h-4 w-4\" />,\n }}\n actions={[\n {\n label: 'Export CSV',\n onSelect: () => {},\n icon: <Download className=\"h-4 w-4\" />,\n },\n ]}\n />\n </div>\n </div>\n\n <PagePurpose icon={<BarChart3 className=\"h-4 w-4\" />}>\n The operational pulse of the platform. Track headline KPIs, dig into revenue, user, and\n booking trends across time ranges, and export reports for stakeholders — use it to spot\n what is growing, what needs attention, and where to act next.\n </PagePurpose>\n\n <NextSteps\n storageKey=\"proserviceworld-admin-reports\"\n steps={[\n {\n id: 'revenue',\n label: 'Review revenue trends',\n description: 'Open the Revenue tab to compare months and spot growth or dips.',\n icon: <DollarSign className=\"h-4 w-4\" />,\n onClick: () => setActiveTab('revenue'),\n },\n {\n id: 'users',\n label: 'Check user health',\n description: 'See role and status distribution plus your top spenders.',\n icon: <Users className=\"h-4 w-4\" />,\n onClick: () => setActiveTab('users'),\n },\n {\n id: 'export',\n label: 'Export a report',\n description: 'Generate a shareable report or CSV for stakeholders.',\n icon: <Download className=\"h-4 w-4\" />,\n onClick: () => {},\n },\n ]}\n />\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 border-border-seam\">\n {tabs.map(({ key, label, icon: Icon }) => (\n <button\n key={key}\n onClick={() => setActiveTab(key)}\n aria-pressed={activeTab === key}\n className={`flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${\n activeTab === key\n ? 'border-accent-blue text-accent-blue'\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 border-border-seam\">\n {(['7d', '30d', '90d', '1y'] as TimeRange[]).map((range) => (\n <button\n key={range}\n onClick={() => setTimeRange(range)}\n aria-pressed={timeRange === range}\n className={`px-3 py-1 text-xs font-medium tabular-nums focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${\n timeRange === range\n ? 'bg-accent-blue text-action-primary-text'\n : 'text-text-secondary hover:bg-bg-sunken'\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 <GlassCard\n key={i}\n treatment={i === 0 ? 'glass' : 'metric'}\n glow={i === 0}\n className=\"border-border-seam\"\n >\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 tabular-nums\">{metric.value}</p>\n <p className={`mt-1 flex items-center gap-1 text-xs ${changeColor(metric.changeType)}`}>\n <ArrowUpRight className=\"h-3 w-3\" />\n {metric.change} vs last period\n </p>\n </CardContent>\n </GlassCard>\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 className=\"border-border-seam\">\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-accent-blue rounded\"\n style={{ width: `${(point.value / maxRevenue) * 100}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-xs font-medium tabular-nums\">\n ${(point.value / 1000).toFixed(1)}k\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Service breakdown */}\n <Card className=\"border-border-seam\">\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-accent-purple rounded\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs font-medium tabular-nums\">\n {service.value}\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Geographic revenue */}\n <Card className=\"lg:col-span-2 border-border-seam\">\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 border-border-seam 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\n key={geo.country}\n className=\"border-b border-border-seam hover:bg-bg-sunken\"\n >\n <td className=\"p-3 font-medium\">{geo.country}</td>\n <td className=\"p-3 text-right tabular-nums\">{geo.clients}</td>\n <td className=\"p-3 text-right font-medium tabular-nums\">\n ${(geo.revenue / 1000).toFixed(0)}k\n </td>\n <td className=\"p-3 text-right text-text-secondary tabular-nums\">\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-accent-blue\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-10 text-right text-xs tabular-nums\">{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 className=\"border-border-seam\">\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 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-chart-6'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm tabular-nums\">\n {count} ({pct}%)\n </span>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n <Card className=\"border-border-seam\">\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 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-chart-6'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-16 text-right text-sm tabular-nums\">\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 border-border-seam\">\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 border-border-seam 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-accent-soft text-sm font-bold text-text-primary tabular-nums\">\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 tabular-nums\">\n ${user.totalSpent.toLocaleString()}\n </p>\n <p className=\"text-xs text-text-secondary tabular-nums\">\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 className=\"border-border-seam\">\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 border-border-seam 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 tabular-nums\">\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-accent-blue-subtle\"\n style={{ width: `${((point.prev ?? 0) / maxRevenue) * 100}%` }}\n />\n <div\n className=\"h-4 rounded bg-accent-blue\"\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 className=\"border-border-seam\">\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 tabular-nums\">\n #{idx + 1}\n </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 className=\"tabular-nums\">{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-accent-blue\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card className=\"border-border-seam\">\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 border-border-seam 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 tabular-nums\">{value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|