@burdenoff/microfe-proserviceworld 2026.509.101
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/dev/main.d.ts +2 -0
- package/dist/dev/main.d.ts.map +1 -0
- package/dist/generated/global-operations.d.ts +2 -0
- package/dist/generated/global-operations.d.ts.map +1 -0
- package/dist/generated/global-types.d.ts +18199 -0
- package/dist/generated/global-types.d.ts.map +1 -0
- package/dist/generated/wspace-operations.d.ts +2 -0
- package/dist/generated/wspace-operations.d.ts.map +1 -0
- package/dist/generated/wspace-types.d.ts +43046 -0
- package/dist/generated/wspace-types.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/proserviceworld/ProServiceWorldRoot.d.ts +5 -0
- package/dist/proserviceworld/ProServiceWorldRoot.d.ts.map +1 -0
- package/dist/proserviceworld/ProServiceWorldRoot.js +151 -0
- package/dist/proserviceworld/ProServiceWorldRoot.js.map +1 -0
- package/dist/proserviceworld/ProServiceWorldRoutes.d.ts +2 -0
- package/dist/proserviceworld/ProServiceWorldRoutes.d.ts.map +1 -0
- package/dist/proserviceworld/ProServiceWorldRoutes.js +91 -0
- package/dist/proserviceworld/ProServiceWorldRoutes.js.map +1 -0
- package/dist/proserviceworld/index.d.ts +5 -0
- package/dist/proserviceworld/index.d.ts.map +1 -0
- package/dist/proserviceworld/index.js +3 -0
- package/dist/proserviceworld/pages/AdminReportsPage.d.ts +2 -0
- package/dist/proserviceworld/pages/AdminReportsPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/AdminReportsPage.js +512 -0
- package/dist/proserviceworld/pages/AdminReportsPage.js.map +1 -0
- package/dist/proserviceworld/pages/AdminUsersPage.d.ts +2 -0
- package/dist/proserviceworld/pages/AdminUsersPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/AdminUsersPage.js +444 -0
- package/dist/proserviceworld/pages/AdminUsersPage.js.map +1 -0
- package/dist/proserviceworld/pages/AnalyticsPage.d.ts +2 -0
- package/dist/proserviceworld/pages/AnalyticsPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/AnalyticsPage.js +433 -0
- package/dist/proserviceworld/pages/AnalyticsPage.js.map +1 -0
- package/dist/proserviceworld/pages/BookingsPage.d.ts +2 -0
- package/dist/proserviceworld/pages/BookingsPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/BookingsPage.js +425 -0
- package/dist/proserviceworld/pages/BookingsPage.js.map +1 -0
- package/dist/proserviceworld/pages/DashboardPage.d.ts +2 -0
- package/dist/proserviceworld/pages/DashboardPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/DashboardPage.js +583 -0
- package/dist/proserviceworld/pages/DashboardPage.js.map +1 -0
- package/dist/proserviceworld/pages/DocumentsPage.d.ts +2 -0
- package/dist/proserviceworld/pages/DocumentsPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/DocumentsPage.js +494 -0
- package/dist/proserviceworld/pages/DocumentsPage.js.map +1 -0
- package/dist/proserviceworld/pages/MessagesPage.d.ts +2 -0
- package/dist/proserviceworld/pages/MessagesPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/MessagesPage.js +229 -0
- package/dist/proserviceworld/pages/MessagesPage.js.map +1 -0
- package/dist/proserviceworld/pages/PaymentsPage.d.ts +2 -0
- package/dist/proserviceworld/pages/PaymentsPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/PaymentsPage.js +479 -0
- package/dist/proserviceworld/pages/PaymentsPage.js.map +1 -0
- package/dist/proserviceworld/pages/ProjectsPage.d.ts +2 -0
- package/dist/proserviceworld/pages/ProjectsPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/ProjectsPage.js +547 -0
- package/dist/proserviceworld/pages/ProjectsPage.js.map +1 -0
- package/dist/proserviceworld/pages/ProviderDetailPage.d.ts +2 -0
- package/dist/proserviceworld/pages/ProviderDetailPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/ProviderDetailPage.js +464 -0
- package/dist/proserviceworld/pages/ProviderDetailPage.js.map +1 -0
- package/dist/proserviceworld/pages/ProvidersPage.d.ts +2 -0
- package/dist/proserviceworld/pages/ProvidersPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/ProvidersPage.js +342 -0
- package/dist/proserviceworld/pages/ProvidersPage.js.map +1 -0
- package/dist/proserviceworld/pages/ReviewsPage.d.ts +2 -0
- package/dist/proserviceworld/pages/ReviewsPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/ReviewsPage.js +339 -0
- package/dist/proserviceworld/pages/ReviewsPage.js.map +1 -0
- package/dist/proserviceworld/pages/ServiceDetailsPage.d.ts +2 -0
- package/dist/proserviceworld/pages/ServiceDetailsPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/ServiceDetailsPage.js +563 -0
- package/dist/proserviceworld/pages/ServiceDetailsPage.js.map +1 -0
- package/dist/proserviceworld/pages/ServicesPage.d.ts +2 -0
- package/dist/proserviceworld/pages/ServicesPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/ServicesPage.js +337 -0
- package/dist/proserviceworld/pages/ServicesPage.js.map +1 -0
- package/dist/proserviceworld/pages/VerificationPage.d.ts +2 -0
- package/dist/proserviceworld/pages/VerificationPage.d.ts.map +1 -0
- package/dist/proserviceworld/pages/VerificationPage.js +357 -0
- package/dist/proserviceworld/pages/VerificationPage.js.map +1 -0
- package/dist/proserviceworld/providers/ProServiceWorldProvider.d.ts +6 -0
- package/dist/proserviceworld/providers/ProServiceWorldProvider.d.ts.map +1 -0
- package/dist/proserviceworld/providers/ProServiceWorldProvider.js +27 -0
- package/dist/proserviceworld/providers/ProServiceWorldProvider.js.map +1 -0
- package/dist/proserviceworld/store/proserviceworldStore.d.ts +32 -0
- package/dist/proserviceworld/store/proserviceworldStore.d.ts.map +1 -0
- package/dist/proserviceworld/store/proserviceworldStore.js +2490 -0
- package/dist/proserviceworld/store/proserviceworldStore.js.map +1 -0
- package/dist/proserviceworld/types/index.d.ts +338 -0
- package/dist/proserviceworld/types/index.d.ts.map +1 -0
- package/dist/proserviceworld/utils/cn.d.ts +4 -0
- package/dist/proserviceworld/utils/cn.d.ts.map +1 -0
- package/dist/proserviceworld/utils/i18n.d.ts +6 -0
- package/dist/proserviceworld/utils/i18n.d.ts.map +1 -0
- package/dist/proserviceworld/utils/i18n.js +15 -0
- package/dist/proserviceworld/utils/i18n.js.map +1 -0
- package/dist/proserviceworld/utils/navigation.d.ts +2 -0
- package/dist/proserviceworld/utils/navigation.d.ts.map +1 -0
- package/dist/proserviceworld/utils/navigation.js +8 -0
- package/dist/proserviceworld/utils/navigation.js.map +1 -0
- package/package.json +107 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import { useProServiceWorldStore as e } from "../store/proserviceworldStore.js";
|
|
2
|
+
import { useState as t } from "react";
|
|
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
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
5
|
+
import { Badge as g, Button as _, Card as v, CardContent as y, CardHeader as b, CardTitle as x } from "@burdenoff/fe-libs/ui";
|
|
6
|
+
//#region src/proserviceworld/pages/AnalyticsPage.tsx
|
|
7
|
+
function S() {
|
|
8
|
+
let { analyticsMetrics: S, revenueData: C, clientData: w, servicePerformance: T, geographicData: E } = e(), [D, O] = t("overview"), [k, A] = t("6m"), j = [
|
|
9
|
+
{
|
|
10
|
+
key: "overview",
|
|
11
|
+
label: "Overview",
|
|
12
|
+
icon: a
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
key: "revenue",
|
|
16
|
+
label: "Revenue",
|
|
17
|
+
icon: s
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
key: "clients",
|
|
21
|
+
label: "Clients",
|
|
22
|
+
icon: p
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
key: "services",
|
|
26
|
+
label: "Services",
|
|
27
|
+
icon: n
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
key: "performance",
|
|
31
|
+
label: "Performance",
|
|
32
|
+
icon: f
|
|
33
|
+
}
|
|
34
|
+
], M = (e) => e === "positive" ? /* @__PURE__ */ m(f, { className: "h-4 w-4 text-green-500" }) : e === "negative" ? /* @__PURE__ */ m(d, { className: "h-4 w-4 text-red-500" }) : /* @__PURE__ */ m(u, { className: "h-4 w-4 text-gray-400" }), N = Math.max(...C.map((e) => e.value)), P = Math.max(...w.map((e) => e.value)), F = Math.max(...T.map((e) => e.value));
|
|
35
|
+
return /* @__PURE__ */ h("div", {
|
|
36
|
+
className: "space-y-6 p-6",
|
|
37
|
+
children: [
|
|
38
|
+
/* @__PURE__ */ h("div", {
|
|
39
|
+
className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between",
|
|
40
|
+
children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h1", {
|
|
41
|
+
className: "text-2xl font-semibold",
|
|
42
|
+
children: "Analytics"
|
|
43
|
+
}), /* @__PURE__ */ m("p", {
|
|
44
|
+
className: "text-sm text-muted-foreground",
|
|
45
|
+
children: "Insights and performance metrics for your professional services."
|
|
46
|
+
})] }), /* @__PURE__ */ h("div", {
|
|
47
|
+
className: "flex items-center gap-2",
|
|
48
|
+
children: [/* @__PURE__ */ m("div", {
|
|
49
|
+
className: "flex rounded border",
|
|
50
|
+
children: [
|
|
51
|
+
"1m",
|
|
52
|
+
"3m",
|
|
53
|
+
"6m",
|
|
54
|
+
"1y"
|
|
55
|
+
].map((e) => /* @__PURE__ */ m("button", {
|
|
56
|
+
onClick: () => A(e),
|
|
57
|
+
className: `px-3 py-1 text-xs font-medium ${k === e ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted/50"}`,
|
|
58
|
+
children: e.toUpperCase()
|
|
59
|
+
}, e))
|
|
60
|
+
}), /* @__PURE__ */ h(_, {
|
|
61
|
+
variant: "outline",
|
|
62
|
+
size: "sm",
|
|
63
|
+
children: [/* @__PURE__ */ m(c, { className: "mr-2 h-4 w-4" }), " Export"]
|
|
64
|
+
})]
|
|
65
|
+
})]
|
|
66
|
+
}),
|
|
67
|
+
/* @__PURE__ */ m("div", {
|
|
68
|
+
className: "flex gap-1 border-b",
|
|
69
|
+
children: j.map(({ key: e, label: t, icon: n }) => /* @__PURE__ */ h("button", {
|
|
70
|
+
onClick: () => O(e),
|
|
71
|
+
className: `flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${D === e ? "border-primary text-primary" : "border-transparent text-muted-foreground hover:text-foreground"}`,
|
|
72
|
+
children: [/* @__PURE__ */ m(n, { className: "h-4 w-4" }), t]
|
|
73
|
+
}, e))
|
|
74
|
+
}),
|
|
75
|
+
/* @__PURE__ */ m("div", {
|
|
76
|
+
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-5",
|
|
77
|
+
children: S.map((e, t) => /* @__PURE__ */ m(v, { children: /* @__PURE__ */ h(y, {
|
|
78
|
+
className: "pt-4",
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ h("div", {
|
|
81
|
+
className: "flex items-center justify-between",
|
|
82
|
+
children: [/* @__PURE__ */ m("p", {
|
|
83
|
+
className: "text-xs font-medium text-muted-foreground",
|
|
84
|
+
children: e.label
|
|
85
|
+
}), M(e.changeType)]
|
|
86
|
+
}),
|
|
87
|
+
/* @__PURE__ */ m("p", {
|
|
88
|
+
className: "mt-1 text-2xl font-bold",
|
|
89
|
+
children: e.value
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ h("p", {
|
|
92
|
+
className: `mt-0.5 text-xs ${e.changeType === "positive" ? "text-green-600" : e.changeType === "negative" ? "text-red-600" : "text-gray-500"}`,
|
|
93
|
+
children: [e.change, " from last period"]
|
|
94
|
+
})
|
|
95
|
+
]
|
|
96
|
+
}) }, t))
|
|
97
|
+
}),
|
|
98
|
+
D === "overview" && /* @__PURE__ */ h("div", {
|
|
99
|
+
className: "grid gap-6 lg:grid-cols-2",
|
|
100
|
+
children: [/* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ h("div", {
|
|
101
|
+
className: "flex items-center justify-between",
|
|
102
|
+
children: [/* @__PURE__ */ m(x, {
|
|
103
|
+
className: "text-base",
|
|
104
|
+
children: "Revenue Trend"
|
|
105
|
+
}), /* @__PURE__ */ m(g, {
|
|
106
|
+
variant: "secondary",
|
|
107
|
+
children: "Last 6 months"
|
|
108
|
+
})]
|
|
109
|
+
}) }), /* @__PURE__ */ m(y, {
|
|
110
|
+
className: "space-y-3",
|
|
111
|
+
children: C.map((e) => /* @__PURE__ */ h("div", {
|
|
112
|
+
className: "flex items-center gap-3",
|
|
113
|
+
children: [
|
|
114
|
+
/* @__PURE__ */ m("span", {
|
|
115
|
+
className: "w-10 text-xs text-muted-foreground",
|
|
116
|
+
children: e.name
|
|
117
|
+
}),
|
|
118
|
+
/* @__PURE__ */ h("div", {
|
|
119
|
+
className: "relative flex-1 h-6 bg-muted rounded overflow-hidden",
|
|
120
|
+
children: [e.prev !== void 0 && /* @__PURE__ */ m("div", {
|
|
121
|
+
className: "absolute h-6 bg-muted-foreground/10 rounded",
|
|
122
|
+
style: { width: `${e.prev / N * 100}%` }
|
|
123
|
+
}), /* @__PURE__ */ m("div", {
|
|
124
|
+
className: "relative h-6 bg-primary rounded",
|
|
125
|
+
style: { width: `${e.value / N * 100}%` }
|
|
126
|
+
})]
|
|
127
|
+
}),
|
|
128
|
+
/* @__PURE__ */ h("span", {
|
|
129
|
+
className: "w-20 text-right text-xs font-medium",
|
|
130
|
+
children: [
|
|
131
|
+
"$",
|
|
132
|
+
(e.value / 1e3).toFixed(1),
|
|
133
|
+
"k"
|
|
134
|
+
]
|
|
135
|
+
})
|
|
136
|
+
]
|
|
137
|
+
}, e.name))
|
|
138
|
+
})] }), /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ h("div", {
|
|
139
|
+
className: "flex items-center justify-between",
|
|
140
|
+
children: [/* @__PURE__ */ m(x, {
|
|
141
|
+
className: "text-base",
|
|
142
|
+
children: "Client Growth"
|
|
143
|
+
}), /* @__PURE__ */ m(g, {
|
|
144
|
+
variant: "secondary",
|
|
145
|
+
children: "Monthly"
|
|
146
|
+
})]
|
|
147
|
+
}) }), /* @__PURE__ */ m(y, {
|
|
148
|
+
className: "space-y-3",
|
|
149
|
+
children: w.map((e) => /* @__PURE__ */ h("div", {
|
|
150
|
+
className: "flex items-center gap-3",
|
|
151
|
+
children: [
|
|
152
|
+
/* @__PURE__ */ m("span", {
|
|
153
|
+
className: "w-10 text-xs text-muted-foreground",
|
|
154
|
+
children: e.name
|
|
155
|
+
}),
|
|
156
|
+
/* @__PURE__ */ m("div", {
|
|
157
|
+
className: "flex-1 h-6 bg-muted rounded overflow-hidden",
|
|
158
|
+
children: /* @__PURE__ */ m("div", {
|
|
159
|
+
className: "h-6 bg-blue-500 rounded",
|
|
160
|
+
style: { width: `${e.value / P * 100}%` }
|
|
161
|
+
})
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ m("span", {
|
|
164
|
+
className: "w-12 text-right text-xs font-medium",
|
|
165
|
+
children: e.value
|
|
166
|
+
})
|
|
167
|
+
]
|
|
168
|
+
}, e.name))
|
|
169
|
+
})] })]
|
|
170
|
+
}),
|
|
171
|
+
D === "revenue" && /* @__PURE__ */ m("div", {
|
|
172
|
+
className: "space-y-6",
|
|
173
|
+
children: /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ m(x, {
|
|
174
|
+
className: "text-base",
|
|
175
|
+
children: "Revenue Breakdown"
|
|
176
|
+
}) }), /* @__PURE__ */ m(y, { children: /* @__PURE__ */ m("div", {
|
|
177
|
+
className: "space-y-4",
|
|
178
|
+
children: C.map((e) => {
|
|
179
|
+
let t = e.prev ? ((e.value - e.prev) / e.prev * 100).toFixed(1) : "0", n = e.prev ? e.value >= e.prev : !0;
|
|
180
|
+
return /* @__PURE__ */ h("div", {
|
|
181
|
+
className: "flex items-center justify-between rounded-lg border p-4",
|
|
182
|
+
children: [/* @__PURE__ */ h("div", {
|
|
183
|
+
className: "flex items-center gap-4",
|
|
184
|
+
children: [/* @__PURE__ */ m("div", {
|
|
185
|
+
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10",
|
|
186
|
+
children: /* @__PURE__ */ m(o, { className: "h-5 w-5 text-primary" })
|
|
187
|
+
}), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ h("p", {
|
|
188
|
+
className: "font-medium",
|
|
189
|
+
children: [e.name, " 2025"]
|
|
190
|
+
}), /* @__PURE__ */ h("p", {
|
|
191
|
+
className: "text-sm text-muted-foreground",
|
|
192
|
+
children: [
|
|
193
|
+
"Previous: $",
|
|
194
|
+
((e.prev ?? 0) / 1e3).toFixed(1),
|
|
195
|
+
"k"
|
|
196
|
+
]
|
|
197
|
+
})] })]
|
|
198
|
+
}), /* @__PURE__ */ h("div", {
|
|
199
|
+
className: "text-right",
|
|
200
|
+
children: [/* @__PURE__ */ h("p", {
|
|
201
|
+
className: "text-lg font-bold",
|
|
202
|
+
children: [
|
|
203
|
+
"$",
|
|
204
|
+
(e.value / 1e3).toFixed(1),
|
|
205
|
+
"k"
|
|
206
|
+
]
|
|
207
|
+
}), /* @__PURE__ */ h("p", {
|
|
208
|
+
className: `flex items-center gap-1 text-xs ${n ? "text-green-600" : "text-red-600"}`,
|
|
209
|
+
children: [
|
|
210
|
+
m(n ? i : r, { className: "h-3 w-3" }),
|
|
211
|
+
t,
|
|
212
|
+
"%"
|
|
213
|
+
]
|
|
214
|
+
})]
|
|
215
|
+
})]
|
|
216
|
+
}, e.name);
|
|
217
|
+
})
|
|
218
|
+
}) })] })
|
|
219
|
+
}),
|
|
220
|
+
D === "clients" && /* @__PURE__ */ h("div", {
|
|
221
|
+
className: "space-y-6",
|
|
222
|
+
children: [/* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ m(x, {
|
|
223
|
+
className: "text-base",
|
|
224
|
+
children: "Client Acquisition"
|
|
225
|
+
}) }), /* @__PURE__ */ m(y, {
|
|
226
|
+
className: "space-y-3",
|
|
227
|
+
children: w.map((e, t) => {
|
|
228
|
+
let n = t > 0 ? w[t - 1]?.value ?? 0 : 0, r = n > 0 ? ((e.value - n) / n * 100).toFixed(0) : "N/A";
|
|
229
|
+
return /* @__PURE__ */ h("div", {
|
|
230
|
+
className: "flex items-center gap-4 rounded-lg border p-3",
|
|
231
|
+
children: [/* @__PURE__ */ h("div", {
|
|
232
|
+
className: "flex-1",
|
|
233
|
+
children: [/* @__PURE__ */ h("div", {
|
|
234
|
+
className: "flex items-center justify-between",
|
|
235
|
+
children: [/* @__PURE__ */ m("span", {
|
|
236
|
+
className: "font-medium",
|
|
237
|
+
children: e.name
|
|
238
|
+
}), /* @__PURE__ */ h("span", {
|
|
239
|
+
className: "text-lg font-bold",
|
|
240
|
+
children: [e.value, " clients"]
|
|
241
|
+
})]
|
|
242
|
+
}), /* @__PURE__ */ m("div", {
|
|
243
|
+
className: "mt-2 h-2 overflow-hidden rounded-full bg-muted",
|
|
244
|
+
children: /* @__PURE__ */ m("div", {
|
|
245
|
+
className: "h-2 rounded-full bg-blue-500",
|
|
246
|
+
style: { width: `${e.value / P * 100}%` }
|
|
247
|
+
})
|
|
248
|
+
})]
|
|
249
|
+
}), /* @__PURE__ */ m(g, {
|
|
250
|
+
variant: "secondary",
|
|
251
|
+
className: "text-xs",
|
|
252
|
+
children: typeof r == "string" ? r : `+${r}%`
|
|
253
|
+
})]
|
|
254
|
+
}, e.name);
|
|
255
|
+
})
|
|
256
|
+
})] }), /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ h("div", {
|
|
257
|
+
className: "flex items-center gap-2",
|
|
258
|
+
children: [/* @__PURE__ */ m(l, { className: "h-5 w-5 text-muted-foreground" }), /* @__PURE__ */ m(x, {
|
|
259
|
+
className: "text-base",
|
|
260
|
+
children: "Geographic Distribution"
|
|
261
|
+
})]
|
|
262
|
+
}) }), /* @__PURE__ */ m(y, { children: /* @__PURE__ */ m("div", {
|
|
263
|
+
className: "overflow-x-auto",
|
|
264
|
+
children: /* @__PURE__ */ h("table", {
|
|
265
|
+
className: "w-full text-sm",
|
|
266
|
+
children: [/* @__PURE__ */ m("thead", { children: /* @__PURE__ */ h("tr", {
|
|
267
|
+
className: "border-b text-left text-muted-foreground",
|
|
268
|
+
children: [
|
|
269
|
+
/* @__PURE__ */ m("th", {
|
|
270
|
+
className: "p-3",
|
|
271
|
+
children: "Country"
|
|
272
|
+
}),
|
|
273
|
+
/* @__PURE__ */ m("th", {
|
|
274
|
+
className: "p-3 text-right",
|
|
275
|
+
children: "Clients"
|
|
276
|
+
}),
|
|
277
|
+
/* @__PURE__ */ m("th", {
|
|
278
|
+
className: "p-3 text-right",
|
|
279
|
+
children: "Revenue"
|
|
280
|
+
}),
|
|
281
|
+
/* @__PURE__ */ m("th", {
|
|
282
|
+
className: "p-3",
|
|
283
|
+
children: "Distribution"
|
|
284
|
+
})
|
|
285
|
+
]
|
|
286
|
+
}) }), /* @__PURE__ */ m("tbody", { children: E.map((e) => {
|
|
287
|
+
let t = E.reduce((e, t) => e + t.clients, 0), n = Math.round(e.clients / t * 100);
|
|
288
|
+
return /* @__PURE__ */ h("tr", {
|
|
289
|
+
className: "border-b hover:bg-muted/50",
|
|
290
|
+
children: [
|
|
291
|
+
/* @__PURE__ */ m("td", {
|
|
292
|
+
className: "p-3 font-medium",
|
|
293
|
+
children: e.country
|
|
294
|
+
}),
|
|
295
|
+
/* @__PURE__ */ m("td", {
|
|
296
|
+
className: "p-3 text-right",
|
|
297
|
+
children: e.clients
|
|
298
|
+
}),
|
|
299
|
+
/* @__PURE__ */ h("td", {
|
|
300
|
+
className: "p-3 text-right",
|
|
301
|
+
children: [
|
|
302
|
+
"$",
|
|
303
|
+
(e.revenue / 1e3).toFixed(0),
|
|
304
|
+
"k"
|
|
305
|
+
]
|
|
306
|
+
}),
|
|
307
|
+
/* @__PURE__ */ m("td", {
|
|
308
|
+
className: "p-3",
|
|
309
|
+
children: /* @__PURE__ */ h("div", {
|
|
310
|
+
className: "flex items-center gap-2",
|
|
311
|
+
children: [/* @__PURE__ */ m("div", {
|
|
312
|
+
className: "h-2 flex-1 overflow-hidden rounded-full bg-muted",
|
|
313
|
+
children: /* @__PURE__ */ m("div", {
|
|
314
|
+
className: "h-2 rounded-full bg-primary",
|
|
315
|
+
style: { width: `${n}%` }
|
|
316
|
+
})
|
|
317
|
+
}), /* @__PURE__ */ h("span", {
|
|
318
|
+
className: "w-10 text-right text-xs text-muted-foreground",
|
|
319
|
+
children: [n, "%"]
|
|
320
|
+
})]
|
|
321
|
+
})
|
|
322
|
+
})
|
|
323
|
+
]
|
|
324
|
+
}, e.country);
|
|
325
|
+
}) })]
|
|
326
|
+
})
|
|
327
|
+
}) })] })]
|
|
328
|
+
}),
|
|
329
|
+
D === "services" && /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ m(x, {
|
|
330
|
+
className: "text-base",
|
|
331
|
+
children: "Service Performance (Total Bookings)"
|
|
332
|
+
}) }), /* @__PURE__ */ m(y, {
|
|
333
|
+
className: "space-y-3",
|
|
334
|
+
children: [...T].sort((e, t) => t.value - e.value).map((e, t) => /* @__PURE__ */ h("div", {
|
|
335
|
+
className: "flex items-center gap-4",
|
|
336
|
+
children: [/* @__PURE__ */ h("span", {
|
|
337
|
+
className: "w-6 text-sm font-medium text-muted-foreground",
|
|
338
|
+
children: ["#", t + 1]
|
|
339
|
+
}), /* @__PURE__ */ h("div", {
|
|
340
|
+
className: "flex-1",
|
|
341
|
+
children: [/* @__PURE__ */ h("div", {
|
|
342
|
+
className: "flex items-center justify-between",
|
|
343
|
+
children: [/* @__PURE__ */ m("span", {
|
|
344
|
+
className: "text-sm font-medium",
|
|
345
|
+
children: e.name
|
|
346
|
+
}), /* @__PURE__ */ m("span", {
|
|
347
|
+
className: "text-sm font-bold",
|
|
348
|
+
children: e.value
|
|
349
|
+
})]
|
|
350
|
+
}), /* @__PURE__ */ m("div", {
|
|
351
|
+
className: "mt-1 h-3 overflow-hidden rounded-full bg-muted",
|
|
352
|
+
children: /* @__PURE__ */ m("div", {
|
|
353
|
+
className: "h-3 rounded-full bg-primary transition-all",
|
|
354
|
+
style: { width: `${e.value / F * 100}%` }
|
|
355
|
+
})
|
|
356
|
+
})]
|
|
357
|
+
})]
|
|
358
|
+
}, e.name))
|
|
359
|
+
})] }),
|
|
360
|
+
D === "performance" && /* @__PURE__ */ h("div", {
|
|
361
|
+
className: "grid gap-6 lg:grid-cols-2",
|
|
362
|
+
children: [/* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ m(x, {
|
|
363
|
+
className: "text-base",
|
|
364
|
+
children: "Monthly Revenue vs Previous"
|
|
365
|
+
}) }), /* @__PURE__ */ h(y, {
|
|
366
|
+
className: "space-y-3",
|
|
367
|
+
children: [C.map((e) => /* @__PURE__ */ h("div", {
|
|
368
|
+
className: "space-y-1",
|
|
369
|
+
children: [/* @__PURE__ */ h("div", {
|
|
370
|
+
className: "flex items-center justify-between text-xs",
|
|
371
|
+
children: [/* @__PURE__ */ m("span", {
|
|
372
|
+
className: "font-medium",
|
|
373
|
+
children: e.name
|
|
374
|
+
}), /* @__PURE__ */ h("span", {
|
|
375
|
+
className: "text-muted-foreground",
|
|
376
|
+
children: [
|
|
377
|
+
"$",
|
|
378
|
+
(e.value / 1e3).toFixed(1),
|
|
379
|
+
"k vs $",
|
|
380
|
+
((e.prev ?? 0) / 1e3).toFixed(1),
|
|
381
|
+
"k"
|
|
382
|
+
]
|
|
383
|
+
})]
|
|
384
|
+
}), /* @__PURE__ */ h("div", {
|
|
385
|
+
className: "flex gap-1",
|
|
386
|
+
children: [/* @__PURE__ */ m("div", {
|
|
387
|
+
className: "h-4 rounded bg-primary/30",
|
|
388
|
+
style: { width: `${(e.prev ?? 0) / N * 100}%` }
|
|
389
|
+
}), /* @__PURE__ */ m("div", {
|
|
390
|
+
className: "h-4 rounded bg-primary",
|
|
391
|
+
style: { width: `${e.value / N * 100 - (e.prev ?? 0) / N * 100}%` }
|
|
392
|
+
})]
|
|
393
|
+
})]
|
|
394
|
+
}, e.name)), /* @__PURE__ */ h("div", {
|
|
395
|
+
className: "mt-4 flex items-center gap-4 text-xs",
|
|
396
|
+
children: [/* @__PURE__ */ h("span", {
|
|
397
|
+
className: "flex items-center gap-1",
|
|
398
|
+
children: [/* @__PURE__ */ m("div", { className: "h-3 w-3 rounded bg-primary/30" }), " Previous"]
|
|
399
|
+
}), /* @__PURE__ */ h("span", {
|
|
400
|
+
className: "flex items-center gap-1",
|
|
401
|
+
children: [/* @__PURE__ */ m("div", { className: "h-3 w-3 rounded bg-primary" }), " Current"]
|
|
402
|
+
})]
|
|
403
|
+
})]
|
|
404
|
+
})] }), /* @__PURE__ */ h(v, { children: [/* @__PURE__ */ m(b, { children: /* @__PURE__ */ m(x, {
|
|
405
|
+
className: "text-base",
|
|
406
|
+
children: "Top Performing Services"
|
|
407
|
+
}) }), /* @__PURE__ */ m(y, { children: /* @__PURE__ */ m("div", {
|
|
408
|
+
className: "space-y-3",
|
|
409
|
+
children: [...T].sort((e, t) => t.value - e.value).slice(0, 5).map((e, t) => /* @__PURE__ */ h("div", {
|
|
410
|
+
className: "flex items-center justify-between rounded-lg border p-3",
|
|
411
|
+
children: [/* @__PURE__ */ h("div", {
|
|
412
|
+
className: "flex items-center gap-3",
|
|
413
|
+
children: [/* @__PURE__ */ m("div", {
|
|
414
|
+
className: `flex h-8 w-8 items-center justify-center rounded-full ${t === 0 ? "bg-yellow-100 text-yellow-700" : t === 1 ? "bg-gray-100 text-gray-600" : t === 2 ? "bg-orange-100 text-orange-600" : "bg-muted text-muted-foreground"} text-sm font-bold`,
|
|
415
|
+
children: t + 1
|
|
416
|
+
}), /* @__PURE__ */ m("span", {
|
|
417
|
+
className: "font-medium text-sm",
|
|
418
|
+
children: e.name
|
|
419
|
+
})]
|
|
420
|
+
}), /* @__PURE__ */ h(g, {
|
|
421
|
+
variant: "secondary",
|
|
422
|
+
children: [e.value, " bookings"]
|
|
423
|
+
})]
|
|
424
|
+
}, e.name))
|
|
425
|
+
}) })] })]
|
|
426
|
+
})
|
|
427
|
+
]
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
//#endregion
|
|
431
|
+
export { S as AnalyticsPage };
|
|
432
|
+
|
|
433
|
+
//# sourceMappingURL=AnalyticsPage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/proserviceworld/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { Badge, Button, Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport {\n TrendingUp,\n TrendingDown,\n Minus,\n DollarSign,\n Users,\n BarChart3,\n Activity,\n Globe,\n Calendar,\n ArrowUpRight,\n ArrowDownRight,\n Download,\n} from 'lucide-react';\nimport { useProServiceWorldStore } from '../store/proserviceworldStore';\n\ntype AnalyticsTab = 'overview' | 'revenue' | 'clients' | 'services' | 'performance';\n\nexport function AnalyticsPage() {\n const { analyticsMetrics, revenueData, clientData, servicePerformance, geographicData } =\n useProServiceWorldStore();\n const [activeTab, setActiveTab] = useState<AnalyticsTab>('overview');\n const [timeRange, setTimeRange] = useState('6m');\n\n const tabs: { key: AnalyticsTab; label: string; icon: typeof BarChart3 }[] = [\n { key: 'overview', label: 'Overview', icon: BarChart3 },\n { key: 'revenue', label: 'Revenue', icon: DollarSign },\n { key: 'clients', label: 'Clients', icon: Users },\n { key: 'services', label: 'Services', icon: Activity },\n { key: 'performance', label: 'Performance', icon: TrendingUp },\n ];\n\n const changeIcon = (type: string) => {\n if (type === 'positive') return <TrendingUp className=\"h-4 w-4 text-green-500\" />;\n if (type === 'negative') return <TrendingDown className=\"h-4 w-4 text-red-500\" />;\n return <Minus className=\"h-4 w-4 text-gray-400\" />;\n };\n\n const maxRevenue = Math.max(...revenueData.map((d) => d.value));\n const maxClients = Math.max(...clientData.map((d) => d.value));\n const maxServiceBookings = Math.max(...servicePerformance.map((d) => d.value));\n\n return (\n <div className=\"space-y-6 p-6\">\n {/* Header */}\n <div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Analytics</h1>\n <p className=\"text-sm text-muted-foreground\">\n Insights and performance metrics for your professional services.\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <div className=\"flex rounded border\">\n {['1m', '3m', '6m', '1y'].map((range) => (\n <button\n key={range}\n onClick={() => setTimeRange(range)}\n className={`px-3 py-1 text-xs font-medium ${\n timeRange === range\n ? 'bg-primary text-primary-foreground'\n : 'text-muted-foreground hover:bg-muted/50'\n }`}\n >\n {range.toUpperCase()}\n </button>\n ))}\n </div>\n <Button variant=\"outline\" size=\"sm\">\n <Download className=\"mr-2 h-4 w-4\" /> Export\n </Button>\n </div>\n </div>\n\n {/* Tabs */}\n <div className=\"flex gap-1 border-b\">\n {tabs.map(({ key, label, icon: Icon }) => (\n <button\n key={key}\n onClick={() => setActiveTab(key)}\n className={`flex items-center gap-2 border-b-2 px-4 py-2 text-sm font-medium transition-colors ${\n activeTab === key\n ? 'border-primary text-primary'\n : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n <Icon className=\"h-4 w-4\" />\n {label}\n </button>\n ))}\n </div>\n\n {/* Metric Cards (shown on all tabs) */}\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-5\">\n {analyticsMetrics.map((metric, i) => (\n <Card key={i}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-xs font-medium text-muted-foreground\">{metric.label}</p>\n {changeIcon(metric.changeType)}\n </div>\n <p className=\"mt-1 text-2xl font-bold\">{metric.value}</p>\n <p\n className={`mt-0.5 text-xs ${\n metric.changeType === 'positive'\n ? 'text-green-600'\n : metric.changeType === 'negative'\n ? 'text-red-600'\n : 'text-gray-500'\n }`}\n >\n {metric.change} from last period\n </p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n {/* Overview Tab */}\n {activeTab === 'overview' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n {/* Revenue chart */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Revenue Trend</CardTitle>\n <Badge variant=\"secondary\">Last 6 months</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {revenueData.map((point) => (\n <div key={point.name} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-xs text-muted-foreground\">{point.name}</span>\n <div className=\"relative flex-1 h-6 bg-muted rounded overflow-hidden\">\n {point.prev !== undefined && (\n <div\n className=\"absolute h-6 bg-muted-foreground/10 rounded\"\n style={{ width: `${(point.prev / maxRevenue) * 100}%` }}\n />\n )}\n <div\n className=\"relative h-6 bg-primary rounded\"\n style={{ width: `${(point.value / maxRevenue) * 100}%` }}\n />\n </div>\n <span className=\"w-20 text-right text-xs font-medium\">\n ${(point.value / 1000).toFixed(1)}k\n </span>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Client growth */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">Client Growth</CardTitle>\n <Badge variant=\"secondary\">Monthly</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {clientData.map((point) => (\n <div key={point.name} className=\"flex items-center gap-3\">\n <span className=\"w-10 text-xs text-muted-foreground\">{point.name}</span>\n <div className=\"flex-1 h-6 bg-muted rounded overflow-hidden\">\n <div\n className=\"h-6 bg-blue-500 rounded\"\n style={{ width: `${(point.value / maxClients) * 100}%` }}\n />\n </div>\n <span className=\"w-12 text-right text-xs font-medium\">{point.value}</span>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Revenue Tab */}\n {activeTab === 'revenue' && (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Revenue Breakdown</CardTitle>\n </CardHeader>\n <CardContent>\n <div 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\n key={point.name}\n className=\"flex items-center justify-between rounded-lg border p-4\"\n >\n <div className=\"flex items-center gap-4\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10\">\n <Calendar className=\"h-5 w-5 text-primary\" />\n </div>\n <div>\n <p className=\"font-medium\">{point.name} 2025</p>\n <p className=\"text-sm text-muted-foreground\">\n Previous: ${((point.prev ?? 0) / 1000).toFixed(1)}k\n </p>\n </div>\n </div>\n <div className=\"text-right\">\n <p className=\"text-lg font-bold\">${(point.value / 1000).toFixed(1)}k</p>\n <p\n className={`flex items-center gap-1 text-xs ${isPositive ? 'text-green-600' : 'text-red-600'}`}\n >\n {isPositive ? (\n <ArrowUpRight className=\"h-3 w-3\" />\n ) : (\n <ArrowDownRight className=\"h-3 w-3\" />\n )}\n {growth}%\n </p>\n </div>\n </div>\n );\n })}\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Clients Tab */}\n {activeTab === 'clients' && (\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Client Acquisition</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {clientData.map((point, idx) => {\n const prev = idx > 0 ? (clientData[idx - 1]?.value ?? 0) : 0;\n const growth = prev > 0 ? (((point.value - prev) / prev) * 100).toFixed(0) : 'N/A';\n return (\n <div key={point.name} className=\"flex items-center gap-4 rounded-lg border p-3\">\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"font-medium\">{point.name}</span>\n <span className=\"text-lg font-bold\">{point.value} clients</span>\n </div>\n <div className=\"mt-2 h-2 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-2 rounded-full bg-blue-500\"\n style={{ width: `${(point.value / maxClients) * 100}%` }}\n />\n </div>\n </div>\n <Badge variant=\"secondary\" className=\"text-xs\">\n {typeof growth === 'string' ? growth : `+${growth}%`}\n </Badge>\n </div>\n );\n })}\n </CardContent>\n </Card>\n\n {/* Geographic data */}\n <Card>\n <CardHeader>\n <div className=\"flex items-center gap-2\">\n <Globe className=\"h-5 w-5 text-muted-foreground\" />\n <CardTitle className=\"text-base\">Geographic Distribution</CardTitle>\n </div>\n </CardHeader>\n <CardContent>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full text-sm\">\n <thead>\n <tr className=\"border-b text-left text-muted-foreground\">\n <th className=\"p-3\">Country</th>\n <th className=\"p-3 text-right\">Clients</th>\n <th className=\"p-3 text-right\">Revenue</th>\n <th className=\"p-3\">Distribution</th>\n </tr>\n </thead>\n <tbody>\n {geographicData.map((geo) => {\n const totalClients = geographicData.reduce((s, g) => s + g.clients, 0);\n const pct = Math.round((geo.clients / totalClients) * 100);\n return (\n <tr key={geo.country} className=\"border-b hover:bg-muted/50\">\n <td className=\"p-3 font-medium\">{geo.country}</td>\n <td className=\"p-3 text-right\">{geo.clients}</td>\n <td className=\"p-3 text-right\">${(geo.revenue / 1000).toFixed(0)}k</td>\n <td className=\"p-3\">\n <div className=\"flex items-center gap-2\">\n <div className=\"h-2 flex-1 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-2 rounded-full bg-primary\"\n style={{ width: `${pct}%` }}\n />\n </div>\n <span className=\"w-10 text-right text-xs text-muted-foreground\">\n {pct}%\n </span>\n </div>\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n\n {/* Services Tab */}\n {activeTab === 'services' && (\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Service Performance (Total Bookings)</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-4\">\n <span className=\"w-6 text-sm font-medium text-muted-foreground\">#{idx + 1}</span>\n <div className=\"flex-1\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{service.name}</span>\n <span className=\"text-sm font-bold\">{service.value}</span>\n </div>\n <div className=\"mt-1 h-3 overflow-hidden rounded-full bg-muted\">\n <div\n className=\"h-3 rounded-full bg-primary transition-all\"\n style={{ width: `${(service.value / maxServiceBookings) * 100}%` }}\n />\n </div>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n )}\n\n {/* Performance Tab */}\n {activeTab === 'performance' && (\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Monthly Revenue vs Previous</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {revenueData.map((point) => (\n <div key={point.name} className=\"space-y-1\">\n <div className=\"flex items-center justify-between text-xs\">\n <span className=\"font-medium\">{point.name}</span>\n <span className=\"text-muted-foreground\">\n ${(point.value / 1000).toFixed(1)}k vs $\n {((point.prev ?? 0) / 1000).toFixed(1)}k\n </span>\n </div>\n <div className=\"flex gap-1\">\n <div\n className=\"h-4 rounded bg-primary/30\"\n style={{ width: `${((point.prev ?? 0) / maxRevenue) * 100}%` }}\n />\n <div\n className=\"h-4 rounded bg-primary\"\n style={{\n width: `${(point.value / maxRevenue) * 100 - ((point.prev ?? 0) / maxRevenue) * 100}%`,\n }}\n />\n </div>\n </div>\n ))}\n <div className=\"mt-4 flex items-center gap-4 text-xs\">\n <span className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 rounded bg-primary/30\" /> Previous\n </span>\n <span className=\"flex items-center gap-1\">\n <div className=\"h-3 w-3 rounded bg-primary\" /> Current\n </span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"text-base\">Top Performing Services</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {[...servicePerformance]\n .sort((a, b) => b.value - a.value)\n .slice(0, 5)\n .map((service, idx) => (\n <div\n key={service.name}\n className=\"flex items-center justify-between rounded-lg border p-3\"\n >\n <div className=\"flex items-center gap-3\">\n <div\n className={`flex h-8 w-8 items-center justify-center rounded-full ${\n idx === 0\n ? 'bg-yellow-100 text-yellow-700'\n : idx === 1\n ? 'bg-gray-100 text-gray-600'\n : idx === 2\n ? 'bg-orange-100 text-orange-600'\n : 'bg-muted text-muted-foreground'\n } text-sm font-bold`}\n >\n {idx + 1}\n </div>\n <span className=\"font-medium text-sm\">{service.name}</span>\n </div>\n <Badge variant=\"secondary\">{service.value} bookings</Badge>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BookingsPage.d.ts","sourceRoot":"","sources":["../../../src/proserviceworld/pages/BookingsPage.tsx"],"names":[],"mappings":"AA0DA,wBAAgB,YAAY,4CAyV3B"}
|