@burdenoff/microfe-proserviceworld 2026.625.1 → 2026.626.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/proserviceworld/pages/AdminReportsPage.js +102 -68
  2. package/dist/proserviceworld/pages/AdminReportsPage.js.map +1 -1
  3. package/dist/proserviceworld/pages/AdminUsersPage.js +37 -33
  4. package/dist/proserviceworld/pages/AdminUsersPage.js.map +1 -1
  5. package/dist/proserviceworld/pages/AnalyticsPage.js +66 -36
  6. package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -1
  7. package/dist/proserviceworld/pages/BookingsPage.js +52 -48
  8. package/dist/proserviceworld/pages/BookingsPage.js.map +1 -1
  9. package/dist/proserviceworld/pages/DashboardPage.js +275 -243
  10. package/dist/proserviceworld/pages/DashboardPage.js.map +1 -1
  11. package/dist/proserviceworld/pages/DocumentsPage.js +204 -200
  12. package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -1
  13. package/dist/proserviceworld/pages/MessagesPage.js +200 -186
  14. package/dist/proserviceworld/pages/MessagesPage.js.map +1 -1
  15. package/dist/proserviceworld/pages/PaymentsPage.js +70 -39
  16. package/dist/proserviceworld/pages/PaymentsPage.js.map +1 -1
  17. package/dist/proserviceworld/pages/ProjectsPage.js +510 -477
  18. package/dist/proserviceworld/pages/ProjectsPage.js.map +1 -1
  19. package/dist/proserviceworld/pages/ProviderDetailPage.js +71 -41
  20. package/dist/proserviceworld/pages/ProviderDetailPage.js.map +1 -1
  21. package/dist/proserviceworld/pages/ProvidersPage.js +62 -58
  22. package/dist/proserviceworld/pages/ProvidersPage.js.map +1 -1
  23. package/dist/proserviceworld/pages/ReviewsPage.js +80 -63
  24. package/dist/proserviceworld/pages/ReviewsPage.js.map +1 -1
  25. package/dist/proserviceworld/pages/ServiceDetailsPage.js +118 -83
  26. package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +1 -1
  27. package/dist/proserviceworld/pages/ServicesPage.js +134 -130
  28. package/dist/proserviceworld/pages/ServicesPage.js.map +1 -1
  29. package/dist/proserviceworld/pages/VerificationPage.js +23 -19
  30. package/dist/proserviceworld/pages/VerificationPage.js.map +1 -1
  31. package/package.json +2 -2
@@ -2,9 +2,9 @@ import { useProServiceWorldStore as e } from "../store/proserviceworldStore.js";
2
2
  import { useMemo as t, useState as n } from "react";
3
3
  import { Ban as r, CheckCircle2 as i, Clock as a, Download as o, Filter as s, Mail as c, MoreHorizontal as l, Plus as u, Search as d, Shield as f, Star as p, UserCheck as m, Users as h, XCircle as g } from "lucide-react";
4
4
  import { jsx as _, jsxs as v } from "react/jsx-runtime";
5
- import { AuroraBackground as y, Badge as b, Button as x, CTAOverflowMenu as S, Card as C, CardContent as w, GlassCard as T, GradientText as E, IllustratedEmptyState as D, Input as O } from "@burdenoff/fe-libs/ui";
5
+ import { AuroraBackground as y, Badge as b, Button as x, CTAOverflowMenu as S, Card as C, CardContent as w, GlassCard as T, GradientText as E, IllustratedEmptyState as D, Input as O, PagePurpose as k } from "@burdenoff/fe-libs/ui";
6
6
  //#region src/proserviceworld/pages/AdminUsersPage.tsx
7
- var k = {
7
+ var A = {
8
8
  client: {
9
9
  label: "Client",
10
10
  variant: "secondary"
@@ -21,7 +21,7 @@ var k = {
21
21
  label: "Team",
22
22
  variant: "outline"
23
23
  }
24
- }, A = {
24
+ }, j = {
25
25
  active: {
26
26
  label: "Active",
27
27
  icon: i,
@@ -43,22 +43,22 @@ var k = {
43
43
  color: "text-status-error-text"
44
44
  }
45
45
  };
46
- function j() {
47
- let { adminUsers: r } = e(), [a, j] = n(""), [M, N] = n("all"), [P, F] = n("all"), [I, L] = n(/* @__PURE__ */ new Set()), R = t(() => {
46
+ function M() {
47
+ let { adminUsers: r } = e(), [a, M] = n(""), [N, P] = n("all"), [F, I] = n("all"), [L, R] = n(/* @__PURE__ */ new Set()), z = t(() => {
48
48
  let e = [...r];
49
- if (M !== "all" && (e = e.filter((e) => e.role === M)), P !== "all" && (e = e.filter((e) => e.status === P)), a.trim()) {
49
+ if (N !== "all" && (e = e.filter((e) => e.role === N)), F !== "all" && (e = e.filter((e) => e.status === F)), a.trim()) {
50
50
  let t = a.toLowerCase();
51
51
  e = e.filter((e) => e.name.toLowerCase().includes(t) || e.email.toLowerCase().includes(t));
52
52
  }
53
53
  return e;
54
54
  }, [
55
55
  r,
56
- M,
57
- P,
56
+ N,
57
+ F,
58
58
  a
59
- ]), z = r.length, B = r.filter((e) => e.status === "active").length, V = r.filter((e) => e.role === "provider").length, H = r.filter((e) => e.verified).length, U = (e) => {
60
- let t = new Set(I);
61
- t.has(e) ? t.delete(e) : t.add(e), L(t);
59
+ ]), B = r.length, V = r.filter((e) => e.status === "active").length, H = r.filter((e) => e.role === "provider").length, U = r.filter((e) => e.verified).length, W = (e) => {
60
+ let t = new Set(L);
61
+ t.has(e) ? t.delete(e) : t.add(e), R(t);
62
62
  };
63
63
  return /* @__PURE__ */ v("div", {
64
64
  className: "space-y-6 p-6 bg-bg-canvas min-h-screen",
@@ -88,6 +88,10 @@ function j() {
88
88
  })
89
89
  ]
90
90
  }),
91
+ /* @__PURE__ */ _(k, {
92
+ icon: /* @__PURE__ */ _(h, { className: "h-4 w-4" }),
93
+ children: "The control room for everyone on the platform. Search, filter by role and status, and review each user's activity and spend at a glance — then invite new members or take bulk action to activate, suspend, or ban accounts as needed."
94
+ }),
91
95
  /* @__PURE__ */ v("div", {
92
96
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4",
93
97
  children: [
@@ -102,7 +106,7 @@ function j() {
102
106
  children: /* @__PURE__ */ _(h, { className: "h-5 w-5 text-accent-blue" })
103
107
  }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
104
108
  className: "text-2xl font-bold tabular-nums",
105
- children: z
109
+ children: B
106
110
  }), /* @__PURE__ */ _("p", {
107
111
  className: "text-xs text-text-secondary",
108
112
  children: "Total Users"
@@ -119,7 +123,7 @@ function j() {
119
123
  children: /* @__PURE__ */ _(m, { className: "h-5 w-5 text-status-success-text" })
120
124
  }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
121
125
  className: "text-2xl font-bold tabular-nums",
122
- children: B
126
+ children: V
123
127
  }), /* @__PURE__ */ _("p", {
124
128
  className: "text-xs text-text-secondary",
125
129
  children: "Active Users"
@@ -136,7 +140,7 @@ function j() {
136
140
  children: /* @__PURE__ */ _(f, { className: "h-5 w-5 text-accent-purple" })
137
141
  }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
138
142
  className: "text-2xl font-bold tabular-nums",
139
- children: V
143
+ children: H
140
144
  }), /* @__PURE__ */ _("p", {
141
145
  className: "text-xs text-text-secondary",
142
146
  children: "Providers"
@@ -153,7 +157,7 @@ function j() {
153
157
  children: /* @__PURE__ */ _(i, { className: "h-5 w-5 text-status-warning-text" })
154
158
  }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
155
159
  className: "text-2xl font-bold tabular-nums",
156
- children: H
160
+ children: U
157
161
  }), /* @__PURE__ */ _("p", {
158
162
  className: "text-xs text-text-secondary",
159
163
  children: "Verified"
@@ -169,7 +173,7 @@ function j() {
169
173
  children: [/* @__PURE__ */ _(d, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ _(O, {
170
174
  placeholder: "Search by name or email...",
171
175
  value: a,
172
- onChange: (e) => j(e.target.value),
176
+ onChange: (e) => M(e.target.value),
173
177
  className: "pl-10"
174
178
  })]
175
179
  }), /* @__PURE__ */ v("div", {
@@ -178,8 +182,8 @@ function j() {
178
182
  /* @__PURE__ */ _(s, { className: "h-4 w-4 text-text-secondary" }),
179
183
  /* @__PURE__ */ v("select", {
180
184
  className: "rounded border border-border-seam bg-bg-surface px-2 py-1 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
181
- value: M,
182
- onChange: (e) => N(e.target.value),
185
+ value: N,
186
+ onChange: (e) => P(e.target.value),
183
187
  children: [
184
188
  /* @__PURE__ */ _("option", {
185
189
  value: "all",
@@ -205,8 +209,8 @@ function j() {
205
209
  }),
206
210
  /* @__PURE__ */ v("select", {
207
211
  className: "rounded border border-border-seam bg-bg-surface px-2 py-1 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus",
208
- value: P,
209
- onChange: (e) => F(e.target.value),
212
+ value: F,
213
+ onChange: (e) => I(e.target.value),
210
214
  children: [
211
215
  /* @__PURE__ */ _("option", {
212
216
  value: "all",
@@ -233,12 +237,12 @@ function j() {
233
237
  ]
234
238
  })]
235
239
  }),
236
- I.size > 0 && /* @__PURE__ */ v("div", {
240
+ L.size > 0 && /* @__PURE__ */ v("div", {
237
241
  className: "flex items-center gap-3 rounded-lg bg-accent-soft border border-border-seam p-3",
238
242
  children: [
239
243
  /* @__PURE__ */ v("span", {
240
244
  className: "text-sm font-medium",
241
- children: [I.size, " user(s) selected"]
245
+ children: [L.size, " user(s) selected"]
242
246
  }),
243
247
  /* @__PURE__ */ _(x, {
244
248
  variant: "outline",
@@ -272,9 +276,9 @@ function j() {
272
276
  className: "p-3",
273
277
  children: /* @__PURE__ */ _("input", {
274
278
  type: "checkbox",
275
- checked: I.size === R.length && R.length > 0,
279
+ checked: L.size === z.length && z.length > 0,
276
280
  onChange: () => {
277
- I.size === R.length ? L(/* @__PURE__ */ new Set()) : L(new Set(R.map((e) => e.id)));
281
+ L.size === z.length ? R(/* @__PURE__ */ new Set()) : R(new Set(z.map((e) => e.id)));
278
282
  },
279
283
  className: "rounded"
280
284
  })
@@ -320,7 +324,7 @@ function j() {
320
324
  children: "Actions"
321
325
  })
322
326
  ]
323
- }) }), /* @__PURE__ */ _("tbody", { children: R.length === 0 ? /* @__PURE__ */ _("tr", { children: /* @__PURE__ */ _("td", {
327
+ }) }), /* @__PURE__ */ _("tbody", { children: z.length === 0 ? /* @__PURE__ */ _("tr", { children: /* @__PURE__ */ _("td", {
324
328
  colSpan: 11,
325
329
  className: "p-4",
326
330
  children: /* @__PURE__ */ _(D, {
@@ -328,8 +332,8 @@ function j() {
328
332
  title: "No users found",
329
333
  description: "No users match your current filters."
330
334
  })
331
- }) }) : R.map((e) => {
332
- let t = A[e.status], n = t.icon;
335
+ }) }) : z.map((e) => {
336
+ let t = j[e.status], n = t.icon;
333
337
  return /* @__PURE__ */ v("tr", {
334
338
  className: "border-b border-border-seam transition-colors hover:bg-bg-sunken/50",
335
339
  children: [
@@ -338,8 +342,8 @@ function j() {
338
342
  children: /* @__PURE__ */ _("input", {
339
343
  type: "checkbox",
340
344
  "aria-label": `Select ${e.name}`,
341
- checked: I.has(e.id),
342
- onChange: () => U(e.id),
345
+ checked: L.has(e.id),
346
+ onChange: () => W(e.id),
343
347
  className: "rounded"
344
348
  })
345
349
  }),
@@ -362,8 +366,8 @@ function j() {
362
366
  /* @__PURE__ */ _("td", {
363
367
  className: "p-3",
364
368
  children: /* @__PURE__ */ _(b, {
365
- variant: k[e.role].variant,
366
- children: k[e.role].label
369
+ variant: A[e.role].variant,
370
+ children: A[e.role].label
367
371
  })
368
372
  }),
369
373
  /* @__PURE__ */ _("td", {
@@ -434,7 +438,7 @@ function j() {
434
438
  className: "mt-4 flex items-center justify-between border-t border-border-seam pt-4 text-sm text-text-secondary",
435
439
  children: [/* @__PURE__ */ v("span", { children: [
436
440
  "Showing ",
437
- R.length,
441
+ z.length,
438
442
  " of ",
439
443
  r.length,
440
444
  " users"
@@ -459,6 +463,6 @@ function j() {
459
463
  });
460
464
  }
461
465
  //#endregion
462
- export { j as AdminUsersPage };
466
+ export { M as AdminUsersPage };
463
467
 
464
468
  //# sourceMappingURL=AdminUsersPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminUsersPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AdminUsersPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n Input,\n GlassCard,\n AuroraBackground,\n GradientText,\n CTAOverflowMenu,\n IllustratedEmptyState,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Search,\n Filter,\n Users,\n UserCheck,\n Shield,\n Star,\n CheckCircle2,\n XCircle,\n Clock,\n Ban,\n MoreHorizontal,\n Download,\n Plus,\n Mail,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport type { AdminUserRole, AdminUserStatus } from '../types';\n\nconst roleConfig: Record<\n AdminUserRole,\n { label: string; variant: 'default' | 'secondary' | 'outline' | 'destructive' }\n> = {\n client: { label: 'Client', variant: 'secondary' },\n provider: { label: 'Provider', variant: 'default' },\n admin: { label: 'Admin', variant: 'destructive' },\n team_member: { label: 'Team', variant: 'outline' },\n};\n\nconst statusConfig: Record<\n AdminUserStatus,\n { label: string; icon: typeof CheckCircle2; color: string }\n> = {\n active: { label: 'Active', icon: CheckCircle2, color: 'text-status-success-text' },\n suspended: { label: 'Suspended', icon: Ban, color: 'text-status-warning-text' },\n pending: { label: 'Pending', icon: Clock, color: 'text-status-warning-text' },\n banned: { label: 'Banned', icon: XCircle, color: 'text-status-error-text' },\n};\n\nexport function AdminUsersPage() {\n const { adminUsers } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [roleFilter, setRoleFilter] = useState<AdminUserRole | 'all'>('all');\n const [statusFilter, setStatusFilter] = useState<AdminUserStatus | 'all'>('all');\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n\n const filtered = useMemo(() => {\n let result = [...adminUsers];\n if (roleFilter !== 'all') result = result.filter((u) => u.role === roleFilter);\n if (statusFilter !== 'all') result = result.filter((u) => u.status === statusFilter);\n if (searchQuery.trim()) {\n const q = searchQuery.toLowerCase();\n result = result.filter(\n (u) => u.name.toLowerCase().includes(q) || u.email.toLowerCase().includes(q)\n );\n }\n return result;\n }, [adminUsers, roleFilter, statusFilter, searchQuery]);\n\n // Stats\n const totalUsers = adminUsers.length;\n const activeUsers = adminUsers.filter((u) => u.status === 'active').length;\n const providers = adminUsers.filter((u) => u.role === 'provider').length;\n const verifiedUsers = adminUsers.filter((u) => u.verified).length;\n\n const toggleSelect = (id: string) => {\n const next = new Set(selectedIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n setSelectedIds(next);\n };\n\n const toggleSelectAll = () => {\n if (selectedIds.size === filtered.length) setSelectedIds(new Set());\n else setSelectedIds(new Set(filtered.map((u) => u.id)));\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 MFE */}\n <div className=\"relative overflow-hidden rounded-card -mx-2 px-2 pt-1 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <AuroraBackground intensity=\"subtle\" />\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n <GradientText>User Management</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Admin panel to manage platform users, roles, and access.\n </p>\n </div>\n <CTAOverflowMenu\n primary={{\n label: 'Invite User',\n onSelect: () => {},\n icon: <Plus className=\"h-4 w-4\" />,\n }}\n actions={[\n {\n label: 'Export',\n onSelect: () => {},\n icon: <Download className=\"h-4 w-4\" />,\n },\n ]}\n />\n </div>\n\n {/* Stats */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-status-info-bg-subtle p-2\">\n <Users className=\"h-5 w-5 text-accent-blue\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold tabular-nums\">{totalUsers}</p>\n <p className=\"text-xs text-text-secondary\">Total Users</p>\n </div>\n </div>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-status-success-bg-subtle p-2\">\n <UserCheck className=\"h-5 w-5 text-status-success-text\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold tabular-nums\">{activeUsers}</p>\n <p className=\"text-xs text-text-secondary\">Active Users</p>\n </div>\n </div>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-accent-purple-subtle p-2\">\n <Shield className=\"h-5 w-5 text-accent-purple\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold tabular-nums\">{providers}</p>\n <p className=\"text-xs text-text-secondary\">Providers</p>\n </div>\n </div>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-status-warning-bg-subtle p-2\">\n <CheckCircle2 className=\"h-5 w-5 text-status-warning-text\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold tabular-nums\">{verifiedUsers}</p>\n <p className=\"text-xs text-text-secondary\">Verified</p>\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search by name or email...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-text-secondary\" />\n <select\n className=\"rounded border border-border-seam bg-bg-surface px-2 py-1 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n value={roleFilter}\n onChange={(e) => setRoleFilter(e.target.value as AdminUserRole | 'all')}\n >\n <option value=\"all\">All Roles</option>\n <option value=\"client\">Client</option>\n <option value=\"provider\">Provider</option>\n <option value=\"admin\">Admin</option>\n <option value=\"team_member\">Team Member</option>\n </select>\n <select\n className=\"rounded border border-border-seam bg-bg-surface px-2 py-1 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as AdminUserStatus | 'all')}\n >\n <option value=\"all\">All Status</option>\n <option value=\"active\">Active</option>\n <option value=\"suspended\">Suspended</option>\n <option value=\"pending\">Pending</option>\n <option value=\"banned\">Banned</option>\n </select>\n </div>\n </div>\n\n {/* Bulk actions */}\n {selectedIds.size > 0 && (\n <div className=\"flex items-center gap-3 rounded-lg bg-accent-soft border border-border-seam p-3\">\n <span className=\"text-sm font-medium\">{selectedIds.size} user(s) selected</span>\n <Button variant=\"outline\" size=\"sm\">\n Activate\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n Suspend\n </Button>\n <Button variant=\"destructive\" size=\"sm\">\n Ban\n </Button>\n </div>\n )}\n\n {/* User table */}\n <Card className=\"border-border-seam\">\n <CardContent className=\"pt-4\">\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\">\n <input\n type=\"checkbox\"\n checked={selectedIds.size === filtered.length && filtered.length > 0}\n onChange={toggleSelectAll}\n className=\"rounded\"\n />\n </th>\n <th className=\"p-3\">User</th>\n <th className=\"p-3\">Role</th>\n <th className=\"p-3\">Status</th>\n <th className=\"p-3\">Verified</th>\n <th className=\"p-3\">Joined</th>\n <th className=\"p-3\">Last Active</th>\n <th className=\"p-3 text-right\">Bookings</th>\n <th className=\"p-3 text-right\">Spent</th>\n <th className=\"p-3\">Rating</th>\n <th className=\"p-3 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {filtered.length === 0 ? (\n <tr>\n <td colSpan={11} className=\"p-4\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No users found\"\n description=\"No users match your current filters.\"\n />\n </td>\n </tr>\n ) : (\n filtered.map((user) => {\n const sCfg = statusConfig[user.status];\n const StatusIcon = sCfg.icon;\n return (\n <tr key={user.id} className=\"border-b border-border-seam transition-colors hover:bg-bg-sunken/50\">\n <td className=\"p-3\">\n <input\n type=\"checkbox\"\n aria-label={`Select ${user.name}`}\n checked={selectedIds.has(user.id)}\n onChange={() => toggleSelect(user.id)}\n className=\"rounded\"\n />\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-8 w-8 items-center justify-center rounded-full bg-accent-soft text-xs font-semibold text-text-primary\">\n {user.name.charAt(0)}\n </div>\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 </td>\n <td className=\"p-3\">\n <Badge variant={roleConfig[user.role].variant}>\n {roleConfig[user.role].label}\n </Badge>\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-1\">\n <StatusIcon className={`h-4 w-4 ${sCfg.color}`} />\n <span className={`text-xs font-medium ${sCfg.color}`}>\n {sCfg.label}\n </span>\n </div>\n </td>\n <td className=\"p-3\">\n {user.verified ? (\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text\" />\n ) : (\n <XCircle className=\"h-4 w-4 text-text-muted\" />\n )}\n </td>\n <td className=\"p-3 text-text-secondary\">{user.joinedDate}</td>\n <td className=\"p-3 text-text-secondary\">{user.lastActive}</td>\n <td className=\"p-3 text-right tabular-nums\">{user.bookingsCount}</td>\n <td className=\"p-3 text-right tabular-nums\">\n {user.totalSpent > 0 ? `$${user.totalSpent.toLocaleString()}` : '-'}\n </td>\n <td className=\"p-3\">\n {user.rating ? (\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n <span className=\"text-xs font-medium tabular-nums\">{user.rating}</span>\n </div>\n ) : (\n <span className=\"text-xs text-text-secondary\">-</span>\n )}\n </td>\n <td className=\"p-3 text-right\">\n <div className=\"flex justify-end gap-1\">\n <Button variant=\"outline\" size=\"sm\" aria-label={`Email ${user.name}`}>\n <Mail className=\"h-3 w-3\" />\n </Button>\n <Button variant=\"outline\" size=\"sm\" aria-label={`More actions for ${user.name}`}>\n <MoreHorizontal className=\"h-3 w-3\" />\n </Button>\n </div>\n </td>\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n\n {/* Pagination hint */}\n <div className=\"mt-4 flex items-center justify-between border-t border-border-seam pt-4 text-sm text-text-secondary\">\n <span>\n Showing {filtered.length} of {adminUsers.length} users\n </span>\n <div className=\"flex gap-1\">\n <Button variant=\"outline\" size=\"sm\" disabled>\n Previous\n </Button>\n <Button variant=\"outline\" size=\"sm\" disabled>\n Next\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminUsersPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AdminUsersPage.tsx"],"sourcesContent":["import { useState, useMemo } from 'react';\nimport {\n Badge,\n Button,\n Card,\n CardContent,\n Input,\n GlassCard,\n AuroraBackground,\n GradientText,\n CTAOverflowMenu,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport {\n Search,\n Filter,\n Users,\n UserCheck,\n Shield,\n Star,\n CheckCircle2,\n XCircle,\n Clock,\n Ban,\n MoreHorizontal,\n Download,\n Plus,\n Mail,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\nimport type { AdminUserRole, AdminUserStatus } from '../types';\n\nconst roleConfig: Record<\n AdminUserRole,\n { label: string; variant: 'default' | 'secondary' | 'outline' | 'destructive' }\n> = {\n client: { label: 'Client', variant: 'secondary' },\n provider: { label: 'Provider', variant: 'default' },\n admin: { label: 'Admin', variant: 'destructive' },\n team_member: { label: 'Team', variant: 'outline' },\n};\n\nconst statusConfig: Record<\n AdminUserStatus,\n { label: string; icon: typeof CheckCircle2; color: string }\n> = {\n active: { label: 'Active', icon: CheckCircle2, color: 'text-status-success-text' },\n suspended: { label: 'Suspended', icon: Ban, color: 'text-status-warning-text' },\n pending: { label: 'Pending', icon: Clock, color: 'text-status-warning-text' },\n banned: { label: 'Banned', icon: XCircle, color: 'text-status-error-text' },\n};\n\nexport function AdminUsersPage() {\n const { adminUsers } = useProServiceWorldStore();\n const [searchQuery, setSearchQuery] = useState('');\n const [roleFilter, setRoleFilter] = useState<AdminUserRole | 'all'>('all');\n const [statusFilter, setStatusFilter] = useState<AdminUserStatus | 'all'>('all');\n const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set());\n\n const filtered = useMemo(() => {\n let result = [...adminUsers];\n if (roleFilter !== 'all') result = result.filter((u) => u.role === roleFilter);\n if (statusFilter !== 'all') result = result.filter((u) => u.status === statusFilter);\n if (searchQuery.trim()) {\n const q = searchQuery.toLowerCase();\n result = result.filter(\n (u) => u.name.toLowerCase().includes(q) || u.email.toLowerCase().includes(q)\n );\n }\n return result;\n }, [adminUsers, roleFilter, statusFilter, searchQuery]);\n\n // Stats\n const totalUsers = adminUsers.length;\n const activeUsers = adminUsers.filter((u) => u.status === 'active').length;\n const providers = adminUsers.filter((u) => u.role === 'provider').length;\n const verifiedUsers = adminUsers.filter((u) => u.verified).length;\n\n const toggleSelect = (id: string) => {\n const next = new Set(selectedIds);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n setSelectedIds(next);\n };\n\n const toggleSelectAll = () => {\n if (selectedIds.size === filtered.length) setSelectedIds(new Set());\n else setSelectedIds(new Set(filtered.map((u) => u.id)));\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 MFE */}\n <div className=\"relative overflow-hidden rounded-card -mx-2 px-2 pt-1 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <AuroraBackground intensity=\"subtle\" />\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight\">\n <GradientText>User Management</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">\n Admin panel to manage platform users, roles, and access.\n </p>\n </div>\n <CTAOverflowMenu\n primary={{\n label: 'Invite User',\n onSelect: () => {},\n icon: <Plus className=\"h-4 w-4\" />,\n }}\n actions={[\n {\n label: 'Export',\n onSelect: () => {},\n icon: <Download className=\"h-4 w-4\" />,\n },\n ]}\n />\n </div>\n\n <PagePurpose icon={<Users className=\"h-4 w-4\" />}>\n The control room for everyone on the platform. Search, filter by role and status, and\n review each user&apos;s activity and spend at a glance — then invite new members or take\n bulk action to activate, suspend, or ban accounts as needed.\n </PagePurpose>\n\n {/* Stats */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-status-info-bg-subtle p-2\">\n <Users className=\"h-5 w-5 text-accent-blue\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold tabular-nums\">{totalUsers}</p>\n <p className=\"text-xs text-text-secondary\">Total Users</p>\n </div>\n </div>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-status-success-bg-subtle p-2\">\n <UserCheck className=\"h-5 w-5 text-status-success-text\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold tabular-nums\">{activeUsers}</p>\n <p className=\"text-xs text-text-secondary\">Active Users</p>\n </div>\n </div>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-accent-purple-subtle p-2\">\n <Shield className=\"h-5 w-5 text-accent-purple\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold tabular-nums\">{providers}</p>\n <p className=\"text-xs text-text-secondary\">Providers</p>\n </div>\n </div>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <div className=\"rounded-lg bg-status-warning-bg-subtle p-2\">\n <CheckCircle2 className=\"h-5 w-5 text-status-warning-text\" />\n </div>\n <div>\n <p className=\"text-2xl font-bold tabular-nums\">{verifiedUsers}</p>\n <p className=\"text-xs text-text-secondary\">Verified</p>\n </div>\n </div>\n </GlassCard>\n </div>\n\n {/* Filters */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center\">\n <div className=\"relative flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search by name or email...\"\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n className=\"pl-10\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-text-secondary\" />\n <select\n className=\"rounded border border-border-seam bg-bg-surface px-2 py-1 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n value={roleFilter}\n onChange={(e) => setRoleFilter(e.target.value as AdminUserRole | 'all')}\n >\n <option value=\"all\">All Roles</option>\n <option value=\"client\">Client</option>\n <option value=\"provider\">Provider</option>\n <option value=\"admin\">Admin</option>\n <option value=\"team_member\">Team Member</option>\n </select>\n <select\n className=\"rounded border border-border-seam bg-bg-surface px-2 py-1 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus\"\n value={statusFilter}\n onChange={(e) => setStatusFilter(e.target.value as AdminUserStatus | 'all')}\n >\n <option value=\"all\">All Status</option>\n <option value=\"active\">Active</option>\n <option value=\"suspended\">Suspended</option>\n <option value=\"pending\">Pending</option>\n <option value=\"banned\">Banned</option>\n </select>\n </div>\n </div>\n\n {/* Bulk actions */}\n {selectedIds.size > 0 && (\n <div className=\"flex items-center gap-3 rounded-lg bg-accent-soft border border-border-seam p-3\">\n <span className=\"text-sm font-medium\">{selectedIds.size} user(s) selected</span>\n <Button variant=\"outline\" size=\"sm\">\n Activate\n </Button>\n <Button variant=\"outline\" size=\"sm\">\n Suspend\n </Button>\n <Button variant=\"destructive\" size=\"sm\">\n Ban\n </Button>\n </div>\n )}\n\n {/* User table */}\n <Card className=\"border-border-seam\">\n <CardContent className=\"pt-4\">\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\">\n <input\n type=\"checkbox\"\n checked={selectedIds.size === filtered.length && filtered.length > 0}\n onChange={toggleSelectAll}\n className=\"rounded\"\n />\n </th>\n <th className=\"p-3\">User</th>\n <th className=\"p-3\">Role</th>\n <th className=\"p-3\">Status</th>\n <th className=\"p-3\">Verified</th>\n <th className=\"p-3\">Joined</th>\n <th className=\"p-3\">Last Active</th>\n <th className=\"p-3 text-right\">Bookings</th>\n <th className=\"p-3 text-right\">Spent</th>\n <th className=\"p-3\">Rating</th>\n <th className=\"p-3 text-right\">Actions</th>\n </tr>\n </thead>\n <tbody>\n {filtered.length === 0 ? (\n <tr>\n <td colSpan={11} className=\"p-4\">\n <IllustratedEmptyState\n illustration=\"empty-people\"\n title=\"No users found\"\n description=\"No users match your current filters.\"\n />\n </td>\n </tr>\n ) : (\n filtered.map((user) => {\n const sCfg = statusConfig[user.status];\n const StatusIcon = sCfg.icon;\n return (\n <tr key={user.id} className=\"border-b border-border-seam transition-colors hover:bg-bg-sunken/50\">\n <td className=\"p-3\">\n <input\n type=\"checkbox\"\n aria-label={`Select ${user.name}`}\n checked={selectedIds.has(user.id)}\n onChange={() => toggleSelect(user.id)}\n className=\"rounded\"\n />\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-8 w-8 items-center justify-center rounded-full bg-accent-soft text-xs font-semibold text-text-primary\">\n {user.name.charAt(0)}\n </div>\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 </td>\n <td className=\"p-3\">\n <Badge variant={roleConfig[user.role].variant}>\n {roleConfig[user.role].label}\n </Badge>\n </td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-1\">\n <StatusIcon className={`h-4 w-4 ${sCfg.color}`} />\n <span className={`text-xs font-medium ${sCfg.color}`}>\n {sCfg.label}\n </span>\n </div>\n </td>\n <td className=\"p-3\">\n {user.verified ? (\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text\" />\n ) : (\n <XCircle className=\"h-4 w-4 text-text-muted\" />\n )}\n </td>\n <td className=\"p-3 text-text-secondary\">{user.joinedDate}</td>\n <td className=\"p-3 text-text-secondary\">{user.lastActive}</td>\n <td className=\"p-3 text-right tabular-nums\">{user.bookingsCount}</td>\n <td className=\"p-3 text-right tabular-nums\">\n {user.totalSpent > 0 ? `$${user.totalSpent.toLocaleString()}` : '-'}\n </td>\n <td className=\"p-3\">\n {user.rating ? (\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 fill-status-warning-text text-status-warning-text\" />\n <span className=\"text-xs font-medium tabular-nums\">{user.rating}</span>\n </div>\n ) : (\n <span className=\"text-xs text-text-secondary\">-</span>\n )}\n </td>\n <td className=\"p-3 text-right\">\n <div className=\"flex justify-end gap-1\">\n <Button variant=\"outline\" size=\"sm\" aria-label={`Email ${user.name}`}>\n <Mail className=\"h-3 w-3\" />\n </Button>\n <Button variant=\"outline\" size=\"sm\" aria-label={`More actions for ${user.name}`}>\n <MoreHorizontal className=\"h-3 w-3\" />\n </Button>\n </div>\n </td>\n </tr>\n );\n })\n )}\n </tbody>\n </table>\n </div>\n\n {/* Pagination hint */}\n <div className=\"mt-4 flex items-center justify-between border-t border-border-seam pt-4 text-sm text-text-secondary\">\n <span>\n Showing {filtered.length} of {adminUsers.length} users\n </span>\n <div className=\"flex gap-1\">\n <Button variant=\"outline\" size=\"sm\" disabled>\n Previous\n </Button>\n <Button variant=\"outline\" size=\"sm\" disabled>\n Next\n </Button>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -2,10 +2,10 @@ import { useProServiceWorldStore as e } from "../store/proserviceworldStore.js";
2
2
  import { useState as t } from "react";
3
3
  import { Activity as n, ArrowDownRight as r, ArrowUpRight as i, BarChart3 as a, Calendar as o, DollarSign as s, Download as c, Globe as l, Minus as u, TrendingDown as d, TrendingUp as f, Users as p } from "lucide-react";
4
4
  import { jsx as m, jsxs as h } from "react/jsx-runtime";
5
- import { AuroraBackground as g, Badge as _, Button as v, Card as y, CardContent as b, CardHeader as x, CardTitle as S, EmphasisPanel as C, GlassCard as w, GradientText as T } from "@burdenoff/fe-libs/ui";
5
+ import { AuroraBackground as g, Badge as _, Button as v, Card as y, CardContent as b, CardHeader as x, CardTitle as S, EmphasisPanel as C, GlassCard as w, GradientText as T, NextSteps as E, PagePurpose as D } from "@burdenoff/fe-libs/ui";
6
6
  //#region src/proserviceworld/pages/AnalyticsPage.tsx
7
- function E() {
8
- let { analyticsMetrics: E, revenueData: D, clientData: O, servicePerformance: k, geographicData: A } = e(), [j, M] = t("overview"), [N, P] = t("6m"), F = [
7
+ function O() {
8
+ let { analyticsMetrics: O, revenueData: k, clientData: A, servicePerformance: j, geographicData: M } = e(), [N, P] = t("overview"), [F, I] = t("6m"), L = [
9
9
  {
10
10
  key: "overview",
11
11
  label: "Overview",
@@ -31,7 +31,7 @@ function E() {
31
31
  label: "Performance",
32
32
  icon: f
33
33
  }
34
- ], I = (e) => e === "positive" ? /* @__PURE__ */ m(f, { className: "h-4 w-4 text-status-success-text" }) : e === "negative" ? /* @__PURE__ */ m(d, { className: "h-4 w-4 text-status-error-text" }) : /* @__PURE__ */ m(u, { className: "h-4 w-4 text-text-muted" }), L = Math.max(...D.map((e) => e.value)), R = Math.max(...O.map((e) => e.value)), z = Math.max(...k.map((e) => e.value));
34
+ ], R = (e) => e === "positive" ? /* @__PURE__ */ m(f, { className: "h-4 w-4 text-status-success-text" }) : e === "negative" ? /* @__PURE__ */ m(d, { className: "h-4 w-4 text-status-error-text" }) : /* @__PURE__ */ m(u, { className: "h-4 w-4 text-text-muted" }), z = Math.max(...k.map((e) => e.value)), B = Math.max(...A.map((e) => e.value)), V = Math.max(...j.map((e) => e.value));
35
35
  return /* @__PURE__ */ h("div", {
36
36
  className: "space-y-6 p-6 bg-bg-canvas min-h-screen",
37
37
  children: [
@@ -55,9 +55,9 @@ function E() {
55
55
  "6m",
56
56
  "1y"
57
57
  ].map((e) => /* @__PURE__ */ m("button", {
58
- onClick: () => P(e),
59
- "aria-pressed": N === e,
60
- className: `px-3 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${N === e ? "bg-action-primary-bg text-action-primary-text" : "text-text-secondary hover:bg-bg-sunken/50"}`,
58
+ onClick: () => I(e),
59
+ "aria-pressed": F === e,
60
+ className: `px-3 py-1 text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus ${F === e ? "bg-action-primary-bg text-action-primary-text" : "text-text-secondary hover:bg-bg-sunken/50"}`,
61
61
  children: e.toUpperCase()
62
62
  }, e))
63
63
  }), /* @__PURE__ */ h(v, {
@@ -68,18 +68,48 @@ function E() {
68
68
  })]
69
69
  })]
70
70
  }),
71
+ /* @__PURE__ */ m(D, {
72
+ icon: /* @__PURE__ */ m(a, { className: "h-4 w-4" }),
73
+ children: "Understand how your professional services are performing. Track revenue and client growth over time, see which services and regions drive the most demand, and export the numbers — so you can double down on what works and plan where to grow next."
74
+ }),
75
+ /* @__PURE__ */ m(E, {
76
+ storageKey: "proserviceworld-analytics",
77
+ steps: [
78
+ {
79
+ id: "revenue",
80
+ label: "Inspect revenue performance",
81
+ description: "Open the Revenue tab to see month-over-month growth.",
82
+ icon: /* @__PURE__ */ m(s, { className: "h-4 w-4" }),
83
+ onClick: () => P("revenue")
84
+ },
85
+ {
86
+ id: "services",
87
+ label: "Find your top services",
88
+ description: "See which services attract the most bookings.",
89
+ icon: /* @__PURE__ */ m(n, { className: "h-4 w-4" }),
90
+ onClick: () => P("services")
91
+ },
92
+ {
93
+ id: "clients",
94
+ label: "Track client acquisition",
95
+ description: "Review client growth and geographic reach.",
96
+ icon: /* @__PURE__ */ m(p, { className: "h-4 w-4" }),
97
+ onClick: () => P("clients")
98
+ }
99
+ ]
100
+ }),
71
101
  /* @__PURE__ */ m("div", {
72
102
  className: "flex flex-wrap gap-1 border-b border-border-seam",
73
- children: F.map(({ key: e, label: t, icon: n }) => /* @__PURE__ */ h("button", {
74
- onClick: () => M(e),
75
- "aria-pressed": j === e,
76
- 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 ${j === e ? "border-action-primary-bg text-text-primary" : "border-transparent text-text-secondary hover:text-text-primary"}`,
103
+ children: L.map(({ key: e, label: t, icon: n }) => /* @__PURE__ */ h("button", {
104
+ onClick: () => P(e),
105
+ "aria-pressed": N === e,
106
+ 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 === e ? "border-action-primary-bg text-text-primary" : "border-transparent text-text-secondary hover:text-text-primary"}`,
77
107
  children: [/* @__PURE__ */ m(n, { className: "h-4 w-4" }), t]
78
108
  }, e))
79
109
  }),
80
110
  /* @__PURE__ */ m("div", {
81
111
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-5",
82
- children: E.map((e, t) => /* @__PURE__ */ h(w, {
112
+ children: O.map((e, t) => /* @__PURE__ */ h(w, {
83
113
  treatment: t === 0 ? "glass" : "metric",
84
114
  glow: t === 0,
85
115
  className: "border-border-seam",
@@ -89,7 +119,7 @@ function E() {
89
119
  children: [/* @__PURE__ */ m("p", {
90
120
  className: "text-xs font-medium text-text-secondary",
91
121
  children: e.label
92
- }), I(e.changeType)]
122
+ }), R(e.changeType)]
93
123
  }),
94
124
  /* @__PURE__ */ m("p", {
95
125
  className: "mt-1 text-2xl font-bold tabular-nums",
@@ -102,7 +132,7 @@ function E() {
102
132
  ]
103
133
  }, t))
104
134
  }),
105
- j === "overview" && /* @__PURE__ */ h("div", {
135
+ N === "overview" && /* @__PURE__ */ h("div", {
106
136
  className: "grid gap-6 lg:grid-cols-2",
107
137
  children: [/* @__PURE__ */ h(C, { children: [/* @__PURE__ */ h("div", {
108
138
  className: "flex items-center justify-between mb-4",
@@ -115,7 +145,7 @@ function E() {
115
145
  })]
116
146
  }), /* @__PURE__ */ m("div", {
117
147
  className: "space-y-3",
118
- children: D.map((e) => /* @__PURE__ */ h("div", {
148
+ children: k.map((e) => /* @__PURE__ */ h("div", {
119
149
  className: "flex items-center gap-3",
120
150
  children: [
121
151
  /* @__PURE__ */ m("span", {
@@ -126,10 +156,10 @@ function E() {
126
156
  className: "relative flex-1 h-6 bg-bg-sunken rounded overflow-hidden",
127
157
  children: [e.prev !== void 0 && /* @__PURE__ */ m("div", {
128
158
  className: "absolute h-6 bg-text-muted/15 rounded",
129
- style: { width: `${e.prev / L * 100}%` }
159
+ style: { width: `${e.prev / z * 100}%` }
130
160
  }), /* @__PURE__ */ m("div", {
131
161
  className: "relative h-6 bg-action-primary-bg rounded shadow-[var(--shadow-glow)]",
132
- style: { width: `${e.value / L * 100}%` }
162
+ style: { width: `${e.value / z * 100}%` }
133
163
  })]
134
164
  }),
135
165
  /* @__PURE__ */ h("span", {
@@ -155,7 +185,7 @@ function E() {
155
185
  })]
156
186
  }) }), /* @__PURE__ */ m(b, {
157
187
  className: "space-y-3",
158
- children: O.map((e) => /* @__PURE__ */ h("div", {
188
+ children: A.map((e) => /* @__PURE__ */ h("div", {
159
189
  className: "flex items-center gap-3",
160
190
  children: [
161
191
  /* @__PURE__ */ m("span", {
@@ -166,7 +196,7 @@ function E() {
166
196
  className: "flex-1 h-6 bg-bg-sunken rounded overflow-hidden",
167
197
  children: /* @__PURE__ */ m("div", {
168
198
  className: "h-6 bg-accent-blue rounded",
169
- style: { width: `${e.value / R * 100}%` }
199
+ style: { width: `${e.value / B * 100}%` }
170
200
  })
171
201
  }),
172
202
  /* @__PURE__ */ m("span", {
@@ -178,7 +208,7 @@ function E() {
178
208
  })]
179
209
  })]
180
210
  }),
181
- j === "revenue" && /* @__PURE__ */ m("div", {
211
+ N === "revenue" && /* @__PURE__ */ m("div", {
182
212
  className: "space-y-6",
183
213
  children: /* @__PURE__ */ h(y, {
184
214
  className: "border-border-seam",
@@ -187,7 +217,7 @@ function E() {
187
217
  children: "Revenue Breakdown"
188
218
  }) }), /* @__PURE__ */ m(b, { children: /* @__PURE__ */ m("div", {
189
219
  className: "space-y-4",
190
- children: D.map((e) => {
220
+ children: k.map((e) => {
191
221
  let t = e.prev ? ((e.value - e.prev) / e.prev * 100).toFixed(1) : "0", n = e.prev ? e.value >= e.prev : !0;
192
222
  return /* @__PURE__ */ h("div", {
193
223
  className: "flex items-center justify-between rounded-lg border border-border-seam p-4 transition-shadow hover:shadow-pop",
@@ -230,7 +260,7 @@ function E() {
230
260
  }) })]
231
261
  })
232
262
  }),
233
- j === "clients" && /* @__PURE__ */ h("div", {
263
+ N === "clients" && /* @__PURE__ */ h("div", {
234
264
  className: "space-y-6",
235
265
  children: [/* @__PURE__ */ h(y, {
236
266
  className: "border-border-seam",
@@ -239,8 +269,8 @@ function E() {
239
269
  children: "Client Acquisition"
240
270
  }) }), /* @__PURE__ */ m(b, {
241
271
  className: "space-y-3",
242
- children: O.map((e, t) => {
243
- let n = t > 0 ? O[t - 1]?.value ?? 0 : 0, r = n > 0 ? ((e.value - n) / n * 100).toFixed(0) : "N/A";
272
+ children: A.map((e, t) => {
273
+ let n = t > 0 ? A[t - 1]?.value ?? 0 : 0, r = n > 0 ? ((e.value - n) / n * 100).toFixed(0) : "N/A";
244
274
  return /* @__PURE__ */ h("div", {
245
275
  className: "flex items-center gap-4 rounded-lg border border-border-seam p-3",
246
276
  children: [/* @__PURE__ */ h("div", {
@@ -258,7 +288,7 @@ function E() {
258
288
  className: "mt-2 h-2 overflow-hidden rounded-full bg-bg-sunken",
259
289
  children: /* @__PURE__ */ m("div", {
260
290
  className: "h-2 rounded-full bg-accent-blue",
261
- style: { width: `${e.value / R * 100}%` }
291
+ style: { width: `${e.value / B * 100}%` }
262
292
  })
263
293
  })]
264
294
  }), /* @__PURE__ */ m(_, {
@@ -301,8 +331,8 @@ function E() {
301
331
  children: "Distribution"
302
332
  })
303
333
  ]
304
- }) }), /* @__PURE__ */ m("tbody", { children: A.map((e) => {
305
- let t = A.reduce((e, t) => e + t.clients, 0), n = Math.round(e.clients / t * 100);
334
+ }) }), /* @__PURE__ */ m("tbody", { children: M.map((e) => {
335
+ let t = M.reduce((e, t) => e + t.clients, 0), n = Math.round(e.clients / t * 100);
306
336
  return /* @__PURE__ */ h("tr", {
307
337
  className: "border-b border-border-seam transition-colors hover:bg-bg-sunken/50",
308
338
  children: [
@@ -345,14 +375,14 @@ function E() {
345
375
  }) })]
346
376
  })]
347
377
  }),
348
- j === "services" && /* @__PURE__ */ h(y, {
378
+ N === "services" && /* @__PURE__ */ h(y, {
349
379
  className: "border-border-seam",
350
380
  children: [/* @__PURE__ */ m(x, { children: /* @__PURE__ */ m(S, {
351
381
  className: "text-base",
352
382
  children: "Service Performance (Total Bookings)"
353
383
  }) }), /* @__PURE__ */ m(b, {
354
384
  className: "space-y-3",
355
- children: [...k].sort((e, t) => t.value - e.value).map((e, t) => /* @__PURE__ */ h("div", {
385
+ children: [...j].sort((e, t) => t.value - e.value).map((e, t) => /* @__PURE__ */ h("div", {
356
386
  className: "flex items-center gap-4",
357
387
  children: [/* @__PURE__ */ h("span", {
358
388
  className: "w-6 text-sm font-medium text-text-secondary tabular-nums",
@@ -372,14 +402,14 @@ function E() {
372
402
  className: "mt-1 h-3 overflow-hidden rounded-full bg-bg-sunken",
373
403
  children: /* @__PURE__ */ m("div", {
374
404
  className: "h-3 rounded-full bg-action-primary-bg transition-all",
375
- style: { width: `${e.value / z * 100}%` }
405
+ style: { width: `${e.value / V * 100}%` }
376
406
  })
377
407
  })]
378
408
  })]
379
409
  }, e.name))
380
410
  })]
381
411
  }),
382
- j === "performance" && /* @__PURE__ */ h("div", {
412
+ N === "performance" && /* @__PURE__ */ h("div", {
383
413
  className: "grid gap-6 lg:grid-cols-2",
384
414
  children: [/* @__PURE__ */ h(y, {
385
415
  className: "border-border-seam",
@@ -388,7 +418,7 @@ function E() {
388
418
  children: "Monthly Revenue vs Previous"
389
419
  }) }), /* @__PURE__ */ h(b, {
390
420
  className: "space-y-3",
391
- children: [D.map((e) => /* @__PURE__ */ h("div", {
421
+ children: [k.map((e) => /* @__PURE__ */ h("div", {
392
422
  className: "space-y-1",
393
423
  children: [/* @__PURE__ */ h("div", {
394
424
  className: "flex items-center justify-between text-xs",
@@ -409,10 +439,10 @@ function E() {
409
439
  className: "flex gap-1",
410
440
  children: [/* @__PURE__ */ m("div", {
411
441
  className: "h-4 rounded bg-action-primary-bg/30",
412
- style: { width: `${(e.prev ?? 0) / L * 100}%` }
442
+ style: { width: `${(e.prev ?? 0) / z * 100}%` }
413
443
  }), /* @__PURE__ */ m("div", {
414
444
  className: "h-4 rounded bg-action-primary-bg",
415
- style: { width: `${e.value / L * 100 - (e.prev ?? 0) / L * 100}%` }
445
+ style: { width: `${e.value / z * 100 - (e.prev ?? 0) / z * 100}%` }
416
446
  })]
417
447
  })]
418
448
  }, e.name)), /* @__PURE__ */ h("div", {
@@ -433,7 +463,7 @@ function E() {
433
463
  children: "Top Performing Services"
434
464
  }) }), /* @__PURE__ */ m(b, { children: /* @__PURE__ */ m("div", {
435
465
  className: "space-y-3",
436
- children: [...k].sort((e, t) => t.value - e.value).slice(0, 5).map((e, t) => /* @__PURE__ */ h("div", {
466
+ children: [...j].sort((e, t) => t.value - e.value).slice(0, 5).map((e, t) => /* @__PURE__ */ h("div", {
437
467
  className: "flex items-center justify-between rounded-lg border border-border-seam p-3",
438
468
  children: [/* @__PURE__ */ h("div", {
439
469
  className: "flex items-center gap-3",
@@ -456,6 +486,6 @@ function E() {
456
486
  });
457
487
  }
458
488
  //#endregion
459
- export { E as AnalyticsPage };
489
+ export { O as AnalyticsPage };
460
490
 
461
491
  //# sourceMappingURL=AnalyticsPage.js.map