medusa-analytics 0.0.18 → 0.0.20
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.
|
@@ -40,8 +40,8 @@ const ACCENT_STYLES = {
|
|
|
40
40
|
function getAccentStyle(accent) {
|
|
41
41
|
return ACCENT_STYLES[accent];
|
|
42
42
|
}
|
|
43
|
-
function AnalyticsDashboardShell({ children }) {
|
|
44
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2.5 md:space-y-3", children });
|
|
43
|
+
function AnalyticsDashboardShell({ children, className }) {
|
|
44
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: ["space-y-2.5 md:space-y-3", className].filter(Boolean).join(" "), children });
|
|
45
45
|
}
|
|
46
46
|
function AnalyticsDashboardHeader({
|
|
47
47
|
title,
|
|
@@ -49,20 +49,30 @@ function AnalyticsDashboardHeader({
|
|
|
49
49
|
actions,
|
|
50
50
|
variant = "default",
|
|
51
51
|
actionsBare = false,
|
|
52
|
-
appearance = "card"
|
|
52
|
+
appearance = "card",
|
|
53
|
+
actionsClassName
|
|
53
54
|
}) {
|
|
54
55
|
const isPremium = variant === "premium";
|
|
55
56
|
const isInset = appearance === "inset";
|
|
56
57
|
const headerRow = /* @__PURE__ */ jsxRuntime.jsxs(
|
|
57
58
|
"div",
|
|
58
59
|
{
|
|
59
|
-
className: `flex flex-col gap-2 px-4 py-2.5 lg:flex-row lg:items-center lg:justify-between ${isPremium ? "border-b border-ui-border-base/80" : "border-b border-ui-border-base"}`.trim(),
|
|
60
|
+
className: `flex flex-col gap-2 px-4 py-2.5 lg:flex-row lg:items-center lg:justify-between lg:gap-4 ${isPremium ? "border-b border-ui-border-base/80" : "border-b border-ui-border-base"}`.trim(),
|
|
60
61
|
children: [
|
|
61
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
62
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [
|
|
62
63
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: isPremium ? "text-lg font-semibold tracking-tight" : "tracking-tight", children: title }),
|
|
63
64
|
description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: `text-ui-fg-muted ${isPremium ? "max-w-2xl text-xs leading-snug" : "text-sm"}`.trim(), children: description }) : null
|
|
64
65
|
] }),
|
|
65
|
-
actions ? actionsBare ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
66
|
+
actions ? actionsBare ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
67
|
+
"div",
|
|
68
|
+
{
|
|
69
|
+
className: [
|
|
70
|
+
"flex flex-wrap items-center gap-3 self-start lg:shrink-0 lg:self-auto",
|
|
71
|
+
actionsClassName ?? ""
|
|
72
|
+
].join(" ").trim(),
|
|
73
|
+
children: actions
|
|
74
|
+
}
|
|
75
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
66
76
|
"div",
|
|
67
77
|
{
|
|
68
78
|
className: `flex self-start rounded-xl border px-2 py-1.5 lg:self-auto ${isPremium ? "border-ui-border-strong bg-ui-bg-subtle shadow-[inset_0_1px_0_rgba(255,255,255,0.35)]" : "border-ui-border-base bg-ui-bg-subtle"}`.trim(),
|
|
@@ -156,7 +166,7 @@ function AnalyticsSection({
|
|
|
156
166
|
}) {
|
|
157
167
|
const isAtlas = variant === "atlas";
|
|
158
168
|
const isPremium = variant !== "default" && !isAtlas;
|
|
159
|
-
const shellClass = isAtlas ? "overflow-
|
|
169
|
+
const shellClass = isAtlas ? "min-w-0 overflow-x-clip overflow-y-visible rounded-lg border border-ui-border-base bg-ui-bg-base shadow-sm" : `overflow-hidden rounded-2xl border bg-ui-bg-base p-0 ${isPremium ? "border-ui-border-strong shadow-sm" : "border-ui-border-base shadow-sm"}`.trim();
|
|
160
170
|
const headerClass = isAtlas ? "flex flex-col gap-1 border-b border-ui-border-base bg-ui-bg-subtle/40 px-3 py-2 lg:flex-row lg:items-center lg:justify-between" : `flex flex-col gap-2 px-4 py-2.5 lg:flex-row lg:items-center lg:justify-between ${isPremium ? "border-b border-ui-border-base/80" : "border-b border-ui-border-base"}`.trim();
|
|
161
171
|
const titleClass = isAtlas ? "text-sm font-semibold tracking-tight text-ui-fg-base" : isPremium ? "text-base font-semibold tracking-tight" : "tracking-tight";
|
|
162
172
|
const descClass = isAtlas ? "max-w-2xl text-[11px] leading-snug text-ui-fg-muted" : isPremium ? "max-w-2xl text-xs leading-snug" : "text-sm";
|
|
@@ -191,11 +201,55 @@ function AnalyticsTableSurface({ children, className }) {
|
|
|
191
201
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
192
202
|
"div",
|
|
193
203
|
{
|
|
194
|
-
className:
|
|
204
|
+
className: [
|
|
205
|
+
"min-w-0 overflow-x-auto overflow-y-visible rounded-xl border border-ui-border-base bg-ui-bg-base",
|
|
206
|
+
"[scrollbar-width:thin]",
|
|
207
|
+
className ?? ""
|
|
208
|
+
].join(" ").trim(),
|
|
195
209
|
children
|
|
196
210
|
}
|
|
197
211
|
);
|
|
198
212
|
}
|
|
213
|
+
function AnalyticsTooltipCard({
|
|
214
|
+
title,
|
|
215
|
+
children,
|
|
216
|
+
variant = "default"
|
|
217
|
+
}) {
|
|
218
|
+
const compact = variant === "compact";
|
|
219
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
220
|
+
"div",
|
|
221
|
+
{
|
|
222
|
+
style: {
|
|
223
|
+
background: "linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(248,250,252,0.96) 100%)",
|
|
224
|
+
color: "#111827",
|
|
225
|
+
border: "1px solid rgba(148, 163, 184, 0.18)",
|
|
226
|
+
borderRadius: compact ? "8px" : "14px",
|
|
227
|
+
padding: compact ? "5px 8px" : "12px 14px",
|
|
228
|
+
boxShadow: compact ? "0 6px 16px rgba(15, 23, 42, 0.1)" : "0 16px 40px rgba(15, 23, 42, 0.12)",
|
|
229
|
+
fontSize: compact ? "11px" : "13px",
|
|
230
|
+
lineHeight: compact ? 1.35 : 1.5,
|
|
231
|
+
minWidth: compact ? 0 : 176,
|
|
232
|
+
maxWidth: compact ? "min(92vw, 200px)" : "min(96vw, 380px)"
|
|
233
|
+
},
|
|
234
|
+
children: [
|
|
235
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
236
|
+
"div",
|
|
237
|
+
{
|
|
238
|
+
style: {
|
|
239
|
+
fontWeight: 600,
|
|
240
|
+
marginBottom: compact ? "2px" : "6px",
|
|
241
|
+
fontSize: compact ? "10px" : "inherit",
|
|
242
|
+
lineHeight: compact ? 1.25 : 1.35,
|
|
243
|
+
wordBreak: "break-word"
|
|
244
|
+
},
|
|
245
|
+
children: title
|
|
246
|
+
}
|
|
247
|
+
),
|
|
248
|
+
children
|
|
249
|
+
]
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
}
|
|
199
253
|
function isDailyOrderRow(value) {
|
|
200
254
|
if (typeof value !== "object" || value === null) return false;
|
|
201
255
|
const v = value;
|
|
@@ -225,7 +279,7 @@ function formatCurrency$1(value) {
|
|
|
225
279
|
maximumFractionDigits: 0
|
|
226
280
|
}).format(value);
|
|
227
281
|
}
|
|
228
|
-
function formatCompactCurrency(value) {
|
|
282
|
+
function formatCompactCurrency$1(value) {
|
|
229
283
|
return new Intl.NumberFormat("en-IN", {
|
|
230
284
|
style: "currency",
|
|
231
285
|
currency: "INR",
|
|
@@ -233,7 +287,7 @@ function formatCompactCurrency(value) {
|
|
|
233
287
|
maximumFractionDigits: 1
|
|
234
288
|
}).format(value);
|
|
235
289
|
}
|
|
236
|
-
function formatChartLabel(value) {
|
|
290
|
+
function formatChartLabel$2(value) {
|
|
237
291
|
if (!value) return "";
|
|
238
292
|
const parsed = new Date(value);
|
|
239
293
|
if (Number.isNaN(parsed.getTime())) return value;
|
|
@@ -248,37 +302,12 @@ function ordersCountFromPiePayload(payload) {
|
|
|
248
302
|
const c = payload.orders_count;
|
|
249
303
|
return Math.floor(Number(c) || 0);
|
|
250
304
|
}
|
|
251
|
-
function formatShortNumber(value) {
|
|
305
|
+
function formatShortNumber$1(value) {
|
|
252
306
|
return new Intl.NumberFormat("en-IN", {
|
|
253
307
|
notation: "compact",
|
|
254
308
|
maximumFractionDigits: value < 10 ? 1 : 0
|
|
255
309
|
}).format(value);
|
|
256
310
|
}
|
|
257
|
-
function TooltipCard({
|
|
258
|
-
title,
|
|
259
|
-
children
|
|
260
|
-
}) {
|
|
261
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
262
|
-
"div",
|
|
263
|
-
{
|
|
264
|
-
style: {
|
|
265
|
-
background: "linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(248,250,252,0.96) 100%)",
|
|
266
|
-
color: "#111827",
|
|
267
|
-
border: "1px solid rgba(148, 163, 184, 0.18)",
|
|
268
|
-
borderRadius: "14px",
|
|
269
|
-
padding: "12px 14px",
|
|
270
|
-
boxShadow: "0 16px 40px rgba(15, 23, 42, 0.12)",
|
|
271
|
-
fontSize: "13px",
|
|
272
|
-
lineHeight: 1.5,
|
|
273
|
-
minWidth: "176px"
|
|
274
|
-
},
|
|
275
|
-
children: [
|
|
276
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 600, marginBottom: "6px" }, children: title }),
|
|
277
|
-
children
|
|
278
|
-
]
|
|
279
|
-
}
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
311
|
function OrdersTrendTooltip({
|
|
283
312
|
active,
|
|
284
313
|
payload,
|
|
@@ -290,7 +319,7 @@ function OrdersTrendTooltip({
|
|
|
290
319
|
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
291
320
|
const orders = ((_c = payload.find((entry) => entry.name === "Orders")) == null ? void 0 : _c.value) ?? 0;
|
|
292
321
|
const revenue = ((_d = payload.find((entry) => entry.name === "Revenue")) == null ? void 0 : _d.value) ?? 0;
|
|
293
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
322
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { title: displayLabel, children: [
|
|
294
323
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
295
324
|
"Orders: ",
|
|
296
325
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(orders).toLocaleString() })
|
|
@@ -311,7 +340,7 @@ function OrdersTodayTooltip({
|
|
|
311
340
|
const row = isOrdersTodayPoint((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
312
341
|
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
313
342
|
const orders = ((_c = payload[0]) == null ? void 0 : _c.value) ?? 0;
|
|
314
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
343
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { variant: "compact", title: displayLabel, children: [
|
|
315
344
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
316
345
|
"Orders: ",
|
|
317
346
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(orders).toLocaleString() })
|
|
@@ -329,7 +358,7 @@ function OrdersSparklineTooltip({
|
|
|
329
358
|
const row = isDailyOrderRow((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
330
359
|
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
331
360
|
const orders = Number((_c = payload[0]) == null ? void 0 : _c.value) || 0;
|
|
332
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
361
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTooltipCard, { variant: "compact", title: displayLabel, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
333
362
|
"Orders: ",
|
|
334
363
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(orders).toLocaleString() })
|
|
335
364
|
] }) });
|
|
@@ -343,33 +372,28 @@ function OutcomesTrendTooltip({
|
|
|
343
372
|
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
344
373
|
const row = isDailyOrderRow((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
345
374
|
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
346
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
375
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTooltipCard, { variant: "compact", title: displayLabel, children: payload.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
347
376
|
entry.name,
|
|
348
377
|
":",
|
|
349
378
|
" ",
|
|
350
379
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(Number(entry.value) || 0).toLocaleString() })
|
|
351
380
|
] }, String(entry.name))) });
|
|
352
381
|
}
|
|
353
|
-
const KPI_ICON_BG = {
|
|
382
|
+
const KPI_ICON_BG$2 = {
|
|
354
383
|
green: "bg-emerald-500/20",
|
|
355
384
|
blue: "bg-sky-500/20",
|
|
356
385
|
purple: "bg-violet-500/20",
|
|
357
386
|
amber: "bg-amber-500/20"
|
|
358
387
|
};
|
|
359
|
-
const KPI_ICONS = {
|
|
388
|
+
const KPI_ICONS$2 = {
|
|
360
389
|
green: icons.CurrencyDollar,
|
|
361
390
|
blue: icons.ShoppingCart,
|
|
362
391
|
purple: icons.TruckFast,
|
|
363
392
|
amber: icons.Cash
|
|
364
393
|
};
|
|
365
394
|
const PIE_PALETTE = ["#6366F1", "#94A3B8", "#10B981", "#F59E0B"];
|
|
366
|
-
function TrafficRevenueDonut({
|
|
367
|
-
|
|
368
|
-
compact = false
|
|
369
|
-
}) {
|
|
370
|
-
const innerRadius = compact ? 28 : 34;
|
|
371
|
-
const outerRadius = compact ? 44 : 54;
|
|
372
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: compact ? "h-[118px] w-full" : "h-[152px] w-full shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.PieChart, { margin: { top: 2, right: 2, left: 2, bottom: 2 }, children: [
|
|
395
|
+
function TrafficRevenueDonut({ traffic }) {
|
|
396
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[152px] w-full shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.PieChart, { margin: { top: 2, right: 2, left: 2, bottom: 2 }, children: [
|
|
373
397
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
374
398
|
recharts.Pie,
|
|
375
399
|
{
|
|
@@ -378,8 +402,8 @@ function TrafficRevenueDonut({
|
|
|
378
402
|
nameKey: "label",
|
|
379
403
|
cx: "50%",
|
|
380
404
|
cy: "48%",
|
|
381
|
-
innerRadius,
|
|
382
|
-
outerRadius,
|
|
405
|
+
innerRadius: 34,
|
|
406
|
+
outerRadius: 54,
|
|
383
407
|
paddingAngle: 2,
|
|
384
408
|
children: traffic.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(recharts.Cell, { fill: PIE_PALETTE[i % PIE_PALETTE.length] }, `traffic-slice-${i}`))
|
|
385
409
|
}
|
|
@@ -389,7 +413,7 @@ function TrafficRevenueDonut({
|
|
|
389
413
|
{
|
|
390
414
|
content: ({ active, payload }) => {
|
|
391
415
|
var _a, _b, _c;
|
|
392
|
-
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
416
|
+
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { variant: "compact", title: String(((_a = payload[0]) == null ? void 0 : _a.name) ?? ""), children: [
|
|
393
417
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
394
418
|
"Revenue:",
|
|
395
419
|
" ",
|
|
@@ -407,44 +431,28 @@ function TrafficRevenueDonut({
|
|
|
407
431
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
408
432
|
recharts.Legend,
|
|
409
433
|
{
|
|
410
|
-
wrapperStyle: { fontSize:
|
|
434
|
+
wrapperStyle: { fontSize: 9, paddingTop: 0 },
|
|
411
435
|
verticalAlign: "bottom",
|
|
412
|
-
height:
|
|
436
|
+
height: 22,
|
|
413
437
|
iconType: "circle"
|
|
414
438
|
}
|
|
415
439
|
)
|
|
416
440
|
] }) }) });
|
|
417
441
|
}
|
|
418
|
-
function
|
|
419
|
-
traffic,
|
|
420
|
-
loading,
|
|
421
|
-
error,
|
|
422
|
-
totalRevenue,
|
|
423
|
-
windowLabel
|
|
424
|
-
}) {
|
|
425
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-[132px] flex-col rounded-2xl border border-ui-border-base p-3 shadow-sm", children: [
|
|
426
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs font-medium text-ui-fg-base", children: "Traffic sources" }),
|
|
427
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "mb-1 text-[10px] leading-snug text-ui-fg-muted", children: [
|
|
428
|
-
"Revenue share · ",
|
|
429
|
-
windowLabel
|
|
430
|
-
] }),
|
|
431
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[104px] items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[104px] items-center justify-center px-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-center text-[10px] text-ui-fg-danger", children: error }) }) : traffic.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[104px] items-center justify-center px-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-center text-[10px] leading-snug text-ui-fg-muted", children: "No traffic breakdown for this window." }) }) : totalRevenue <= 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[104px] items-center justify-center px-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-center text-[10px] leading-snug text-ui-fg-muted", children: "No recorded revenue in this window." }) }) : /* @__PURE__ */ jsxRuntime.jsx(TrafficRevenueDonut, { traffic, compact: true }) })
|
|
432
|
-
] });
|
|
433
|
-
}
|
|
434
|
-
function AtlasKpiCard({
|
|
442
|
+
function AtlasKpiCard$2({
|
|
435
443
|
label,
|
|
436
444
|
value,
|
|
437
445
|
helper,
|
|
438
446
|
accent
|
|
439
447
|
}) {
|
|
440
|
-
const Icon = KPI_ICONS[accent];
|
|
448
|
+
const Icon = KPI_ICONS$2[accent];
|
|
441
449
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-[112px] min-w-0 flex-col rounded-xl border border-ui-border-base/80 bg-ui-bg-subtle/25 p-4 shadow-sm", children: [
|
|
442
450
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
443
451
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "min-w-0 flex-1 text-left text-xs font-medium leading-snug text-ui-fg-base", children: label }),
|
|
444
452
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
445
453
|
"div",
|
|
446
454
|
{
|
|
447
|
-
className: `flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${KPI_ICON_BG[accent]}`,
|
|
455
|
+
className: `flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${KPI_ICON_BG$2[accent]}`,
|
|
448
456
|
"aria-hidden": true,
|
|
449
457
|
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4 text-ui-fg-base" })
|
|
450
458
|
}
|
|
@@ -476,11 +484,11 @@ const STATUS_BAR_COLORS = {
|
|
|
476
484
|
orders: "#38BDF8",
|
|
477
485
|
revenue: "#D946EF"
|
|
478
486
|
};
|
|
479
|
-
const CHART_AXIS_TICK = { fontSize: 10, fill: "#e5e7eb" };
|
|
480
|
-
const CHART_AXIS_TICK_SM = { fontSize: 9, fill: "#e5e7eb" };
|
|
481
|
-
const CHART_AXIS_LINE = "#94a3b8";
|
|
482
|
-
const SELECT_CLASS_NAME = "h-9 min-w-[132px] cursor-pointer appearance-none rounded
|
|
483
|
-
function EmptyAnalyticsPanel({
|
|
487
|
+
const CHART_AXIS_TICK$2 = { fontSize: 10, fill: "#e5e7eb" };
|
|
488
|
+
const CHART_AXIS_TICK_SM$1 = { fontSize: 9, fill: "#e5e7eb" };
|
|
489
|
+
const CHART_AXIS_LINE$2 = "#94a3b8";
|
|
490
|
+
const SELECT_CLASS_NAME$2 = "h-9 min-w-[132px] cursor-pointer appearance-none rounded border border-ui-border-base bg-ui-bg-base py-1.5 pl-3 pr-8 text-xs text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive";
|
|
491
|
+
function EmptyAnalyticsPanel$1({
|
|
484
492
|
title,
|
|
485
493
|
description
|
|
486
494
|
}) {
|
|
@@ -490,7 +498,7 @@ function EmptyAnalyticsPanel({
|
|
|
490
498
|
] });
|
|
491
499
|
}
|
|
492
500
|
function OrdersDashboard() {
|
|
493
|
-
var _a, _b, _c
|
|
501
|
+
var _a, _b, _c;
|
|
494
502
|
const [data, setData] = react.useState(null);
|
|
495
503
|
const [loading, setLoading] = react.useState(true);
|
|
496
504
|
const [error, setError] = react.useState(null);
|
|
@@ -734,7 +742,7 @@ function OrdersDashboard() {
|
|
|
734
742
|
const pctDeliveredOfShipped = shipped > 0 ? delivered / shipped * 100 : 0;
|
|
735
743
|
return {
|
|
736
744
|
date: d.date,
|
|
737
|
-
label: d.label ?? formatChartLabel(d.date),
|
|
745
|
+
label: d.label ?? formatChartLabel$2(d.date),
|
|
738
746
|
placed,
|
|
739
747
|
not_cancelled: notCancelled,
|
|
740
748
|
shipped,
|
|
@@ -749,17 +757,16 @@ function OrdersDashboard() {
|
|
|
749
757
|
() => (ordersInsights == null ? void 0 : ordersInsights.traffic.reduce((sum, t) => sum + (t.revenue || 0), 0)) ?? 0,
|
|
750
758
|
[ordersInsights]
|
|
751
759
|
);
|
|
752
|
-
const insightsRangeLabel = ((_b = OVER_TIME_PERIODS.find((p) => p.value === overTimePeriod)) == null ? void 0 : _b.label) ?? "One week";
|
|
753
760
|
const quickPulseMetrics = [
|
|
754
761
|
{
|
|
755
762
|
label: "Range revenue",
|
|
756
|
-
value: formatCompactCurrency(trendRevenueTotal),
|
|
763
|
+
value: formatCompactCurrency$1(trendRevenueTotal),
|
|
757
764
|
helper: selectedSummaryPeriod,
|
|
758
765
|
accentClassName: "border-emerald-400/25 bg-emerald-500/5"
|
|
759
766
|
},
|
|
760
767
|
{
|
|
761
768
|
label: "Range orders",
|
|
762
|
-
value: formatShortNumber(trendOrdersTotal),
|
|
769
|
+
value: formatShortNumber$1(trendOrdersTotal),
|
|
763
770
|
helper: "Current trend window",
|
|
764
771
|
accentClassName: "border-sky-400/25 bg-sky-500/5"
|
|
765
772
|
},
|
|
@@ -771,8 +778,8 @@ function OrdersDashboard() {
|
|
|
771
778
|
},
|
|
772
779
|
{
|
|
773
780
|
label: "Peak revenue day",
|
|
774
|
-
value: peakRevenuePoint ? formatCompactCurrency(peakRevenuePoint.total_revenue) : "—",
|
|
775
|
-
helper: (peakRevenuePoint == null ? void 0 : peakRevenuePoint.label) ?? formatChartLabel(peakRevenuePoint == null ? void 0 : peakRevenuePoint.date),
|
|
781
|
+
value: peakRevenuePoint ? formatCompactCurrency$1(peakRevenuePoint.total_revenue) : "—",
|
|
782
|
+
helper: (peakRevenuePoint == null ? void 0 : peakRevenuePoint.label) ?? formatChartLabel$2(peakRevenuePoint == null ? void 0 : peakRevenuePoint.date),
|
|
776
783
|
accentClassName: "border-amber-400/25 bg-amber-500/5"
|
|
777
784
|
}
|
|
778
785
|
];
|
|
@@ -815,7 +822,7 @@ function OrdersDashboard() {
|
|
|
815
822
|
id: "analytics-period",
|
|
816
823
|
value: filter,
|
|
817
824
|
onChange: (e) => setFilter(e.target.value),
|
|
818
|
-
className: SELECT_CLASS_NAME,
|
|
825
|
+
className: SELECT_CLASS_NAME$2,
|
|
819
826
|
children: SUMMARY_PERIODS$1.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.value, children: p.label }, p.value))
|
|
820
827
|
}
|
|
821
828
|
)
|
|
@@ -836,28 +843,16 @@ function OrdersDashboard() {
|
|
|
836
843
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 p-3 pt-0 md:p-4 md:pt-0", children: [
|
|
837
844
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
838
845
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Order overview" }) }),
|
|
839
|
-
/* @__PURE__ */ jsxRuntime.
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
),
|
|
850
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3 lg:grid-cols-4", children: primaryStats.map((stat) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
851
|
-
AtlasKpiCard,
|
|
852
|
-
{
|
|
853
|
-
label: stat.label,
|
|
854
|
-
value: stat.value,
|
|
855
|
-
helper: stat.helper,
|
|
856
|
-
accent: stat.accent
|
|
857
|
-
},
|
|
858
|
-
stat.label
|
|
859
|
-
)) })
|
|
860
|
-
] })
|
|
846
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3 lg:grid-cols-4", children: primaryStats.map((stat) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
847
|
+
AtlasKpiCard$2,
|
|
848
|
+
{
|
|
849
|
+
label: stat.label,
|
|
850
|
+
value: stat.value,
|
|
851
|
+
helper: stat.helper,
|
|
852
|
+
accent: stat.accent
|
|
853
|
+
},
|
|
854
|
+
stat.label
|
|
855
|
+
)) })
|
|
861
856
|
] }),
|
|
862
857
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 grid grid-cols-12 gap-2 xl:items-start", children: [
|
|
863
858
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-12 xl:col-span-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -882,7 +877,7 @@ function OrdersDashboard() {
|
|
|
882
877
|
id: "over-time-period",
|
|
883
878
|
value: overTimePeriod,
|
|
884
879
|
onChange: (e) => setOverTimePeriod(e.target.value),
|
|
885
|
-
className: SELECT_CLASS_NAME,
|
|
880
|
+
className: SELECT_CLASS_NAME$2,
|
|
886
881
|
children: OVER_TIME_PERIODS.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.value, children: p.label }, p.value))
|
|
887
882
|
}
|
|
888
883
|
)
|
|
@@ -891,10 +886,10 @@ function OrdersDashboard() {
|
|
|
891
886
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-1.5 sm:grid-cols-3", children: [
|
|
892
887
|
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
893
888
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window revenue" }),
|
|
894
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: formatCompactCurrency(trendRevenueTotal) }),
|
|
889
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: formatCompactCurrency$1(trendRevenueTotal) }),
|
|
895
890
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: [
|
|
896
891
|
"Avg/day ",
|
|
897
|
-
formatCompactCurrency(trendAverageRevenue)
|
|
892
|
+
formatCompactCurrency$1(trendAverageRevenue)
|
|
898
893
|
] })
|
|
899
894
|
] }) }),
|
|
900
895
|
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
@@ -938,26 +933,27 @@ function OrdersDashboard() {
|
|
|
938
933
|
recharts.YAxis,
|
|
939
934
|
{
|
|
940
935
|
width: 36,
|
|
941
|
-
tick: CHART_AXIS_TICK_SM,
|
|
942
|
-
tickFormatter: (v) => formatCompactCurrency(Number(v))
|
|
936
|
+
tick: CHART_AXIS_TICK_SM$1,
|
|
937
|
+
tickFormatter: (v) => formatCompactCurrency$1(Number(v))
|
|
943
938
|
}
|
|
944
939
|
),
|
|
945
940
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
946
941
|
recharts.Tooltip,
|
|
947
942
|
{
|
|
948
943
|
content: ({ active, payload, label }) => {
|
|
949
|
-
var _a2, _b2, _c2,
|
|
944
|
+
var _a2, _b2, _c2, _d;
|
|
950
945
|
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
951
|
-
|
|
946
|
+
AnalyticsTooltipCard,
|
|
952
947
|
{
|
|
948
|
+
variant: "compact",
|
|
953
949
|
title: String(
|
|
954
|
-
isDailyOrderRow((_a2 = payload[0]) == null ? void 0 : _a2.payload) ? ((_b2 = payload[0]) == null ? void 0 : _b2.payload.label) ?? formatChartLabel(String(label)) : label
|
|
950
|
+
isDailyOrderRow((_a2 = payload[0]) == null ? void 0 : _a2.payload) ? ((_b2 = payload[0]) == null ? void 0 : _b2.payload.label) ?? formatChartLabel$2(String(label)) : label
|
|
955
951
|
),
|
|
956
952
|
children: [
|
|
957
953
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
958
954
|
"This:",
|
|
959
955
|
" ",
|
|
960
|
-
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCompactCurrency(
|
|
956
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCompactCurrency$1(
|
|
961
957
|
Number(
|
|
962
958
|
(_c2 = payload.find((p) => p.dataKey === "total_revenue")) == null ? void 0 : _c2.value
|
|
963
959
|
) || 0
|
|
@@ -966,9 +962,9 @@ function OrdersDashboard() {
|
|
|
966
962
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
967
963
|
"Previous:",
|
|
968
964
|
" ",
|
|
969
|
-
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCompactCurrency(
|
|
965
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCompactCurrency$1(
|
|
970
966
|
Number(
|
|
971
|
-
(
|
|
967
|
+
(_d = payload.find((p) => p.dataKey === "prev_revenue")) == null ? void 0 : _d.value
|
|
972
968
|
) || 0
|
|
973
969
|
) })
|
|
974
970
|
] })
|
|
@@ -1026,7 +1022,7 @@ function OrdersDashboard() {
|
|
|
1026
1022
|
recharts.YAxis,
|
|
1027
1023
|
{
|
|
1028
1024
|
width: 28,
|
|
1029
|
-
tick: CHART_AXIS_TICK_SM,
|
|
1025
|
+
tick: CHART_AXIS_TICK_SM$1,
|
|
1030
1026
|
allowDecimals: false
|
|
1031
1027
|
}
|
|
1032
1028
|
),
|
|
@@ -1035,7 +1031,7 @@ function OrdersDashboard() {
|
|
|
1035
1031
|
{
|
|
1036
1032
|
content: ({ active, payload, label }) => {
|
|
1037
1033
|
var _a2, _b2;
|
|
1038
|
-
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1034
|
+
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { variant: "compact", title: String(label), children: [
|
|
1039
1035
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1040
1036
|
"This:",
|
|
1041
1037
|
" ",
|
|
@@ -1106,8 +1102,8 @@ function OrdersDashboard() {
|
|
|
1106
1102
|
recharts.YAxis,
|
|
1107
1103
|
{
|
|
1108
1104
|
width: 36,
|
|
1109
|
-
tick: CHART_AXIS_TICK_SM,
|
|
1110
|
-
tickFormatter: (v) => formatCompactCurrency(Number(v))
|
|
1105
|
+
tick: CHART_AXIS_TICK_SM$1,
|
|
1106
|
+
tickFormatter: (v) => formatCompactCurrency$1(Number(v))
|
|
1111
1107
|
}
|
|
1112
1108
|
),
|
|
1113
1109
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1115,7 +1111,7 @@ function OrdersDashboard() {
|
|
|
1115
1111
|
{
|
|
1116
1112
|
content: ({ active, payload, label }) => {
|
|
1117
1113
|
var _a2, _b2;
|
|
1118
|
-
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1114
|
+
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { variant: "compact", title: String(label), children: [
|
|
1119
1115
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1120
1116
|
"This:",
|
|
1121
1117
|
" ",
|
|
@@ -1186,26 +1182,26 @@ function OrdersDashboard() {
|
|
|
1186
1182
|
recharts.XAxis,
|
|
1187
1183
|
{
|
|
1188
1184
|
dataKey: "date",
|
|
1189
|
-
tick: CHART_AXIS_TICK,
|
|
1185
|
+
tick: CHART_AXIS_TICK$2,
|
|
1190
1186
|
tickLine: false,
|
|
1191
1187
|
axisLine: false,
|
|
1192
1188
|
tickFormatter: (value, index) => {
|
|
1193
1189
|
const row = dailyOrders[index];
|
|
1194
|
-
return (row == null ? void 0 : row.label) ?? formatChartLabel(value);
|
|
1190
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel$2(value);
|
|
1195
1191
|
}
|
|
1196
1192
|
}
|
|
1197
1193
|
),
|
|
1198
1194
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1199
1195
|
recharts.YAxis,
|
|
1200
1196
|
{
|
|
1201
|
-
tick: CHART_AXIS_TICK,
|
|
1202
|
-
tickFormatter: (v) => formatCompactCurrency(Number(v))
|
|
1197
|
+
tick: CHART_AXIS_TICK$2,
|
|
1198
|
+
tickFormatter: (v) => formatCompactCurrency$1(Number(v))
|
|
1203
1199
|
}
|
|
1204
1200
|
),
|
|
1205
1201
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1206
1202
|
recharts.Tooltip,
|
|
1207
1203
|
{
|
|
1208
|
-
content: ({ active, payload, label }) => active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1204
|
+
content: ({ active, payload, label }) => active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTooltipCard, { variant: "compact", title: String(label), children: payload.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1209
1205
|
p.name,
|
|
1210
1206
|
":",
|
|
1211
1207
|
" ",
|
|
@@ -1261,7 +1257,7 @@ function OrdersDashboard() {
|
|
|
1261
1257
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-1.5", children: [
|
|
1262
1258
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0", children: [
|
|
1263
1259
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Trend overview" }),
|
|
1264
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-sm font-semibold", children: ((
|
|
1260
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-sm font-semibold", children: ((_b = OVER_TIME_PERIODS.find((period) => period.value === overTimePeriod)) == null ? void 0 : _b.label) ?? "One week" })
|
|
1265
1261
|
] }),
|
|
1266
1262
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap justify-end gap-2 text-[10px] text-ui-fg-muted", children: [
|
|
1267
1263
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
@@ -1318,12 +1314,12 @@ function OrdersDashboard() {
|
|
|
1318
1314
|
recharts.XAxis,
|
|
1319
1315
|
{
|
|
1320
1316
|
dataKey: "date",
|
|
1321
|
-
tick: CHART_AXIS_TICK,
|
|
1317
|
+
tick: CHART_AXIS_TICK$2,
|
|
1322
1318
|
tickLine: false,
|
|
1323
1319
|
axisLine: false,
|
|
1324
1320
|
tickFormatter: (value, index) => {
|
|
1325
1321
|
const row = dailyOrders[index];
|
|
1326
|
-
return (row == null ? void 0 : row.label) ?? formatChartLabel(value);
|
|
1322
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel$2(value);
|
|
1327
1323
|
}
|
|
1328
1324
|
}
|
|
1329
1325
|
),
|
|
@@ -1332,7 +1328,7 @@ function OrdersDashboard() {
|
|
|
1332
1328
|
{
|
|
1333
1329
|
yAxisId: "orders",
|
|
1334
1330
|
width: 32,
|
|
1335
|
-
tick: CHART_AXIS_TICK,
|
|
1331
|
+
tick: CHART_AXIS_TICK$2,
|
|
1336
1332
|
tickLine: false,
|
|
1337
1333
|
axisLine: false,
|
|
1338
1334
|
allowDecimals: false,
|
|
@@ -1345,10 +1341,10 @@ function OrdersDashboard() {
|
|
|
1345
1341
|
yAxisId: "revenue",
|
|
1346
1342
|
orientation: "right",
|
|
1347
1343
|
width: 40,
|
|
1348
|
-
tick: CHART_AXIS_TICK,
|
|
1344
|
+
tick: CHART_AXIS_TICK$2,
|
|
1349
1345
|
tickLine: false,
|
|
1350
1346
|
axisLine: false,
|
|
1351
|
-
tickFormatter: (value) => formatCompactCurrency(Number(value) || 0)
|
|
1347
|
+
tickFormatter: (value) => formatCompactCurrency$1(Number(value) || 0)
|
|
1352
1348
|
}
|
|
1353
1349
|
),
|
|
1354
1350
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1487,7 +1483,7 @@ function OrdersDashboard() {
|
|
|
1487
1483
|
{
|
|
1488
1484
|
variant: "atlas",
|
|
1489
1485
|
title: "Order → fulfillment funnel",
|
|
1490
|
-
description: `Stages by order created date (UTC), same range as Revenue & orders (${((
|
|
1486
|
+
description: `Stages by order created date (UTC), same range as Revenue & orders (${((_c = OVER_TIME_PERIODS.find((p) => p.value === overTimePeriod)) == null ? void 0 : _c.label) ?? "period"}). Not storefront visitors.`,
|
|
1491
1487
|
children: overTimeLoading ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[120px] items-center justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) }) : overTimeError ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[120px] items-center justify-center px-2 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: overTimeError }) }) }) : funnelTimeSeriesRows.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
1492
1488
|
/* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
|
|
1493
1489
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Stage counts (time series)" }),
|
|
@@ -1508,21 +1504,21 @@ function OrdersDashboard() {
|
|
|
1508
1504
|
recharts.XAxis,
|
|
1509
1505
|
{
|
|
1510
1506
|
dataKey: "date",
|
|
1511
|
-
tick: CHART_AXIS_TICK_SM,
|
|
1512
|
-
stroke: CHART_AXIS_LINE,
|
|
1513
|
-
tickLine: { stroke: CHART_AXIS_LINE },
|
|
1507
|
+
tick: CHART_AXIS_TICK_SM$1,
|
|
1508
|
+
stroke: CHART_AXIS_LINE$2,
|
|
1509
|
+
tickLine: { stroke: CHART_AXIS_LINE$2 },
|
|
1514
1510
|
tickFormatter: (value) => {
|
|
1515
1511
|
const row = dailyOrders.find((r) => r.date === value);
|
|
1516
|
-
return (row == null ? void 0 : row.label) ?? formatChartLabel(String(value));
|
|
1512
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel$2(String(value));
|
|
1517
1513
|
}
|
|
1518
1514
|
}
|
|
1519
1515
|
),
|
|
1520
1516
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1521
1517
|
recharts.YAxis,
|
|
1522
1518
|
{
|
|
1523
|
-
tick: CHART_AXIS_TICK_SM,
|
|
1524
|
-
stroke: CHART_AXIS_LINE,
|
|
1525
|
-
tickLine: { stroke: CHART_AXIS_LINE },
|
|
1519
|
+
tick: CHART_AXIS_TICK_SM$1,
|
|
1520
|
+
stroke: CHART_AXIS_LINE$2,
|
|
1521
|
+
tickLine: { stroke: CHART_AXIS_LINE$2 },
|
|
1526
1522
|
width: 28,
|
|
1527
1523
|
allowDecimals: false,
|
|
1528
1524
|
tickFormatter: (v) => Math.floor(Number(v) || 0).toLocaleString()
|
|
@@ -1534,9 +1530,10 @@ function OrdersDashboard() {
|
|
|
1534
1530
|
content: ({ active, payload, label }) => {
|
|
1535
1531
|
var _a2;
|
|
1536
1532
|
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1537
|
-
|
|
1533
|
+
AnalyticsTooltipCard,
|
|
1538
1534
|
{
|
|
1539
|
-
|
|
1535
|
+
variant: "compact",
|
|
1536
|
+
title: typeof label === "string" ? ((_a2 = dailyOrders.find((r) => r.date === label)) == null ? void 0 : _a2.label) ?? formatChartLabel$2(label) : String(label),
|
|
1540
1537
|
children: payload.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1541
1538
|
String(p.name),
|
|
1542
1539
|
":",
|
|
@@ -1623,12 +1620,12 @@ function OrdersDashboard() {
|
|
|
1623
1620
|
recharts.XAxis,
|
|
1624
1621
|
{
|
|
1625
1622
|
dataKey: "date",
|
|
1626
|
-
tick: CHART_AXIS_TICK_SM,
|
|
1627
|
-
stroke: CHART_AXIS_LINE,
|
|
1628
|
-
tickLine: { stroke: CHART_AXIS_LINE },
|
|
1623
|
+
tick: CHART_AXIS_TICK_SM$1,
|
|
1624
|
+
stroke: CHART_AXIS_LINE$2,
|
|
1625
|
+
tickLine: { stroke: CHART_AXIS_LINE$2 },
|
|
1629
1626
|
tickFormatter: (value) => {
|
|
1630
1627
|
const row = dailyOrders.find((r) => r.date === value);
|
|
1631
|
-
return (row == null ? void 0 : row.label) ?? formatChartLabel(String(value));
|
|
1628
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel$2(String(value));
|
|
1632
1629
|
}
|
|
1633
1630
|
}
|
|
1634
1631
|
),
|
|
@@ -1636,9 +1633,9 @@ function OrdersDashboard() {
|
|
|
1636
1633
|
recharts.YAxis,
|
|
1637
1634
|
{
|
|
1638
1635
|
domain: [0, 100],
|
|
1639
|
-
tick: CHART_AXIS_TICK_SM,
|
|
1640
|
-
stroke: CHART_AXIS_LINE,
|
|
1641
|
-
tickLine: { stroke: CHART_AXIS_LINE },
|
|
1636
|
+
tick: CHART_AXIS_TICK_SM$1,
|
|
1637
|
+
stroke: CHART_AXIS_LINE$2,
|
|
1638
|
+
tickLine: { stroke: CHART_AXIS_LINE$2 },
|
|
1642
1639
|
width: 36,
|
|
1643
1640
|
tickFormatter: (v) => `${Math.round(Number(v) || 0)}%`
|
|
1644
1641
|
}
|
|
@@ -1649,9 +1646,10 @@ function OrdersDashboard() {
|
|
|
1649
1646
|
content: ({ active, payload, label }) => {
|
|
1650
1647
|
var _a2;
|
|
1651
1648
|
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1652
|
-
|
|
1649
|
+
AnalyticsTooltipCard,
|
|
1653
1650
|
{
|
|
1654
|
-
|
|
1651
|
+
variant: "compact",
|
|
1652
|
+
title: typeof label === "string" ? ((_a2 = dailyOrders.find((r) => r.date === label)) == null ? void 0 : _a2.label) ?? formatChartLabel$2(label) : String(label),
|
|
1655
1653
|
children: payload.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1656
1654
|
String(p.name),
|
|
1657
1655
|
":",
|
|
@@ -1709,7 +1707,7 @@ function OrdersDashboard() {
|
|
|
1709
1707
|
) }) })
|
|
1710
1708
|
] })
|
|
1711
1709
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1712
|
-
EmptyAnalyticsPanel,
|
|
1710
|
+
EmptyAnalyticsPanel$1,
|
|
1713
1711
|
{
|
|
1714
1712
|
title: "No funnel data",
|
|
1715
1713
|
description: "No orders in this range to chart."
|
|
@@ -1724,7 +1722,7 @@ function OrdersDashboard() {
|
|
|
1724
1722
|
title: "Order revenue (attribution)",
|
|
1725
1723
|
description: "All revenue is from captured Medusa orders until storefront channel data exists.",
|
|
1726
1724
|
children: insightsLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) : insightsError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center px-2 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: insightsError }) }) : ordersInsights && ordersInsights.traffic.length > 0 ? trafficTotalRevenue <= 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted px-0.5 text-center text-[11px] leading-snug", children: "No recorded revenue in this window (totals may be zero until orders are paid or finalized)." }) : /* @__PURE__ */ jsxRuntime.jsx(TrafficRevenueDonut, { traffic: ordersInsights.traffic }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1727
|
-
EmptyAnalyticsPanel,
|
|
1725
|
+
EmptyAnalyticsPanel$1,
|
|
1728
1726
|
{
|
|
1729
1727
|
title: "No order revenue",
|
|
1730
1728
|
description: "No orders in this window."
|
|
@@ -1773,13 +1771,13 @@ function OrdersDashboard() {
|
|
|
1773
1771
|
recharts.XAxis,
|
|
1774
1772
|
{
|
|
1775
1773
|
dataKey: "date",
|
|
1776
|
-
tick: CHART_AXIS_TICK,
|
|
1774
|
+
tick: CHART_AXIS_TICK$2,
|
|
1777
1775
|
tickLine: false,
|
|
1778
1776
|
axisLine: false,
|
|
1779
1777
|
tickFormatter: (value) => {
|
|
1780
1778
|
const dateStr = typeof value === "string" ? value : String(value);
|
|
1781
1779
|
const row = dailyOrders.find((d) => d.date === dateStr);
|
|
1782
|
-
return (row == null ? void 0 : row.label) ?? formatChartLabel(dateStr);
|
|
1780
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel$2(dateStr);
|
|
1783
1781
|
}
|
|
1784
1782
|
}
|
|
1785
1783
|
),
|
|
@@ -1787,7 +1785,7 @@ function OrdersDashboard() {
|
|
|
1787
1785
|
recharts.YAxis,
|
|
1788
1786
|
{
|
|
1789
1787
|
width: 28,
|
|
1790
|
-
tick: CHART_AXIS_TICK,
|
|
1788
|
+
tick: CHART_AXIS_TICK$2,
|
|
1791
1789
|
tickLine: false,
|
|
1792
1790
|
axisLine: false,
|
|
1793
1791
|
allowDecimals: false,
|
|
@@ -1898,17 +1896,17 @@ function OrdersDashboard() {
|
|
|
1898
1896
|
recharts.XAxis,
|
|
1899
1897
|
{
|
|
1900
1898
|
dataKey: "date",
|
|
1901
|
-
tick: CHART_AXIS_TICK,
|
|
1899
|
+
tick: CHART_AXIS_TICK$2,
|
|
1902
1900
|
tickLine: false,
|
|
1903
1901
|
axisLine: false,
|
|
1904
|
-
tickFormatter: formatChartLabel
|
|
1902
|
+
tickFormatter: formatChartLabel$2
|
|
1905
1903
|
}
|
|
1906
1904
|
),
|
|
1907
1905
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1908
1906
|
recharts.YAxis,
|
|
1909
1907
|
{
|
|
1910
1908
|
width: 28,
|
|
1911
|
-
tick: CHART_AXIS_TICK,
|
|
1909
|
+
tick: CHART_AXIS_TICK$2,
|
|
1912
1910
|
tickLine: false,
|
|
1913
1911
|
axisLine: false,
|
|
1914
1912
|
allowDecimals: false,
|
|
@@ -1997,15 +1995,15 @@ function OrdersDashboard() {
|
|
|
1997
1995
|
recharts.XAxis,
|
|
1998
1996
|
{
|
|
1999
1997
|
dataKey: "label",
|
|
2000
|
-
tick: CHART_AXIS_TICK
|
|
1998
|
+
tick: CHART_AXIS_TICK$2
|
|
2001
1999
|
}
|
|
2002
2000
|
),
|
|
2003
2001
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2004
2002
|
recharts.YAxis,
|
|
2005
2003
|
{
|
|
2006
2004
|
width: 40,
|
|
2007
|
-
tick: CHART_AXIS_TICK_SM,
|
|
2008
|
-
tickFormatter: (v) => formatCompactCurrency(Number(v))
|
|
2005
|
+
tick: CHART_AXIS_TICK_SM$1,
|
|
2006
|
+
tickFormatter: (v) => formatCompactCurrency$1(Number(v))
|
|
2009
2007
|
}
|
|
2010
2008
|
),
|
|
2011
2009
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2013,7 +2011,7 @@ function OrdersDashboard() {
|
|
|
2013
2011
|
{
|
|
2014
2012
|
content: ({ active, payload, label }) => {
|
|
2015
2013
|
var _a2, _b2;
|
|
2016
|
-
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2014
|
+
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { variant: "compact", title: String(label), children: [
|
|
2017
2015
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2018
2016
|
"Revenue:",
|
|
2019
2017
|
" ",
|
|
@@ -2040,7 +2038,7 @@ function OrdersDashboard() {
|
|
|
2040
2038
|
]
|
|
2041
2039
|
}
|
|
2042
2040
|
) }) }) }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2043
|
-
EmptyAnalyticsPanel,
|
|
2041
|
+
EmptyAnalyticsPanel$1,
|
|
2044
2042
|
{
|
|
2045
2043
|
title: "No weekday data",
|
|
2046
2044
|
description: "No orders in this window."
|
|
@@ -2081,7 +2079,7 @@ function OrdersDashboard() {
|
|
|
2081
2079
|
{
|
|
2082
2080
|
content: ({ active, payload }) => {
|
|
2083
2081
|
var _a2, _b2;
|
|
2084
|
-
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2082
|
+
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTooltipCard, { variant: "compact", title: String(((_a2 = payload[0]) == null ? void 0 : _a2.name) ?? ""), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2085
2083
|
"Count:",
|
|
2086
2084
|
" ",
|
|
2087
2085
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(
|
|
@@ -2103,7 +2101,7 @@ function OrdersDashboard() {
|
|
|
2103
2101
|
] }) }) }),
|
|
2104
2102
|
(ordersInsights == null ? void 0 : ordersInsights.drafts.available) === false ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted mt-0.5 px-0.5 text-[10px] leading-snug", children: "Draft totals need draft_order remote query access." }) : null
|
|
2105
2103
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2106
|
-
EmptyAnalyticsPanel,
|
|
2104
|
+
EmptyAnalyticsPanel$1,
|
|
2107
2105
|
{
|
|
2108
2106
|
title: "Nothing to compare",
|
|
2109
2107
|
description: "No placed orders and no draft rows in this snapshot."
|
|
@@ -2136,14 +2134,14 @@ function OrdersDashboard() {
|
|
|
2136
2134
|
{
|
|
2137
2135
|
dataKey: "label",
|
|
2138
2136
|
interval: 3,
|
|
2139
|
-
tick: CHART_AXIS_TICK_SM
|
|
2137
|
+
tick: CHART_AXIS_TICK_SM$1
|
|
2140
2138
|
}
|
|
2141
2139
|
),
|
|
2142
2140
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2143
2141
|
recharts.YAxis,
|
|
2144
2142
|
{
|
|
2145
2143
|
width: 28,
|
|
2146
|
-
tick: CHART_AXIS_TICK,
|
|
2144
|
+
tick: CHART_AXIS_TICK$2,
|
|
2147
2145
|
allowDecimals: false
|
|
2148
2146
|
}
|
|
2149
2147
|
),
|
|
@@ -2152,7 +2150,7 @@ function OrdersDashboard() {
|
|
|
2152
2150
|
{
|
|
2153
2151
|
content: ({ active, payload, label }) => {
|
|
2154
2152
|
var _a2, _b2;
|
|
2155
|
-
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2153
|
+
return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { variant: "compact", title: String(label), children: [
|
|
2156
2154
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2157
2155
|
"Orders:",
|
|
2158
2156
|
" ",
|
|
@@ -2179,7 +2177,7 @@ function OrdersDashboard() {
|
|
|
2179
2177
|
]
|
|
2180
2178
|
}
|
|
2181
2179
|
) }) }) }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2182
|
-
EmptyAnalyticsPanel,
|
|
2180
|
+
EmptyAnalyticsPanel$1,
|
|
2183
2181
|
{
|
|
2184
2182
|
title: "No hourly data",
|
|
2185
2183
|
description: "No orders in this window."
|
|
@@ -2191,6 +2189,41 @@ function OrdersDashboard() {
|
|
|
2191
2189
|
] })
|
|
2192
2190
|
] }) });
|
|
2193
2191
|
}
|
|
2192
|
+
const KPI_ICON_BG$1 = {
|
|
2193
|
+
green: "bg-emerald-500/20",
|
|
2194
|
+
blue: "bg-sky-500/20",
|
|
2195
|
+
purple: "bg-violet-500/20",
|
|
2196
|
+
amber: "bg-amber-500/20"
|
|
2197
|
+
};
|
|
2198
|
+
const KPI_ICONS$1 = {
|
|
2199
|
+
green: icons.UsersSolid,
|
|
2200
|
+
blue: icons.User,
|
|
2201
|
+
purple: icons.UserGroup,
|
|
2202
|
+
amber: icons.Trash
|
|
2203
|
+
};
|
|
2204
|
+
function AtlasKpiCard$1({
|
|
2205
|
+
label,
|
|
2206
|
+
value,
|
|
2207
|
+
helper,
|
|
2208
|
+
accent
|
|
2209
|
+
}) {
|
|
2210
|
+
const Icon = KPI_ICONS$1[accent];
|
|
2211
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-[88px] min-w-0 flex-col rounded-xl border border-ui-border-base/80 bg-ui-bg-subtle/25 p-3 shadow-sm", children: [
|
|
2212
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
2213
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "min-w-0 flex-1 text-left text-xs font-medium leading-snug text-ui-fg-base", children: label }),
|
|
2214
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2215
|
+
"div",
|
|
2216
|
+
{
|
|
2217
|
+
className: `flex h-7 w-7 shrink-0 items-center justify-center rounded-lg ${KPI_ICON_BG$1[accent]}`,
|
|
2218
|
+
"aria-hidden": true,
|
|
2219
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-3.5 w-3.5 text-ui-fg-base" })
|
|
2220
|
+
}
|
|
2221
|
+
)
|
|
2222
|
+
] }),
|
|
2223
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-lg font-semibold tabular-nums tracking-tight text-ui-fg-base sm:text-xl", children: value }),
|
|
2224
|
+
helper ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-[11px] leading-snug text-ui-fg-muted", children: helper }) : null
|
|
2225
|
+
] });
|
|
2226
|
+
}
|
|
2194
2227
|
function CustomersOverTimeTooltip({
|
|
2195
2228
|
active,
|
|
2196
2229
|
payload,
|
|
@@ -2202,32 +2235,16 @@ function CustomersOverTimeTooltip({
|
|
|
2202
2235
|
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : label);
|
|
2203
2236
|
const registered = Math.floor(((_b = payload.find((p) => p.name === "Registered")) == null ? void 0 : _b.value) ?? 0);
|
|
2204
2237
|
const guest = Math.floor(((_c = payload.find((p) => p.name === "Guest")) == null ? void 0 : _c.value) ?? 0);
|
|
2205
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2206
|
-
"div",
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
fontSize: "14px",
|
|
2216
|
-
lineHeight: 1.5
|
|
2217
|
-
},
|
|
2218
|
-
children: [
|
|
2219
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 600, marginBottom: "6px", color: "#111827" }, children: displayLabel }),
|
|
2220
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "#374151" }, children: [
|
|
2221
|
-
"Registered: ",
|
|
2222
|
-
/* @__PURE__ */ jsxRuntime.jsx("strong", { style: { color: "#1f2937" }, children: registered })
|
|
2223
|
-
] }),
|
|
2224
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "#374151" }, children: [
|
|
2225
|
-
"Guest: ",
|
|
2226
|
-
/* @__PURE__ */ jsxRuntime.jsx("strong", { style: { color: "#1f2937" }, children: guest })
|
|
2227
|
-
] })
|
|
2228
|
-
]
|
|
2229
|
-
}
|
|
2230
|
-
);
|
|
2238
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { title: String(displayLabel ?? ""), children: [
|
|
2239
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2240
|
+
"Registered: ",
|
|
2241
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: registered.toLocaleString() })
|
|
2242
|
+
] }),
|
|
2243
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2244
|
+
"Guest: ",
|
|
2245
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: guest.toLocaleString() })
|
|
2246
|
+
] })
|
|
2247
|
+
] });
|
|
2231
2248
|
}
|
|
2232
2249
|
function MixPieTooltip({
|
|
2233
2250
|
active,
|
|
@@ -2235,10 +2252,10 @@ function MixPieTooltip({
|
|
|
2235
2252
|
}) {
|
|
2236
2253
|
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
2237
2254
|
const row = payload[0];
|
|
2238
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
2239
|
-
|
|
2240
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2241
|
-
] });
|
|
2255
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTooltipCard, { variant: "compact", title: String(row.name ?? ""), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2256
|
+
"Count: ",
|
|
2257
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(Number(row.value) || 0).toLocaleString() })
|
|
2258
|
+
] }) });
|
|
2242
2259
|
}
|
|
2243
2260
|
const COUNT_DAY_PRESETS = [
|
|
2244
2261
|
{ value: "all", label: "All time" },
|
|
@@ -2254,7 +2271,17 @@ const GRAPH_PERIODS$1 = [
|
|
|
2254
2271
|
];
|
|
2255
2272
|
const REGISTERED_COLOR = "var(--medusa-color-ui-fg-interactive)";
|
|
2256
2273
|
const GUEST_COLOR = "#6B7280";
|
|
2274
|
+
const CHART_AXIS_TICK$1 = { fontSize: 10, fill: "#e5e7eb" };
|
|
2275
|
+
const CHART_AXIS_LINE$1 = "#94a3b8";
|
|
2276
|
+
const SELECT_CLASS_NAME$1 = "h-9 w-full min-w-[8rem] max-w-full cursor-pointer appearance-none rounded border border-ui-border-base bg-ui-bg-base py-1.5 pl-3 pr-8 text-xs text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive sm:w-auto sm:min-w-[132px] sm:max-w-[220px]";
|
|
2277
|
+
function formatChartLabel$1(value) {
|
|
2278
|
+
if (!value) return "";
|
|
2279
|
+
const parsed = new Date(value);
|
|
2280
|
+
if (Number.isNaN(parsed.getTime())) return value;
|
|
2281
|
+
return `${parsed.getUTCMonth() + 1}/${parsed.getUTCDate()}`;
|
|
2282
|
+
}
|
|
2257
2283
|
function CustomersDashboard() {
|
|
2284
|
+
var _a, _b;
|
|
2258
2285
|
const [data, setData] = react.useState(null);
|
|
2259
2286
|
const [loading, setLoading] = react.useState(true);
|
|
2260
2287
|
const [error, setError] = react.useState(null);
|
|
@@ -2324,6 +2351,7 @@ function CustomersDashboard() {
|
|
|
2324
2351
|
cancelled = true;
|
|
2325
2352
|
};
|
|
2326
2353
|
}, [countDays]);
|
|
2354
|
+
const selectedCountPeriodLabel = ((_a = COUNT_DAY_PRESETS.find((p) => p.value === countDays)) == null ? void 0 : _a.label) ?? "All time";
|
|
2327
2355
|
const guestRegisteredPieData = react.useMemo(() => {
|
|
2328
2356
|
const g = Math.max(0, Math.floor((data == null ? void 0 : data.guestCount) ?? 0));
|
|
2329
2357
|
const r = Math.max(0, Math.floor((data == null ? void 0 : data.registeredCount) ?? 0));
|
|
@@ -2332,11 +2360,50 @@ function CustomersDashboard() {
|
|
|
2332
2360
|
{ name: "Registered", value: r, key: "registered" }
|
|
2333
2361
|
].filter((s) => s.value > 0);
|
|
2334
2362
|
}, [data]);
|
|
2363
|
+
const seriesWindowTotals = react.useMemo(() => {
|
|
2364
|
+
let registered = 0;
|
|
2365
|
+
let guest = 0;
|
|
2366
|
+
for (const row of series) {
|
|
2367
|
+
registered += Math.max(0, Math.floor(row.registered_count));
|
|
2368
|
+
guest += Math.max(0, Math.floor(row.guest_count));
|
|
2369
|
+
}
|
|
2370
|
+
return {
|
|
2371
|
+
registered,
|
|
2372
|
+
guest,
|
|
2373
|
+
points: series.length
|
|
2374
|
+
};
|
|
2375
|
+
}, [series]);
|
|
2376
|
+
const primaryStats = data ? [
|
|
2377
|
+
{
|
|
2378
|
+
label: "Guest customers",
|
|
2379
|
+
value: Math.floor(data.guestCount).toLocaleString(),
|
|
2380
|
+
helper: selectedCountPeriodLabel,
|
|
2381
|
+
accent: "blue"
|
|
2382
|
+
},
|
|
2383
|
+
{
|
|
2384
|
+
label: "Registered customers",
|
|
2385
|
+
value: Math.floor(data.registeredCount).toLocaleString(),
|
|
2386
|
+
helper: selectedCountPeriodLabel,
|
|
2387
|
+
accent: "purple"
|
|
2388
|
+
},
|
|
2389
|
+
{
|
|
2390
|
+
label: "Overall customers",
|
|
2391
|
+
value: Math.floor(data.totalCount).toLocaleString(),
|
|
2392
|
+
helper: "Guest + registered",
|
|
2393
|
+
accent: "green"
|
|
2394
|
+
},
|
|
2395
|
+
{
|
|
2396
|
+
label: "Deleted accounts",
|
|
2397
|
+
value: (data.deletedAccountsCount ?? 0).toLocaleString(),
|
|
2398
|
+
helper: "Removed profiles",
|
|
2399
|
+
accent: "amber"
|
|
2400
|
+
}
|
|
2401
|
+
] : [];
|
|
2335
2402
|
if (loading) {
|
|
2336
2403
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2337
2404
|
"div",
|
|
2338
2405
|
{
|
|
2339
|
-
className: "flex items-center justify-center
|
|
2406
|
+
className: "flex min-h-[200px] items-center justify-center",
|
|
2340
2407
|
role: "status",
|
|
2341
2408
|
"aria-label": "Loading analytics",
|
|
2342
2409
|
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading…" })
|
|
@@ -2346,19 +2413,22 @@ function CustomersDashboard() {
|
|
|
2346
2413
|
if (error || !data) {
|
|
2347
2414
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: error ?? "Failed to load analytics" }) });
|
|
2348
2415
|
}
|
|
2349
|
-
return /* @__PURE__ */ jsxRuntime.
|
|
2416
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AnalyticsDashboardShell, { className: "w-full min-w-0 max-w-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full min-w-0 max-w-full overflow-hidden rounded-2xl border border-ui-border-base/80 bg-ui-bg-base shadow-sm", children: [
|
|
2350
2417
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2351
2418
|
AnalyticsDashboardHeader,
|
|
2352
2419
|
{
|
|
2420
|
+
variant: "premium",
|
|
2421
|
+
appearance: "inset",
|
|
2422
|
+
actionsBare: true,
|
|
2353
2423
|
title: "Customers",
|
|
2354
|
-
description: "
|
|
2424
|
+
description: "Acquisition mix, repeat behavior, and signups over time — aligned with Orders and Products analytics.",
|
|
2355
2425
|
actions: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2356
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2426
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
2357
2427
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2358
2428
|
ui.Label,
|
|
2359
2429
|
{
|
|
2360
2430
|
htmlFor: "customers-count-period",
|
|
2361
|
-
className: "text-ui-fg-muted text-
|
|
2431
|
+
className: "shrink-0 text-ui-fg-muted text-xs font-medium",
|
|
2362
2432
|
children: "Period"
|
|
2363
2433
|
}
|
|
2364
2434
|
),
|
|
@@ -2368,214 +2438,245 @@ function CustomersDashboard() {
|
|
|
2368
2438
|
id: "customers-count-period",
|
|
2369
2439
|
value: countDays,
|
|
2370
2440
|
onChange: (e) => setCountDays(e.target.value),
|
|
2371
|
-
className:
|
|
2441
|
+
className: SELECT_CLASS_NAME$1,
|
|
2372
2442
|
children: COUNT_DAY_PRESETS.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.value, children: p.label }, p.value))
|
|
2373
2443
|
}
|
|
2374
2444
|
)
|
|
2375
2445
|
] }),
|
|
2376
|
-
countDays !== "all"
|
|
2446
|
+
countDays !== "all" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2377
2447
|
"button",
|
|
2378
2448
|
{
|
|
2379
2449
|
type: "button",
|
|
2380
2450
|
onClick: () => setCountDays("all"),
|
|
2381
|
-
className: "text-xs text-ui-fg-muted hover:text-ui-fg-base
|
|
2451
|
+
className: "text-xs text-ui-fg-muted transition-colors hover:text-ui-fg-base",
|
|
2382
2452
|
"aria-label": "Show all customers (clear filter)",
|
|
2383
|
-
children: "Clear
|
|
2453
|
+
children: "Clear period"
|
|
2384
2454
|
}
|
|
2385
|
-
)
|
|
2455
|
+
) : null
|
|
2386
2456
|
] })
|
|
2387
2457
|
}
|
|
2388
2458
|
),
|
|
2389
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
2390
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2391
|
-
|
|
2392
|
-
{
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
label
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2459
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 p-2 pt-0 sm:p-3 sm:pt-0 md:p-4 md:pt-0", children: [
|
|
2460
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
2461
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Customer overview" }) }),
|
|
2462
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2 lg:grid-cols-4", children: primaryStats.map((stat) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2463
|
+
AtlasKpiCard$1,
|
|
2464
|
+
{
|
|
2465
|
+
label: stat.label,
|
|
2466
|
+
value: stat.value,
|
|
2467
|
+
helper: stat.helper,
|
|
2468
|
+
accent: stat.accent
|
|
2469
|
+
},
|
|
2470
|
+
stat.label
|
|
2471
|
+
)) })
|
|
2472
|
+
] }),
|
|
2473
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 grid grid-cols-12 gap-2 lg:items-stretch", children: [
|
|
2474
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-12 flex min-h-0 min-w-0 lg:col-span-5", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2475
|
+
AnalyticsSection,
|
|
2476
|
+
{
|
|
2477
|
+
variant: "atlas",
|
|
2478
|
+
className: "flex min-h-0 w-full flex-col",
|
|
2479
|
+
title: "Guest vs registered",
|
|
2480
|
+
description: "Mix for the selected period (same filter as overview).",
|
|
2481
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", className: "min-h-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(168px,22vh)] min-h-[140px] w-full sm:h-[min(176px,24vh)]", children: guestRegisteredPieData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-center text-ui-fg-muted text-[11px] leading-snug", children: "No guest or registered customers in this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.PieChart, { margin: { top: 2, right: 2, left: 2, bottom: 2 }, children: [
|
|
2482
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2483
|
+
recharts.Pie,
|
|
2484
|
+
{
|
|
2485
|
+
data: guestRegisteredPieData,
|
|
2486
|
+
dataKey: "value",
|
|
2487
|
+
nameKey: "name",
|
|
2488
|
+
cx: "50%",
|
|
2489
|
+
cy: "48%",
|
|
2490
|
+
innerRadius: 34,
|
|
2491
|
+
outerRadius: 54,
|
|
2492
|
+
paddingAngle: 2,
|
|
2493
|
+
children: guestRegisteredPieData.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2494
|
+
recharts.Cell,
|
|
2495
|
+
{
|
|
2496
|
+
fill: entry.key === "guest" ? GUEST_COLOR : REGISTERED_COLOR
|
|
2497
|
+
},
|
|
2498
|
+
entry.key
|
|
2499
|
+
))
|
|
2500
|
+
}
|
|
2501
|
+
),
|
|
2502
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(MixPieTooltip, {}) }),
|
|
2503
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2504
|
+
recharts.Legend,
|
|
2505
|
+
{
|
|
2506
|
+
wrapperStyle: { fontSize: 9, paddingTop: 0 },
|
|
2507
|
+
verticalAlign: "bottom",
|
|
2508
|
+
height: 22,
|
|
2509
|
+
iconType: "circle"
|
|
2510
|
+
}
|
|
2511
|
+
)
|
|
2512
|
+
] }) }) }) })
|
|
2513
|
+
}
|
|
2514
|
+
) }),
|
|
2515
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-12 flex min-h-0 min-w-0 lg:col-span-7", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2516
|
+
AnalyticsSection,
|
|
2517
|
+
{
|
|
2518
|
+
variant: "atlas",
|
|
2519
|
+
className: "flex min-h-0 w-full flex-col",
|
|
2520
|
+
title: "Repeat purchases",
|
|
2521
|
+
description: "Orders with a customer_id in the same window (guest checkouts excluded from rate).",
|
|
2522
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-0 flex-1 flex-col gap-1.5", children: repeatLoading ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", className: "flex flex-1 items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) : repeatError ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", className: "flex flex-1 items-center justify-center px-2 py-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-center text-ui-fg-danger text-xs", children: repeatError }) }) : repeatStats ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-1.5 sm:grid-cols-3", children: [
|
|
2523
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
2524
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Repeat rate" }),
|
|
2525
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: `${repeatStats.repeat_rate_percent.toFixed(1)}%` }),
|
|
2526
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] leading-snug", children: repeatStats.window_days >= 365 ? "Approx. 12-month window" : `Last ${repeatStats.window_days} days` })
|
|
2527
|
+
] }) }),
|
|
2528
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
2529
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "With orders" }),
|
|
2530
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: repeatStats.customers_with_orders.toLocaleString() }),
|
|
2531
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] leading-snug", children: "Customers in window" })
|
|
2532
|
+
] }) }),
|
|
2533
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
2534
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Repeat (2+ orders)" }),
|
|
2535
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: repeatStats.repeat_customers.toLocaleString() }),
|
|
2536
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] leading-snug", children: "Multi-order customers" })
|
|
2537
|
+
] }) })
|
|
2538
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", className: "flex flex-1 items-center justify-center py-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "No data." }) }) })
|
|
2539
|
+
}
|
|
2540
|
+
) })
|
|
2541
|
+
] }),
|
|
2420
2542
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2421
2543
|
AnalyticsSection,
|
|
2422
2544
|
{
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2545
|
+
variant: "atlas",
|
|
2546
|
+
actionsBare: true,
|
|
2547
|
+
title: "Customers over time",
|
|
2548
|
+
description: "New registered vs guest activity for the chart range below.",
|
|
2549
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
2426
2550
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2427
|
-
|
|
2551
|
+
ui.Label,
|
|
2428
2552
|
{
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
cx: "50%",
|
|
2433
|
-
cy: "50%",
|
|
2434
|
-
innerRadius: 52,
|
|
2435
|
-
outerRadius: 88,
|
|
2436
|
-
paddingAngle: 2,
|
|
2437
|
-
children: guestRegisteredPieData.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2438
|
-
recharts.Cell,
|
|
2439
|
-
{
|
|
2440
|
-
fill: entry.key === "guest" ? GUEST_COLOR : REGISTERED_COLOR
|
|
2441
|
-
},
|
|
2442
|
-
entry.key
|
|
2443
|
-
))
|
|
2553
|
+
htmlFor: "customers-graph-period",
|
|
2554
|
+
className: "shrink-0 text-ui-fg-muted text-xs font-medium",
|
|
2555
|
+
children: "Range"
|
|
2444
2556
|
}
|
|
2445
2557
|
),
|
|
2446
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(MixPieTooltip, {}) }),
|
|
2447
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { wrapperStyle: { fontSize: 12 }, iconType: "circle" })
|
|
2448
|
-
] }) }) }) })
|
|
2449
|
-
}
|
|
2450
|
-
),
|
|
2451
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2452
|
-
AnalyticsSection,
|
|
2453
|
-
{
|
|
2454
|
-
title: "Repeat purchases",
|
|
2455
|
-
description: "Orders with a customer_id in the same window as the period filter (guest checkouts excluded from rate).",
|
|
2456
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: repeatLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[200px] items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Loading…" }) }) : repeatError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[200px] items-center justify-center px-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-sm", children: repeatError }) }) : repeatStats ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
|
|
2457
2558
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2458
|
-
|
|
2559
|
+
"select",
|
|
2459
2560
|
{
|
|
2460
|
-
|
|
2461
|
-
value:
|
|
2462
|
-
|
|
2561
|
+
id: "customers-graph-period",
|
|
2562
|
+
value: graphPeriod,
|
|
2563
|
+
onChange: (e) => setGraphPeriod(e.target.value),
|
|
2564
|
+
className: SELECT_CLASS_NAME$1,
|
|
2565
|
+
children: GRAPH_PERIODS$1.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.value, children: p.label }, p.value))
|
|
2463
2566
|
}
|
|
2464
|
-
)
|
|
2465
|
-
|
|
2466
|
-
|
|
2567
|
+
)
|
|
2568
|
+
] }),
|
|
2569
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
2570
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-1.5 sm:grid-cols-3", children: [
|
|
2571
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
2572
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window registered (sum)" }),
|
|
2573
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: seriesWindowTotals.registered.toLocaleString() }),
|
|
2574
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: [
|
|
2575
|
+
"Across ",
|
|
2576
|
+
seriesWindowTotals.points,
|
|
2577
|
+
" buckets"
|
|
2578
|
+
] })
|
|
2579
|
+
] }) }),
|
|
2580
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
2581
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window guest (sum)" }),
|
|
2582
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: seriesWindowTotals.guest.toLocaleString() }),
|
|
2583
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: "Same range as chart" })
|
|
2584
|
+
] }) }),
|
|
2585
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
2586
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Chart period" }),
|
|
2587
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: ((_b = GRAPH_PERIODS$1.find((p) => p.value === graphPeriod)) == null ? void 0 : _b.label) ?? graphPeriod }),
|
|
2588
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] leading-snug", children: "Independent of summary period filter" })
|
|
2589
|
+
] }) })
|
|
2590
|
+
] }),
|
|
2591
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[212px] w-full sm:h-[228px]", children: graphLoading ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2592
|
+
"div",
|
|
2467
2593
|
{
|
|
2468
|
-
|
|
2469
|
-
|
|
2594
|
+
className: "flex h-full items-center justify-center",
|
|
2595
|
+
role: "status",
|
|
2596
|
+
"aria-label": "Loading customers over time",
|
|
2597
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading chart…" })
|
|
2470
2598
|
}
|
|
2471
|
-
),
|
|
2472
|
-
|
|
2473
|
-
AnalyticsStatCard,
|
|
2599
|
+
) : graphError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-center text-ui-fg-danger text-xs", children: graphError }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2600
|
+
recharts.LineChart,
|
|
2474
2601
|
{
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
{
|
|
2554
|
-
type: "monotone",
|
|
2555
|
-
dataKey: "registered_count",
|
|
2556
|
-
name: "Registered",
|
|
2557
|
-
stroke: REGISTERED_COLOR,
|
|
2558
|
-
strokeWidth: 2,
|
|
2559
|
-
dot: { r: 3 }
|
|
2560
|
-
}
|
|
2561
|
-
),
|
|
2562
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2563
|
-
recharts.Line,
|
|
2564
|
-
{
|
|
2565
|
-
type: "monotone",
|
|
2566
|
-
dataKey: "guest_count",
|
|
2567
|
-
name: "Guest",
|
|
2568
|
-
stroke: GUEST_COLOR,
|
|
2569
|
-
strokeWidth: 2,
|
|
2570
|
-
dot: { r: 3 }
|
|
2571
|
-
}
|
|
2572
|
-
)
|
|
2573
|
-
]
|
|
2574
|
-
}
|
|
2575
|
-
) }) }) })
|
|
2576
|
-
}
|
|
2577
|
-
)
|
|
2578
|
-
] });
|
|
2602
|
+
data: series,
|
|
2603
|
+
margin: { top: 6, right: 8, left: 0, bottom: 4 },
|
|
2604
|
+
children: [
|
|
2605
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2606
|
+
recharts.CartesianGrid,
|
|
2607
|
+
{
|
|
2608
|
+
strokeDasharray: "3 3",
|
|
2609
|
+
vertical: false,
|
|
2610
|
+
stroke: "rgba(148,163,184,0.12)"
|
|
2611
|
+
}
|
|
2612
|
+
),
|
|
2613
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2614
|
+
recharts.XAxis,
|
|
2615
|
+
{
|
|
2616
|
+
dataKey: "date",
|
|
2617
|
+
tick: CHART_AXIS_TICK$1,
|
|
2618
|
+
stroke: CHART_AXIS_LINE$1,
|
|
2619
|
+
tickLine: false,
|
|
2620
|
+
axisLine: { stroke: CHART_AXIS_LINE$1 },
|
|
2621
|
+
tickFormatter: (_v, index) => {
|
|
2622
|
+
var _a2;
|
|
2623
|
+
const row = series[index];
|
|
2624
|
+
if (row == null ? void 0 : row.label) return row.label;
|
|
2625
|
+
const d = (_a2 = series[index]) == null ? void 0 : _a2.date;
|
|
2626
|
+
return formatChartLabel$1(d);
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
),
|
|
2630
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2631
|
+
recharts.YAxis,
|
|
2632
|
+
{
|
|
2633
|
+
width: 36,
|
|
2634
|
+
tick: CHART_AXIS_TICK$1,
|
|
2635
|
+
stroke: CHART_AXIS_LINE$1,
|
|
2636
|
+
tickLine: false,
|
|
2637
|
+
axisLine: { stroke: CHART_AXIS_LINE$1 },
|
|
2638
|
+
tickFormatter: (v) => String(Math.floor(Number(v))),
|
|
2639
|
+
allowDecimals: false
|
|
2640
|
+
}
|
|
2641
|
+
),
|
|
2642
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(CustomersOverTimeTooltip, {}) }),
|
|
2643
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2644
|
+
recharts.Legend,
|
|
2645
|
+
{
|
|
2646
|
+
wrapperStyle: { fontSize: 10, paddingTop: 4 },
|
|
2647
|
+
iconType: "circle"
|
|
2648
|
+
}
|
|
2649
|
+
),
|
|
2650
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2651
|
+
recharts.Line,
|
|
2652
|
+
{
|
|
2653
|
+
type: "monotone",
|
|
2654
|
+
dataKey: "registered_count",
|
|
2655
|
+
name: "Registered",
|
|
2656
|
+
stroke: REGISTERED_COLOR,
|
|
2657
|
+
strokeWidth: 2,
|
|
2658
|
+
dot: { r: 2 }
|
|
2659
|
+
}
|
|
2660
|
+
),
|
|
2661
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2662
|
+
recharts.Line,
|
|
2663
|
+
{
|
|
2664
|
+
type: "monotone",
|
|
2665
|
+
dataKey: "guest_count",
|
|
2666
|
+
name: "Guest",
|
|
2667
|
+
stroke: GUEST_COLOR,
|
|
2668
|
+
strokeWidth: 2,
|
|
2669
|
+
dot: { r: 2 }
|
|
2670
|
+
}
|
|
2671
|
+
)
|
|
2672
|
+
]
|
|
2673
|
+
}
|
|
2674
|
+
) }) }) })
|
|
2675
|
+
] })
|
|
2676
|
+
}
|
|
2677
|
+
)
|
|
2678
|
+
] })
|
|
2679
|
+
] }) });
|
|
2579
2680
|
}
|
|
2580
2681
|
const SUMMARY_PERIODS = [
|
|
2581
2682
|
{ value: "all", label: "All time" },
|
|
@@ -2595,10 +2696,32 @@ const TOP_SELLER_PERIODS = [
|
|
|
2595
2696
|
{ value: "year", label: "Year" },
|
|
2596
2697
|
{ value: "all", label: "All time" }
|
|
2597
2698
|
];
|
|
2598
|
-
const
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2699
|
+
const PRODUCT_CHART_COLORS = {
|
|
2700
|
+
units: "#38BDF8",
|
|
2701
|
+
views: "#C084FC",
|
|
2702
|
+
revenue: "#D946EF",
|
|
2703
|
+
revenueMuted: "#94A3B8",
|
|
2704
|
+
scatter: "#A855F7"
|
|
2705
|
+
};
|
|
2706
|
+
const KPI_ICON_BG = {
|
|
2707
|
+
green: "bg-emerald-500/20",
|
|
2708
|
+
blue: "bg-sky-500/20",
|
|
2709
|
+
purple: "bg-violet-500/20",
|
|
2710
|
+
amber: "bg-amber-500/20"
|
|
2711
|
+
};
|
|
2712
|
+
const KPI_ICONS = {
|
|
2713
|
+
green: icons.CurrencyDollar,
|
|
2714
|
+
blue: icons.ShoppingCart,
|
|
2715
|
+
purple: icons.Tag,
|
|
2716
|
+
amber: icons.CubeSolid
|
|
2717
|
+
};
|
|
2718
|
+
const CHART_AXIS_TICK = { fontSize: 10, fill: "#e5e7eb" };
|
|
2719
|
+
const CHART_AXIS_TICK_SM = { fontSize: 9, fill: "#e5e7eb" };
|
|
2720
|
+
const CHART_AXIS_LINE = "#94a3b8";
|
|
2721
|
+
const SELECT_CLASS_NAME = "h-9 w-full min-w-[8rem] max-w-full cursor-pointer appearance-none rounded border border-ui-border-base bg-ui-bg-base py-1.5 pl-3 pr-8 text-xs text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive sm:w-auto sm:min-w-[132px] sm:max-w-[220px]";
|
|
2722
|
+
const LEADERBOARD_TABLE_CLASS = "min-w-[32rem] sm:min-w-[36rem] max-w-none";
|
|
2723
|
+
const OPPORTUNITIES_TABLE_CLASS = "min-w-[30rem] sm:min-w-[34rem] max-w-none";
|
|
2724
|
+
const TABLE_HEAD_CELL_NOWRAP = "whitespace-nowrap";
|
|
2602
2725
|
function formatCurrency(value) {
|
|
2603
2726
|
return new Intl.NumberFormat("en-IN", {
|
|
2604
2727
|
style: "currency",
|
|
@@ -2607,12 +2730,39 @@ function formatCurrency(value) {
|
|
|
2607
2730
|
maximumFractionDigits: 0
|
|
2608
2731
|
}).format(value);
|
|
2609
2732
|
}
|
|
2733
|
+
function formatCompactCurrency(value) {
|
|
2734
|
+
return new Intl.NumberFormat("en-IN", {
|
|
2735
|
+
style: "currency",
|
|
2736
|
+
currency: "INR",
|
|
2737
|
+
notation: "compact",
|
|
2738
|
+
maximumFractionDigits: 1
|
|
2739
|
+
}).format(value);
|
|
2740
|
+
}
|
|
2741
|
+
function formatChartLabel(value) {
|
|
2742
|
+
if (!value) return "";
|
|
2743
|
+
const parsed = new Date(value);
|
|
2744
|
+
if (Number.isNaN(parsed.getTime())) return value;
|
|
2745
|
+
return `${parsed.getUTCMonth() + 1}/${parsed.getUTCDate()}`;
|
|
2746
|
+
}
|
|
2610
2747
|
function formatRatio(value) {
|
|
2611
2748
|
if (value === null || Number.isNaN(value)) return "N/A";
|
|
2612
2749
|
return `${value.toFixed(2)}x`;
|
|
2613
2750
|
}
|
|
2614
|
-
function
|
|
2615
|
-
return
|
|
2751
|
+
function formatShortNumber(value) {
|
|
2752
|
+
return new Intl.NumberFormat("en-IN", {
|
|
2753
|
+
notation: "compact",
|
|
2754
|
+
maximumFractionDigits: value < 10 ? 1 : 0
|
|
2755
|
+
}).format(value);
|
|
2756
|
+
}
|
|
2757
|
+
function topSellerPeriodToGraphPeriod(period) {
|
|
2758
|
+
if (period === "month") return "one_month";
|
|
2759
|
+
if (period === "year" || period === "all") return "one_year";
|
|
2760
|
+
return "one_week";
|
|
2761
|
+
}
|
|
2762
|
+
function summaryDaysToGraphPeriod(days) {
|
|
2763
|
+
if (days === "30") return "one_month";
|
|
2764
|
+
if (days === "all" || days === "90") return "one_year";
|
|
2765
|
+
return "one_week";
|
|
2616
2766
|
}
|
|
2617
2767
|
function buildAnalyticsUrl(path, params) {
|
|
2618
2768
|
const search = new URLSearchParams();
|
|
@@ -2624,48 +2774,71 @@ function buildAnalyticsUrl(path, params) {
|
|
|
2624
2774
|
const query = search.toString();
|
|
2625
2775
|
return query ? `${path}?${query}` : path;
|
|
2626
2776
|
}
|
|
2777
|
+
function isProductOverTimePoint(value) {
|
|
2778
|
+
if (typeof value !== "object" || value === null) return false;
|
|
2779
|
+
const v = value;
|
|
2780
|
+
return typeof v.date === "string" && typeof v.units_sold === "number" && typeof v.revenue === "number" && typeof v.views === "number";
|
|
2781
|
+
}
|
|
2627
2782
|
function TrendTooltip({
|
|
2628
2783
|
active,
|
|
2629
2784
|
payload,
|
|
2630
2785
|
label
|
|
2631
2786
|
}) {
|
|
2632
|
-
var _a, _b, _c, _d;
|
|
2787
|
+
var _a, _b, _c, _d, _e;
|
|
2633
2788
|
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
2634
|
-
const row = (_a = payload[0]) == null ? void 0 : _a.payload;
|
|
2789
|
+
const row = isProductOverTimePoint((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
2635
2790
|
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
2636
|
-
const unitsSold = ((
|
|
2637
|
-
const views = ((
|
|
2638
|
-
const revenue = ((
|
|
2639
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2640
|
-
"div",
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
);
|
|
2791
|
+
const unitsSold = ((_c = payload.find((entry) => entry.name === "Units sold")) == null ? void 0 : _c.value) ?? 0;
|
|
2792
|
+
const views = ((_d = payload.find((entry) => entry.name === "Views")) == null ? void 0 : _d.value) ?? 0;
|
|
2793
|
+
const revenue = ((_e = payload.find((entry) => entry.name === "Revenue")) == null ? void 0 : _e.value) ?? 0;
|
|
2794
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { title: displayLabel, children: [
|
|
2795
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2796
|
+
"Units sold: ",
|
|
2797
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(Number(unitsSold)).toLocaleString() })
|
|
2798
|
+
] }),
|
|
2799
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2800
|
+
"Views: ",
|
|
2801
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(Number(views)).toLocaleString() })
|
|
2802
|
+
] }),
|
|
2803
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2804
|
+
"Revenue: ",
|
|
2805
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency(Number(revenue)) })
|
|
2806
|
+
] })
|
|
2807
|
+
] });
|
|
2808
|
+
}
|
|
2809
|
+
function MiniTrendTooltip({
|
|
2810
|
+
active,
|
|
2811
|
+
payload,
|
|
2812
|
+
label,
|
|
2813
|
+
productHeadline,
|
|
2814
|
+
hideViewsRow,
|
|
2815
|
+
hideUnitsRow,
|
|
2816
|
+
hideRevenueRow
|
|
2817
|
+
}) {
|
|
2818
|
+
var _a, _b, _c, _d, _e;
|
|
2819
|
+
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
2820
|
+
const row = isProductOverTimePoint((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
2821
|
+
const labelKey = label !== void 0 && label !== null ? String(label) : "";
|
|
2822
|
+
const displayLabel = (row == null ? void 0 : row.label) ?? (labelKey ? new Date(labelKey).toLocaleDateString() : "");
|
|
2823
|
+
const unitsSold = ((_c = payload.find((entry) => entry.name === "Units sold")) == null ? void 0 : _c.value) ?? 0;
|
|
2824
|
+
const views = ((_d = payload.find((entry) => entry.name === "Views")) == null ? void 0 : _d.value) ?? 0;
|
|
2825
|
+
const revenue = ((_e = payload.find((entry) => entry.name === "Revenue")) == null ? void 0 : _e.value) ?? 0;
|
|
2826
|
+
const title = productHeadline ?? displayLabel;
|
|
2827
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { variant: "compact", title, children: [
|
|
2828
|
+
productHeadline ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-1 text-[10px] leading-tight opacity-80", children: displayLabel }) : null,
|
|
2829
|
+
!hideUnitsRow ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2830
|
+
"Units sold: ",
|
|
2831
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(Number(unitsSold)).toLocaleString() })
|
|
2832
|
+
] }) : null,
|
|
2833
|
+
!hideViewsRow ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2834
|
+
"Views: ",
|
|
2835
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(Number(views)).toLocaleString() })
|
|
2836
|
+
] }) : null,
|
|
2837
|
+
!hideRevenueRow ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2838
|
+
"Revenue: ",
|
|
2839
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency(Number(revenue)) })
|
|
2840
|
+
] }) : null
|
|
2841
|
+
] });
|
|
2669
2842
|
}
|
|
2670
2843
|
function ProductBarTooltip({
|
|
2671
2844
|
active,
|
|
@@ -2679,39 +2852,52 @@ function ProductBarTooltip({
|
|
|
2679
2852
|
const unitsSold = "units_sold" in row ? row.units_sold : 0;
|
|
2680
2853
|
const totalViews = "total_views" in row ? row.total_views : 0;
|
|
2681
2854
|
const revenue = "revenue" in row ? row.revenue : 0;
|
|
2682
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2683
|
-
"div",
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2855
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsTooltipCard, { variant: "compact", title: row.product_title, children: [
|
|
2856
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2857
|
+
"Units sold: ",
|
|
2858
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(unitsSold).toLocaleString() })
|
|
2859
|
+
] }),
|
|
2860
|
+
showViews ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2861
|
+
"Views: ",
|
|
2862
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(totalViews).toLocaleString() })
|
|
2863
|
+
] }) : null,
|
|
2864
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2865
|
+
"Revenue: ",
|
|
2866
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency(Number(revenue)) })
|
|
2867
|
+
] })
|
|
2868
|
+
] });
|
|
2869
|
+
}
|
|
2870
|
+
function AtlasKpiCard({
|
|
2871
|
+
label,
|
|
2872
|
+
value,
|
|
2873
|
+
helper,
|
|
2874
|
+
accent
|
|
2875
|
+
}) {
|
|
2876
|
+
const Icon = KPI_ICONS[accent];
|
|
2877
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-[88px] min-w-0 flex-col rounded-xl border border-ui-border-base/80 bg-ui-bg-subtle/25 p-3 shadow-sm", children: [
|
|
2878
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
2879
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "min-w-0 flex-1 text-left text-xs font-medium leading-snug text-ui-fg-base", children: label }),
|
|
2880
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2881
|
+
"div",
|
|
2882
|
+
{
|
|
2883
|
+
className: `flex h-7 w-7 shrink-0 items-center justify-center rounded-lg ${KPI_ICON_BG[accent]}`,
|
|
2884
|
+
"aria-hidden": true,
|
|
2885
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-3.5 w-3.5 text-ui-fg-base" })
|
|
2886
|
+
}
|
|
2887
|
+
)
|
|
2888
|
+
] }),
|
|
2889
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-lg font-semibold tabular-nums tracking-tight text-ui-fg-base sm:text-xl", children: value }),
|
|
2890
|
+
helper ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-[11px] leading-snug text-ui-fg-muted", children: helper }) : null
|
|
2891
|
+
] });
|
|
2892
|
+
}
|
|
2893
|
+
function EmptyAnalyticsPanel({ title, description }) {
|
|
2894
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-[88px] flex-col gap-1.5 rounded-lg border border-dashed border-ui-border-base px-3 py-3", children: [
|
|
2895
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-xs font-semibold", children: title }),
|
|
2896
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[11px] leading-snug", children: description })
|
|
2897
|
+
] });
|
|
2712
2898
|
}
|
|
2713
2899
|
function ProductsDashboard() {
|
|
2714
|
-
var _a, _b, _c;
|
|
2900
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
2715
2901
|
const [summaryDays, setSummaryDays] = react.useState("all");
|
|
2716
2902
|
const [graphPeriod, setGraphPeriod] = react.useState("one_week");
|
|
2717
2903
|
const [topSellerPeriod, setTopSellerPeriod] = react.useState("week");
|
|
@@ -2731,11 +2917,17 @@ function ProductsDashboard() {
|
|
|
2731
2917
|
const [performance, setPerformance] = react.useState(null);
|
|
2732
2918
|
const [performanceLoading, setPerformanceLoading] = react.useState(true);
|
|
2733
2919
|
const [performanceError, setPerformanceError] = react.useState(null);
|
|
2920
|
+
const [bestSellersTrend, setBestSellersTrend] = react.useState(null);
|
|
2921
|
+
const [bestSellersTrendLoading, setBestSellersTrendLoading] = react.useState(true);
|
|
2922
|
+
const [bestSellersTrendError, setBestSellersTrendError] = react.useState(null);
|
|
2923
|
+
const [mostViewedTrend, setMostViewedTrend] = react.useState(null);
|
|
2924
|
+
const [mostViewedTrendLoading, setMostViewedTrendLoading] = react.useState(true);
|
|
2925
|
+
const [mostViewedTrendError, setMostViewedTrendError] = react.useState(null);
|
|
2734
2926
|
react.useEffect(() => {
|
|
2735
2927
|
let cancelled = false;
|
|
2736
2928
|
setFiltersLoading(true);
|
|
2737
2929
|
setFiltersError(null);
|
|
2738
|
-
fetch("/admin/analytics/products-filters").then((res) => {
|
|
2930
|
+
fetch("/admin/analytics/products-filters", { credentials: "include" }).then((res) => {
|
|
2739
2931
|
if (!res.ok) throw new Error(res.statusText);
|
|
2740
2932
|
return res.json();
|
|
2741
2933
|
}).then((body) => {
|
|
@@ -2761,7 +2953,8 @@ function ProductsDashboard() {
|
|
|
2761
2953
|
buildAnalyticsUrl("/admin/analytics/products-summary", {
|
|
2762
2954
|
days: summaryDays,
|
|
2763
2955
|
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null
|
|
2764
|
-
})
|
|
2956
|
+
}),
|
|
2957
|
+
{ credentials: "include" }
|
|
2765
2958
|
).then((res) => {
|
|
2766
2959
|
if (!res.ok) throw new Error(res.statusText);
|
|
2767
2960
|
return res.json();
|
|
@@ -2786,7 +2979,8 @@ function ProductsDashboard() {
|
|
|
2786
2979
|
buildAnalyticsUrl("/admin/analytics/products-over-time", {
|
|
2787
2980
|
period: graphPeriod,
|
|
2788
2981
|
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null
|
|
2789
|
-
})
|
|
2982
|
+
}),
|
|
2983
|
+
{ credentials: "include" }
|
|
2790
2984
|
).then((res) => {
|
|
2791
2985
|
if (!res.ok) throw new Error(res.statusText);
|
|
2792
2986
|
return res.json();
|
|
@@ -2811,7 +3005,8 @@ function ProductsDashboard() {
|
|
|
2811
3005
|
buildAnalyticsUrl("/admin/analytics/products-top-sellers", {
|
|
2812
3006
|
period: topSellerPeriod,
|
|
2813
3007
|
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null
|
|
2814
|
-
})
|
|
3008
|
+
}),
|
|
3009
|
+
{ credentials: "include" }
|
|
2815
3010
|
).then((res) => {
|
|
2816
3011
|
if (!res.ok) throw new Error(res.statusText);
|
|
2817
3012
|
return res.json();
|
|
@@ -2828,6 +3023,126 @@ function ProductsDashboard() {
|
|
|
2828
3023
|
cancelled = true;
|
|
2829
3024
|
};
|
|
2830
3025
|
}, [salesChannelId, topSellerPeriod]);
|
|
3026
|
+
react.useEffect(() => {
|
|
3027
|
+
var _a2, _b2;
|
|
3028
|
+
let cancelled = false;
|
|
3029
|
+
if (topSellersLoading) {
|
|
3030
|
+
setBestSellersTrendLoading(true);
|
|
3031
|
+
return () => {
|
|
3032
|
+
cancelled = true;
|
|
3033
|
+
};
|
|
3034
|
+
}
|
|
3035
|
+
if (topSellersError) {
|
|
3036
|
+
setBestSellersTrendLoading(false);
|
|
3037
|
+
setBestSellersTrendError(topSellersError);
|
|
3038
|
+
setBestSellersTrend(null);
|
|
3039
|
+
return () => {
|
|
3040
|
+
cancelled = true;
|
|
3041
|
+
};
|
|
3042
|
+
}
|
|
3043
|
+
const topProductId = (_b2 = (_a2 = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.product_id;
|
|
3044
|
+
if (!topProductId) {
|
|
3045
|
+
setBestSellersTrendLoading(false);
|
|
3046
|
+
setBestSellersTrendError(null);
|
|
3047
|
+
setBestSellersTrend({
|
|
3048
|
+
series: [],
|
|
3049
|
+
productViewsConnected: (topSellers == null ? void 0 : topSellers.productViewsConnected) ?? false,
|
|
3050
|
+
product: null
|
|
3051
|
+
});
|
|
3052
|
+
return () => {
|
|
3053
|
+
cancelled = true;
|
|
3054
|
+
};
|
|
3055
|
+
}
|
|
3056
|
+
setBestSellersTrendLoading(true);
|
|
3057
|
+
setBestSellersTrendError(null);
|
|
3058
|
+
const period = topSellerPeriodToGraphPeriod(topSellerPeriod);
|
|
3059
|
+
fetch(
|
|
3060
|
+
buildAnalyticsUrl("/admin/analytics/products-over-time", {
|
|
3061
|
+
period,
|
|
3062
|
+
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null,
|
|
3063
|
+
product_id: topProductId
|
|
3064
|
+
}),
|
|
3065
|
+
{ credentials: "include" }
|
|
3066
|
+
).then((res) => {
|
|
3067
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
3068
|
+
return res.json();
|
|
3069
|
+
}).then((body) => {
|
|
3070
|
+
if (!cancelled) setBestSellersTrend(body);
|
|
3071
|
+
}).catch((error) => {
|
|
3072
|
+
if (!cancelled) {
|
|
3073
|
+
setBestSellersTrendError(error instanceof Error ? error.message : String(error));
|
|
3074
|
+
}
|
|
3075
|
+
}).finally(() => {
|
|
3076
|
+
if (!cancelled) setBestSellersTrendLoading(false);
|
|
3077
|
+
});
|
|
3078
|
+
return () => {
|
|
3079
|
+
cancelled = true;
|
|
3080
|
+
};
|
|
3081
|
+
}, [salesChannelId, topSellerPeriod, topSellersLoading, topSellersError, topSellers]);
|
|
3082
|
+
react.useEffect(() => {
|
|
3083
|
+
var _a2, _b2;
|
|
3084
|
+
let cancelled = false;
|
|
3085
|
+
if (performanceLoading) {
|
|
3086
|
+
setMostViewedTrendLoading(true);
|
|
3087
|
+
return () => {
|
|
3088
|
+
cancelled = true;
|
|
3089
|
+
};
|
|
3090
|
+
}
|
|
3091
|
+
if (performanceError) {
|
|
3092
|
+
setMostViewedTrendLoading(false);
|
|
3093
|
+
setMostViewedTrendError(performanceError);
|
|
3094
|
+
setMostViewedTrend(null);
|
|
3095
|
+
return () => {
|
|
3096
|
+
cancelled = true;
|
|
3097
|
+
};
|
|
3098
|
+
}
|
|
3099
|
+
if (!(performance == null ? void 0 : performance.productViewsConnected)) {
|
|
3100
|
+
setMostViewedTrendLoading(false);
|
|
3101
|
+
setMostViewedTrendError(null);
|
|
3102
|
+
setMostViewedTrend(null);
|
|
3103
|
+
return () => {
|
|
3104
|
+
cancelled = true;
|
|
3105
|
+
};
|
|
3106
|
+
}
|
|
3107
|
+
const topViewedId = (_b2 = (_a2 = performance.topViewedProducts) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.product_id;
|
|
3108
|
+
if (!topViewedId) {
|
|
3109
|
+
setMostViewedTrendLoading(false);
|
|
3110
|
+
setMostViewedTrendError(null);
|
|
3111
|
+
setMostViewedTrend({
|
|
3112
|
+
series: [],
|
|
3113
|
+
productViewsConnected: true,
|
|
3114
|
+
product: null
|
|
3115
|
+
});
|
|
3116
|
+
return () => {
|
|
3117
|
+
cancelled = true;
|
|
3118
|
+
};
|
|
3119
|
+
}
|
|
3120
|
+
setMostViewedTrendLoading(true);
|
|
3121
|
+
setMostViewedTrendError(null);
|
|
3122
|
+
const period = summaryDaysToGraphPeriod(summaryDays);
|
|
3123
|
+
fetch(
|
|
3124
|
+
buildAnalyticsUrl("/admin/analytics/products-over-time", {
|
|
3125
|
+
period,
|
|
3126
|
+
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null,
|
|
3127
|
+
product_id: topViewedId
|
|
3128
|
+
}),
|
|
3129
|
+
{ credentials: "include" }
|
|
3130
|
+
).then((res) => {
|
|
3131
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
3132
|
+
return res.json();
|
|
3133
|
+
}).then((body) => {
|
|
3134
|
+
if (!cancelled) setMostViewedTrend(body);
|
|
3135
|
+
}).catch((error) => {
|
|
3136
|
+
if (!cancelled) {
|
|
3137
|
+
setMostViewedTrendError(error instanceof Error ? error.message : String(error));
|
|
3138
|
+
}
|
|
3139
|
+
}).finally(() => {
|
|
3140
|
+
if (!cancelled) setMostViewedTrendLoading(false);
|
|
3141
|
+
});
|
|
3142
|
+
return () => {
|
|
3143
|
+
cancelled = true;
|
|
3144
|
+
};
|
|
3145
|
+
}, [salesChannelId, summaryDays, performanceLoading, performanceError, performance]);
|
|
2831
3146
|
react.useEffect(() => {
|
|
2832
3147
|
let cancelled = false;
|
|
2833
3148
|
setPerformanceLoading(true);
|
|
@@ -2836,7 +3151,8 @@ function ProductsDashboard() {
|
|
|
2836
3151
|
buildAnalyticsUrl("/admin/analytics/products-performance", {
|
|
2837
3152
|
days: summaryDays,
|
|
2838
3153
|
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null
|
|
2839
|
-
})
|
|
3154
|
+
}),
|
|
3155
|
+
{ credentials: "include" }
|
|
2840
3156
|
).then((res) => {
|
|
2841
3157
|
if (!res.ok) throw new Error(res.statusText);
|
|
2842
3158
|
return res.json();
|
|
@@ -2853,33 +3169,64 @@ function ProductsDashboard() {
|
|
|
2853
3169
|
cancelled = true;
|
|
2854
3170
|
};
|
|
2855
3171
|
}, [salesChannelId, summaryDays]);
|
|
2856
|
-
const
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
);
|
|
3172
|
+
const series = (overTime == null ? void 0 : overTime.series) ?? [];
|
|
3173
|
+
const trendTotals = react.useMemo(() => {
|
|
3174
|
+
const totalUnits = series.reduce((s, p) => s + p.units_sold, 0);
|
|
3175
|
+
const totalRevenue = series.reduce((s, p) => s + p.revenue, 0);
|
|
3176
|
+
const totalViews = series.reduce((s, p) => s + p.views, 0);
|
|
3177
|
+
const n = series.length || 1;
|
|
3178
|
+
return {
|
|
3179
|
+
totalUnits,
|
|
3180
|
+
totalRevenue,
|
|
3181
|
+
totalViews,
|
|
3182
|
+
avgUnitsPerDay: totalUnits / n,
|
|
3183
|
+
avgRevenuePerDay: totalRevenue / n,
|
|
3184
|
+
avgViewsPerDay: totalViews / n
|
|
3185
|
+
};
|
|
3186
|
+
}, [series]);
|
|
3187
|
+
const peakRevenuePoint = series.length > 0 ? series.reduce((peak, point) => point.revenue > peak.revenue ? point : peak) : null;
|
|
3188
|
+
const viewsConnectedForPulse = ((summary == null ? void 0 : summary.productViewsConnected) ?? false) || (overTime == null ? void 0 : overTime.productViewsConnected) === true || (topSellers == null ? void 0 : topSellers.productViewsConnected) === true || (performance == null ? void 0 : performance.productViewsConnected) === true;
|
|
3189
|
+
const bestSellersTrendSeries = (bestSellersTrend == null ? void 0 : bestSellersTrend.series) ?? [];
|
|
3190
|
+
const mostViewedTrendSeries = (mostViewedTrend == null ? void 0 : mostViewedTrend.series) ?? [];
|
|
3191
|
+
const bestSellerTrendProductTitle = ((_a = bestSellersTrend == null ? void 0 : bestSellersTrend.product) == null ? void 0 : _a.product_title) ?? ((_c = (_b = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _b[0]) == null ? void 0 : _c.product_title) ?? null;
|
|
3192
|
+
const mostViewedTrendProductTitle = ((_d = mostViewedTrend == null ? void 0 : mostViewedTrend.product) == null ? void 0 : _d.product_title) ?? ((_f = (_e = performance == null ? void 0 : performance.topViewedProducts) == null ? void 0 : _e[0]) == null ? void 0 : _f.product_title) ?? null;
|
|
2874
3193
|
const viewsVsUnitsScatterData = react.useMemo(
|
|
2875
3194
|
() => ((performance == null ? void 0 : performance.topViewedProducts) ?? []).slice(0, 48).filter((row) => row.total_views > 0 || row.units_sold > 0),
|
|
2876
3195
|
[performance]
|
|
2877
3196
|
);
|
|
3197
|
+
const selectedSummaryPeriod = ((_g = SUMMARY_PERIODS.find((p) => p.value === summaryDays)) == null ? void 0 : _g.label) ?? "All time";
|
|
3198
|
+
const selectedGraphPeriodLabel = ((_h = GRAPH_PERIODS.find((p) => p.value === graphPeriod)) == null ? void 0 : _h.label) ?? "One week";
|
|
3199
|
+
const quickPulseMetrics = [
|
|
3200
|
+
{
|
|
3201
|
+
label: "Range units",
|
|
3202
|
+
value: formatShortNumber(trendTotals.totalUnits),
|
|
3203
|
+
helper: selectedGraphPeriodLabel,
|
|
3204
|
+
accentClassName: "border-sky-400/25 bg-sky-500/5"
|
|
3205
|
+
},
|
|
3206
|
+
{
|
|
3207
|
+
label: "Range revenue",
|
|
3208
|
+
value: formatCompactCurrency(trendTotals.totalRevenue),
|
|
3209
|
+
helper: "Current chart window",
|
|
3210
|
+
accentClassName: "border-fuchsia-400/25 bg-fuchsia-500/5"
|
|
3211
|
+
},
|
|
3212
|
+
{
|
|
3213
|
+
label: "Avg/day views",
|
|
3214
|
+
value: trendTotals.avgViewsPerDay.toFixed(0),
|
|
3215
|
+
helper: viewsConnectedForPulse ? "Across chart range" : "Views need tracking",
|
|
3216
|
+
accentClassName: "border-violet-400/25 bg-violet-500/5"
|
|
3217
|
+
},
|
|
3218
|
+
{
|
|
3219
|
+
label: "Peak revenue day",
|
|
3220
|
+
value: peakRevenuePoint ? formatCompactCurrency(peakRevenuePoint.revenue) : "—",
|
|
3221
|
+
helper: (peakRevenuePoint == null ? void 0 : peakRevenuePoint.label) ?? formatChartLabel(peakRevenuePoint == null ? void 0 : peakRevenuePoint.date),
|
|
3222
|
+
accentClassName: "border-amber-400/25 bg-amber-500/5"
|
|
3223
|
+
}
|
|
3224
|
+
];
|
|
2878
3225
|
if (summaryLoading) {
|
|
2879
3226
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2880
3227
|
"div",
|
|
2881
3228
|
{
|
|
2882
|
-
className: "flex items-center justify-center
|
|
3229
|
+
className: "flex min-h-[200px] items-center justify-center",
|
|
2883
3230
|
role: "status",
|
|
2884
3231
|
"aria-label": "Loading product analytics",
|
|
2885
3232
|
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading…" })
|
|
@@ -2890,16 +3237,65 @@ function ProductsDashboard() {
|
|
|
2890
3237
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: summaryError ?? "Failed to load product analytics" }) });
|
|
2891
3238
|
}
|
|
2892
3239
|
const viewsConnected = summary.productViewsConnected || (overTime == null ? void 0 : overTime.productViewsConnected) === true || (topSellers == null ? void 0 : topSellers.productViewsConnected) === true || (performance == null ? void 0 : performance.productViewsConnected) === true;
|
|
2893
|
-
const selectedChannelLabel = salesChannelId === "all" ? "All channels" : ((
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
3240
|
+
const selectedChannelLabel = salesChannelId === "all" ? "All channels" : ((_i = salesChannels.find((channel) => channel.id === salesChannelId)) == null ? void 0 : _i.name) ?? "Selected channel";
|
|
3241
|
+
const primaryStats = [
|
|
3242
|
+
{
|
|
3243
|
+
label: "Units sold",
|
|
3244
|
+
value: summary.unitsSold.toLocaleString(),
|
|
3245
|
+
helper: selectedSummaryPeriod,
|
|
3246
|
+
accent: "amber"
|
|
3247
|
+
},
|
|
3248
|
+
{
|
|
3249
|
+
label: "Product revenue",
|
|
3250
|
+
value: formatCurrency(summary.productRevenue),
|
|
3251
|
+
helper: selectedSummaryPeriod,
|
|
3252
|
+
accent: "green"
|
|
3253
|
+
},
|
|
3254
|
+
{
|
|
3255
|
+
label: "Orders with products",
|
|
3256
|
+
value: summary.ordersWithProducts.toLocaleString(),
|
|
3257
|
+
helper: "Distinct orders",
|
|
3258
|
+
accent: "blue"
|
|
3259
|
+
},
|
|
3260
|
+
{
|
|
3261
|
+
label: "Active products sold",
|
|
3262
|
+
value: summary.activeProductsSold.toLocaleString(),
|
|
3263
|
+
helper: "Unique SKUs with sales",
|
|
3264
|
+
accent: "purple"
|
|
3265
|
+
},
|
|
3266
|
+
{
|
|
3267
|
+
label: "Product views",
|
|
3268
|
+
value: summary.totalProductViews.toLocaleString(),
|
|
3269
|
+
helper: viewsConnected ? "Tracked views" : "Tracking unavailable",
|
|
3270
|
+
accent: "green"
|
|
3271
|
+
},
|
|
3272
|
+
{
|
|
3273
|
+
label: "View / order ratio",
|
|
3274
|
+
value: formatRatio(summary.viewToOrderRatio),
|
|
3275
|
+
helper: viewsConnected ? "Views per product order" : "N/A without views",
|
|
3276
|
+
accent: "blue"
|
|
3277
|
+
}
|
|
3278
|
+
];
|
|
3279
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AnalyticsDashboardShell, { className: "w-full min-w-0 max-w-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full min-w-0 max-w-full overflow-hidden rounded-2xl border border-ui-border-base/80 bg-ui-bg-base shadow-sm", children: [
|
|
3280
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3281
|
+
AnalyticsDashboardHeader,
|
|
3282
|
+
{
|
|
3283
|
+
variant: "premium",
|
|
3284
|
+
appearance: "inset",
|
|
3285
|
+
actionsBare: true,
|
|
3286
|
+
actionsClassName: "w-full justify-start lg:w-auto lg:justify-end",
|
|
2898
3287
|
title: "Products",
|
|
2899
|
-
description: "
|
|
2900
|
-
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-
|
|
2901
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
2902
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3288
|
+
description: "Units, revenue, best sellers, and visit behavior in a compact layout aligned with Orders analytics.",
|
|
3289
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full min-w-0 flex-wrap items-center gap-x-3 gap-y-2 sm:flex-nowrap lg:gap-x-4", children: [
|
|
3290
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2 sm:flex-initial sm:flex-none", children: [
|
|
3291
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3292
|
+
ui.Label,
|
|
3293
|
+
{
|
|
3294
|
+
htmlFor: "products-channel-filter",
|
|
3295
|
+
className: "shrink-0 whitespace-nowrap text-ui-fg-muted text-xs font-medium",
|
|
3296
|
+
children: "Channel"
|
|
3297
|
+
}
|
|
3298
|
+
),
|
|
2903
3299
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2904
3300
|
"select",
|
|
2905
3301
|
{
|
|
@@ -2907,7 +3303,8 @@ function ProductsDashboard() {
|
|
|
2907
3303
|
value: salesChannelId,
|
|
2908
3304
|
onChange: (event) => setSalesChannelId(event.target.value),
|
|
2909
3305
|
disabled: filtersLoading,
|
|
2910
|
-
className:
|
|
3306
|
+
className: SELECT_CLASS_NAME,
|
|
3307
|
+
"aria-busy": filtersLoading,
|
|
2911
3308
|
children: [
|
|
2912
3309
|
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "all", children: "All channels" }),
|
|
2913
3310
|
salesChannels.map((channel) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: channel.id, children: channel.name }, channel.id))
|
|
@@ -2915,419 +3312,966 @@ function ProductsDashboard() {
|
|
|
2915
3312
|
}
|
|
2916
3313
|
)
|
|
2917
3314
|
] }),
|
|
2918
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
2919
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3315
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2 sm:flex-initial sm:flex-none", children: [
|
|
3316
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3317
|
+
ui.Label,
|
|
3318
|
+
{
|
|
3319
|
+
htmlFor: "products-summary-period",
|
|
3320
|
+
className: "shrink-0 whitespace-nowrap text-ui-fg-muted text-xs font-medium",
|
|
3321
|
+
children: "Period"
|
|
3322
|
+
}
|
|
3323
|
+
),
|
|
2920
3324
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2921
3325
|
"select",
|
|
2922
3326
|
{
|
|
2923
3327
|
id: "products-summary-period",
|
|
2924
3328
|
value: summaryDays,
|
|
2925
3329
|
onChange: (event) => setSummaryDays(event.target.value),
|
|
2926
|
-
className:
|
|
3330
|
+
className: SELECT_CLASS_NAME,
|
|
2927
3331
|
children: SUMMARY_PERIODS.map((period) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: period.value, children: period.label }, period.value))
|
|
2928
3332
|
}
|
|
2929
3333
|
)
|
|
2930
3334
|
] }),
|
|
2931
|
-
|
|
2932
|
-
summaryDays !== "all"
|
|
3335
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full shrink-0 flex-wrap items-center gap-x-3 gap-y-1 sm:w-auto sm:pl-1", children: [
|
|
3336
|
+
summaryDays !== "all" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2933
3337
|
"button",
|
|
2934
3338
|
{
|
|
2935
3339
|
type: "button",
|
|
2936
3340
|
onClick: () => setSummaryDays("all"),
|
|
2937
3341
|
className: "text-xs text-ui-fg-muted transition-colors hover:text-ui-fg-base",
|
|
2938
|
-
"aria-label": "
|
|
3342
|
+
"aria-label": "Clear summary period filter",
|
|
2939
3343
|
children: "Clear period"
|
|
2940
3344
|
}
|
|
2941
|
-
),
|
|
2942
|
-
salesChannelId !== "all"
|
|
3345
|
+
) : null,
|
|
3346
|
+
salesChannelId !== "all" ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2943
3347
|
"button",
|
|
2944
3348
|
{
|
|
2945
3349
|
type: "button",
|
|
2946
3350
|
onClick: () => setSalesChannelId("all"),
|
|
2947
3351
|
className: "text-xs text-ui-fg-muted transition-colors hover:text-ui-fg-base",
|
|
2948
|
-
"aria-label": "
|
|
3352
|
+
"aria-label": "Clear sales channel filter",
|
|
2949
3353
|
children: "Clear channel"
|
|
2950
3354
|
}
|
|
2951
|
-
)
|
|
3355
|
+
) : null
|
|
2952
3356
|
] })
|
|
2953
3357
|
] })
|
|
2954
3358
|
}
|
|
2955
3359
|
),
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
"
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2965
|
-
|
|
2966
|
-
{
|
|
2967
|
-
|
|
2968
|
-
value: summary.unitsSold.toLocaleString()
|
|
2969
|
-
}
|
|
2970
|
-
),
|
|
2971
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2972
|
-
AnalyticsStatCard,
|
|
2973
|
-
{
|
|
2974
|
-
label: "Product revenue",
|
|
2975
|
-
value: formatCurrency(summary.productRevenue)
|
|
2976
|
-
}
|
|
2977
|
-
),
|
|
2978
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2979
|
-
AnalyticsStatCard,
|
|
2980
|
-
{
|
|
2981
|
-
label: "Orders with product sales",
|
|
2982
|
-
value: summary.ordersWithProducts.toLocaleString()
|
|
2983
|
-
}
|
|
2984
|
-
),
|
|
2985
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2986
|
-
AnalyticsStatCard,
|
|
2987
|
-
{
|
|
2988
|
-
label: "Active products sold",
|
|
2989
|
-
value: summary.activeProductsSold.toLocaleString()
|
|
2990
|
-
}
|
|
2991
|
-
),
|
|
2992
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2993
|
-
AnalyticsStatCard,
|
|
2994
|
-
{
|
|
2995
|
-
label: "Product views",
|
|
2996
|
-
value: summary.totalProductViews.toLocaleString()
|
|
2997
|
-
}
|
|
2998
|
-
),
|
|
2999
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3000
|
-
AnalyticsStatCard,
|
|
3001
|
-
{
|
|
3002
|
-
label: "View / order ratio",
|
|
3003
|
-
value: formatRatio(summary.viewToOrderRatio)
|
|
3004
|
-
}
|
|
3005
|
-
)
|
|
3006
|
-
] }),
|
|
3007
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3008
|
-
AnalyticsSection,
|
|
3009
|
-
{
|
|
3010
|
-
title: "Sales and views over time",
|
|
3011
|
-
description: "Units sold and revenue are always shown. Product views appear when tracking data is available.",
|
|
3012
|
-
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3013
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "products-graph-period", className: "text-ui-fg-muted text-sm", children: "Range" }),
|
|
3014
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3015
|
-
"select",
|
|
3016
|
-
{
|
|
3017
|
-
id: "products-graph-period",
|
|
3018
|
-
value: graphPeriod,
|
|
3019
|
-
onChange: (event) => setGraphPeriod(event.target.value),
|
|
3020
|
-
className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
3021
|
-
children: GRAPH_PERIODS.map((period) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: period.value, children: period.label }, period.value))
|
|
3022
|
-
}
|
|
3023
|
-
)
|
|
3024
|
-
] }),
|
|
3025
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-80", children: overTimeLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading chart…" }) }) : overTimeError || !overTime ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: overTimeError ?? "Failed to load product trend" }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.LineChart, { data: overTime.series, children: [
|
|
3026
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
|
|
3027
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "label" }),
|
|
3028
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3029
|
-
recharts.YAxis,
|
|
3030
|
-
{
|
|
3031
|
-
yAxisId: "counts",
|
|
3032
|
-
allowDecimals: false,
|
|
3033
|
-
tickFormatter: (value) => Math.floor(Number(value)).toLocaleString()
|
|
3034
|
-
}
|
|
3035
|
-
),
|
|
3036
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3037
|
-
recharts.YAxis,
|
|
3038
|
-
{
|
|
3039
|
-
yAxisId: "revenue",
|
|
3040
|
-
orientation: "right",
|
|
3041
|
-
tickFormatter: (value) => formatCurrency(Number(value))
|
|
3042
|
-
}
|
|
3043
|
-
),
|
|
3044
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(TrendTooltip, {}) }),
|
|
3045
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, {}),
|
|
3046
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3047
|
-
recharts.Line,
|
|
3048
|
-
{
|
|
3049
|
-
yAxisId: "counts",
|
|
3050
|
-
type: "monotone",
|
|
3051
|
-
dataKey: "units_sold",
|
|
3052
|
-
name: "Units sold",
|
|
3053
|
-
stroke: SALES_COLOR,
|
|
3054
|
-
strokeWidth: 2,
|
|
3055
|
-
dot: false
|
|
3056
|
-
}
|
|
3057
|
-
),
|
|
3058
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3059
|
-
recharts.Line,
|
|
3060
|
-
{
|
|
3061
|
-
yAxisId: "counts",
|
|
3062
|
-
type: "monotone",
|
|
3063
|
-
dataKey: "views",
|
|
3064
|
-
name: "Views",
|
|
3065
|
-
stroke: VIEWS_COLOR,
|
|
3066
|
-
strokeWidth: 2,
|
|
3067
|
-
dot: false
|
|
3068
|
-
}
|
|
3069
|
-
),
|
|
3070
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3071
|
-
recharts.Line,
|
|
3072
|
-
{
|
|
3073
|
-
yAxisId: "revenue",
|
|
3074
|
-
type: "monotone",
|
|
3075
|
-
dataKey: "revenue",
|
|
3076
|
-
name: "Revenue",
|
|
3077
|
-
stroke: REVENUE_COLOR,
|
|
3078
|
-
strokeWidth: 2,
|
|
3079
|
-
dot: false
|
|
3080
|
-
}
|
|
3081
|
-
)
|
|
3082
|
-
] }) }) }) })
|
|
3083
|
-
}
|
|
3084
|
-
),
|
|
3085
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 xl:grid-cols-2", children: [
|
|
3086
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3087
|
-
AnalyticsSection,
|
|
3088
|
-
{
|
|
3089
|
-
title: "Best sellers",
|
|
3090
|
-
description: "Top products by revenue.",
|
|
3091
|
-
actions: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2", children: TOP_SELLER_PERIODS.map((period) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3092
|
-
ui.Button,
|
|
3093
|
-
{
|
|
3094
|
-
variant: topSellerPeriod === period.value ? "secondary" : "transparent",
|
|
3095
|
-
size: "small",
|
|
3096
|
-
onClick: () => setTopSellerPeriod(period.value),
|
|
3097
|
-
children: period.label
|
|
3098
|
-
},
|
|
3099
|
-
period.value
|
|
3100
|
-
)) }),
|
|
3101
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-80", children: topSellersLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading best sellers…" }) }) : topSellersError || !topSellers ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: topSellersError ?? "Failed to load best sellers" }) }) : topSellerChartData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "No product sales found for this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.BarChart, { data: topSellerChartData, layout: "vertical", margin: { left: 8, right: 8 }, children: [
|
|
3102
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", horizontal: false }),
|
|
3103
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3104
|
-
recharts.XAxis,
|
|
3105
|
-
{
|
|
3106
|
-
type: "number",
|
|
3107
|
-
allowDecimals: false,
|
|
3108
|
-
tickFormatter: (value) => formatCurrency(Number(value))
|
|
3109
|
-
}
|
|
3110
|
-
),
|
|
3111
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3112
|
-
recharts.YAxis,
|
|
3113
|
-
{
|
|
3114
|
-
type: "category",
|
|
3115
|
-
dataKey: "product_title",
|
|
3116
|
-
width: 160,
|
|
3117
|
-
tickLine: false
|
|
3118
|
-
}
|
|
3119
|
-
),
|
|
3120
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(ProductBarTooltip, { showViews: false }) }),
|
|
3121
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Bar, { dataKey: "revenue", radius: [0, 6, 6, 0], children: topSellerChartData.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3122
|
-
recharts.Cell,
|
|
3123
|
-
{
|
|
3124
|
-
fill: entry.isBestSeller ? REVENUE_COLOR : REVENUE_MUTED_COLOR
|
|
3125
|
-
},
|
|
3126
|
-
entry.product_id
|
|
3127
|
-
)) })
|
|
3128
|
-
] }) }) }) })
|
|
3129
|
-
}
|
|
3130
|
-
),
|
|
3131
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3132
|
-
AnalyticsSection,
|
|
3133
|
-
{
|
|
3134
|
-
title: "Top seller breakdown",
|
|
3135
|
-
description: "Products ranked by revenue.",
|
|
3136
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTableSurface, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
3137
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3138
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Product" }),
|
|
3139
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Units" }),
|
|
3140
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Orders" }),
|
|
3141
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Revenue" })
|
|
3142
|
-
] }) }),
|
|
3143
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Body, { children: [
|
|
3144
|
-
((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 8).map((product) => {
|
|
3145
|
-
var _a2, _b2;
|
|
3146
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3147
|
-
ui.Table.Row,
|
|
3148
|
-
{
|
|
3149
|
-
className: product.product_id === ((_b2 = (_a2 = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.product_id) ? "bg-ui-bg-subtle" : void 0,
|
|
3150
|
-
children: [
|
|
3151
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.product_title }),
|
|
3152
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.units_sold.toLocaleString() }),
|
|
3153
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.order_count.toLocaleString() }),
|
|
3154
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatCurrency(product.revenue) })
|
|
3155
|
-
]
|
|
3156
|
-
},
|
|
3157
|
-
product.product_id
|
|
3158
|
-
);
|
|
3159
|
-
}),
|
|
3160
|
-
!topSellersLoading && (((_b = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _b.length) ?? 0) === 0 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3161
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: "No best seller data yet." }),
|
|
3162
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
3163
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
3164
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {})
|
|
3165
|
-
] })
|
|
3166
|
-
] })
|
|
3167
|
-
] }) })
|
|
3168
|
-
}
|
|
3169
|
-
)
|
|
3170
|
-
] }),
|
|
3171
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 xl:grid-cols-2", children: [
|
|
3172
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3173
|
-
AnalyticsSection,
|
|
3174
|
-
{
|
|
3175
|
-
title: "Most viewed products",
|
|
3176
|
-
description: "View activity is sourced from the existing product-helper tracking module.",
|
|
3177
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-80", children: performanceLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading product views…" }) }) : performanceError || !performance ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: performanceError ?? "Failed to load product performance" }) }) : !performance.productViewsConnected ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Product view tracking is not available in this environment." }) }) : topViewedChartData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "No product views found for this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.BarChart, { data: topViewedChartData, layout: "vertical", margin: { left: 8, right: 8 }, children: [
|
|
3178
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", horizontal: false }),
|
|
3179
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { type: "number", allowDecimals: false }),
|
|
3180
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3181
|
-
recharts.YAxis,
|
|
3182
|
-
{
|
|
3183
|
-
type: "category",
|
|
3184
|
-
dataKey: "product_title",
|
|
3185
|
-
width: 160,
|
|
3186
|
-
tickLine: false
|
|
3187
|
-
}
|
|
3188
|
-
),
|
|
3189
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(ProductBarTooltip, {}) }),
|
|
3190
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Bar, { dataKey: "total_views", fill: VIEWS_COLOR, radius: [0, 6, 6, 0] })
|
|
3191
|
-
] }) }) }) })
|
|
3192
|
-
}
|
|
3193
|
-
),
|
|
3194
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3195
|
-
AnalyticsSection,
|
|
3196
|
-
{
|
|
3197
|
-
title: "View-to-sales opportunities",
|
|
3198
|
-
description: "Highlight products that attract attention but convert less efficiently.",
|
|
3199
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTableSurface, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
3200
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3201
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Product" }),
|
|
3202
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Views" }),
|
|
3203
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Units" }),
|
|
3204
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Views / unit" })
|
|
3205
|
-
] }) }),
|
|
3206
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Body, { children: [
|
|
3207
|
-
((performance == null ? void 0 : performance.viewOpportunities) ?? []).slice(0, 8).map((product) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3208
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.product_title }),
|
|
3209
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.total_views.toLocaleString() }),
|
|
3210
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.units_sold.toLocaleString() }),
|
|
3211
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatRatio(product.views_per_unit) })
|
|
3212
|
-
] }, product.product_id)),
|
|
3213
|
-
!performanceLoading && !performanceError && (((_c = performance == null ? void 0 : performance.viewOpportunities) == null ? void 0 : _c.length) ?? 0) === 0 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3214
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: "No product view opportunities yet." }),
|
|
3215
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
3216
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
3217
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {})
|
|
3218
|
-
] })
|
|
3219
|
-
] })
|
|
3220
|
-
] }) })
|
|
3221
|
-
}
|
|
3222
|
-
)
|
|
3223
|
-
] }),
|
|
3224
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3225
|
-
AnalyticsSection,
|
|
3226
|
-
{
|
|
3227
|
-
title: "Views vs units sold",
|
|
3228
|
-
description: "Each point is a product (top viewed in this period). Requires product view tracking.",
|
|
3229
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-80", children: performanceLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading scatter…" }) }) : performanceError || !performance ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: performanceError ?? "Failed to load product performance" }) }) : !performance.productViewsConnected ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Product view tracking is not available in this environment." }) }) : viewsVsUnitsScatterData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "No products with views or sales in this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.ScatterChart, { margin: { top: 12, right: 16, bottom: 8, left: 8 }, children: [
|
|
3230
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: "rgba(148,163,184,0.2)" }),
|
|
3231
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3232
|
-
recharts.XAxis,
|
|
3233
|
-
{
|
|
3234
|
-
type: "number",
|
|
3235
|
-
dataKey: "total_views",
|
|
3236
|
-
name: "Views",
|
|
3237
|
-
tick: { fontSize: 11 },
|
|
3238
|
-
allowDecimals: false,
|
|
3239
|
-
label: {
|
|
3240
|
-
value: "Views",
|
|
3241
|
-
position: "insideBottom",
|
|
3242
|
-
offset: -4,
|
|
3243
|
-
fontSize: 11,
|
|
3244
|
-
fill: "#6b7280"
|
|
3245
|
-
}
|
|
3246
|
-
}
|
|
3247
|
-
),
|
|
3248
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3249
|
-
recharts.YAxis,
|
|
3250
|
-
{
|
|
3251
|
-
type: "number",
|
|
3252
|
-
dataKey: "units_sold",
|
|
3253
|
-
name: "Units sold",
|
|
3254
|
-
tick: { fontSize: 11 },
|
|
3255
|
-
allowDecimals: false,
|
|
3256
|
-
label: {
|
|
3257
|
-
value: "Units sold",
|
|
3258
|
-
angle: -90,
|
|
3259
|
-
position: "insideLeft",
|
|
3260
|
-
fontSize: 11,
|
|
3261
|
-
fill: "#6b7280"
|
|
3262
|
-
}
|
|
3263
|
-
}
|
|
3264
|
-
),
|
|
3265
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3266
|
-
recharts.Tooltip,
|
|
3267
|
-
{
|
|
3268
|
-
cursor: { strokeDasharray: "4 4" },
|
|
3269
|
-
content: /* @__PURE__ */ jsxRuntime.jsx(ProductBarTooltip, {})
|
|
3270
|
-
}
|
|
3271
|
-
),
|
|
3272
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3273
|
-
recharts.Scatter,
|
|
3274
|
-
{
|
|
3275
|
-
name: "Products",
|
|
3276
|
-
data: viewsVsUnitsScatterData,
|
|
3277
|
-
fill: VIEWS_COLOR
|
|
3278
|
-
}
|
|
3279
|
-
)
|
|
3280
|
-
] }) }) }) })
|
|
3281
|
-
}
|
|
3282
|
-
)
|
|
3283
|
-
] });
|
|
3284
|
-
}
|
|
3285
|
-
const ANALYTICS_MODULES = [
|
|
3286
|
-
{ id: "orders", label: "Orders" },
|
|
3287
|
-
{ id: "customers", label: "Customers" },
|
|
3288
|
-
{ id: "products", label: "Products" }
|
|
3289
|
-
];
|
|
3290
|
-
const AnalyticsPage = () => {
|
|
3291
|
-
const [activeModule, setActiveModule] = react.useState("orders");
|
|
3292
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-[1600px] flex-col gap-4 p-4", children: [
|
|
3293
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "overflow-hidden rounded-2xl border border-ui-border-base bg-ui-bg-base p-0 shadow-sm", children: [
|
|
3294
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-5 px-5 py-5 xl:flex-row xl:items-end xl:justify-between", children: [
|
|
3295
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
3296
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", className: "tracking-tight", children: "Analytics" }),
|
|
3297
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Review orders, customers, and products in one organized workspace." })
|
|
3298
|
-
] }),
|
|
3299
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3300
|
-
"div",
|
|
3360
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 p-2 pt-0 sm:p-3 sm:pt-0 md:p-4 md:pt-0", children: [
|
|
3361
|
+
filtersError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-dashed border-ui-border-base bg-ui-bg-subtle/30 px-3 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-xs", children: filtersError }) }) : null,
|
|
3362
|
+
!viewsConnected ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-dashed border-ui-border-base bg-ui-bg-subtle/30 px-3 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[11px] leading-snug", children: "Product views are unavailable until the `medusa-product-helper` tracking module is registered and storefront page views are being recorded." }) }) : null,
|
|
3363
|
+
salesChannelId !== "all" && viewsConnected ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-lg border border-ui-border-base/80 bg-ui-bg-subtle/25 px-3 py-2 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[11px] leading-snug", children: [
|
|
3364
|
+
"Showing analytics for ",
|
|
3365
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-ui-fg-base", children: selectedChannelLabel }),
|
|
3366
|
+
". Views are scoped to products assigned to this sales channel."
|
|
3367
|
+
] }) }) : null,
|
|
3368
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
3369
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Product overview" }) }),
|
|
3370
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-6", children: primaryStats.map((stat) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3371
|
+
AtlasKpiCard,
|
|
3301
3372
|
{
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
{
|
|
3310
|
-
type: "button",
|
|
3311
|
-
role: "tab",
|
|
3312
|
-
"aria-selected": active,
|
|
3313
|
-
onClick: () => setActiveModule(m.id),
|
|
3314
|
-
className: `rounded-xl px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ui-fg-interactive focus-visible:ring-offset-2 focus-visible:ring-offset-ui-bg-base ${active ? "bg-ui-bg-base text-ui-fg-base shadow-sm" : "text-ui-fg-muted hover:bg-ui-bg-base/60 hover:text-ui-fg-base"}`.trim(),
|
|
3315
|
-
children: m.label
|
|
3316
|
-
},
|
|
3317
|
-
m.id
|
|
3318
|
-
);
|
|
3319
|
-
})
|
|
3320
|
-
}
|
|
3321
|
-
)
|
|
3373
|
+
label: stat.label,
|
|
3374
|
+
value: stat.value,
|
|
3375
|
+
helper: stat.helper,
|
|
3376
|
+
accent: stat.accent
|
|
3377
|
+
},
|
|
3378
|
+
stat.label
|
|
3379
|
+
)) })
|
|
3322
3380
|
] }),
|
|
3323
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3381
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 grid grid-cols-12 gap-2 lg:items-start", children: [
|
|
3382
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-12 lg:col-span-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3383
|
+
AnalyticsSection,
|
|
3384
|
+
{
|
|
3385
|
+
variant: "atlas",
|
|
3386
|
+
actionsBare: true,
|
|
3387
|
+
title: "Sales & views over time",
|
|
3388
|
+
description: "Units and views share the left axis; revenue uses the right. Window totals match this range.",
|
|
3389
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
3390
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3391
|
+
ui.Label,
|
|
3392
|
+
{
|
|
3393
|
+
htmlFor: "products-graph-period",
|
|
3394
|
+
className: "shrink-0 text-ui-fg-muted text-xs font-medium",
|
|
3395
|
+
children: "Range"
|
|
3396
|
+
}
|
|
3397
|
+
),
|
|
3398
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3399
|
+
"select",
|
|
3400
|
+
{
|
|
3401
|
+
id: "products-graph-period",
|
|
3402
|
+
value: graphPeriod,
|
|
3403
|
+
onChange: (event) => setGraphPeriod(event.target.value),
|
|
3404
|
+
className: SELECT_CLASS_NAME,
|
|
3405
|
+
children: GRAPH_PERIODS.map((period) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: period.value, children: period.label }, period.value))
|
|
3406
|
+
}
|
|
3407
|
+
)
|
|
3408
|
+
] }),
|
|
3409
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
3410
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-1.5 sm:grid-cols-3", children: [
|
|
3411
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
3412
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window units" }),
|
|
3413
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: formatShortNumber(trendTotals.totalUnits) }),
|
|
3414
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: [
|
|
3415
|
+
"Avg/day ",
|
|
3416
|
+
trendTotals.avgUnitsPerDay.toFixed(1)
|
|
3417
|
+
] })
|
|
3418
|
+
] }) }),
|
|
3419
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
3420
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window revenue" }),
|
|
3421
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: formatCompactCurrency(trendTotals.totalRevenue) }),
|
|
3422
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: [
|
|
3423
|
+
"Avg/day ",
|
|
3424
|
+
formatCompactCurrency(trendTotals.avgRevenuePerDay)
|
|
3425
|
+
] })
|
|
3426
|
+
] }) }),
|
|
3427
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
3428
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window views" }),
|
|
3429
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: formatShortNumber(trendTotals.totalViews) }),
|
|
3430
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: [
|
|
3431
|
+
"Avg/day ",
|
|
3432
|
+
trendTotals.avgViewsPerDay.toFixed(0)
|
|
3433
|
+
] })
|
|
3434
|
+
] }) })
|
|
3435
|
+
] }),
|
|
3436
|
+
!overTimeLoading && !overTimeError && series.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-1.5 md:grid-cols-3", children: [
|
|
3437
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
|
|
3438
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Units sold" }),
|
|
3439
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[96px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3440
|
+
recharts.LineChart,
|
|
3441
|
+
{
|
|
3442
|
+
data: series,
|
|
3443
|
+
margin: { top: 4, right: 4, left: 0, bottom: 0 },
|
|
3444
|
+
children: [
|
|
3445
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3446
|
+
recharts.CartesianGrid,
|
|
3447
|
+
{
|
|
3448
|
+
strokeDasharray: "3 3",
|
|
3449
|
+
vertical: false,
|
|
3450
|
+
stroke: "rgba(148,163,184,0.12)"
|
|
3451
|
+
}
|
|
3452
|
+
),
|
|
3453
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "date", hide: true }),
|
|
3454
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3455
|
+
recharts.YAxis,
|
|
3456
|
+
{
|
|
3457
|
+
width: 28,
|
|
3458
|
+
tick: CHART_AXIS_TICK_SM,
|
|
3459
|
+
allowDecimals: false
|
|
3460
|
+
}
|
|
3461
|
+
),
|
|
3462
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(MiniTrendTooltip, {}) }),
|
|
3463
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3464
|
+
recharts.Line,
|
|
3465
|
+
{
|
|
3466
|
+
type: "natural",
|
|
3467
|
+
dataKey: "units_sold",
|
|
3468
|
+
name: "Units sold",
|
|
3469
|
+
stroke: PRODUCT_CHART_COLORS.units,
|
|
3470
|
+
strokeWidth: 2,
|
|
3471
|
+
dot: false
|
|
3472
|
+
}
|
|
3473
|
+
)
|
|
3474
|
+
]
|
|
3475
|
+
}
|
|
3476
|
+
) }) })
|
|
3477
|
+
] }),
|
|
3478
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
|
|
3479
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Views" }),
|
|
3480
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[96px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3481
|
+
recharts.LineChart,
|
|
3482
|
+
{
|
|
3483
|
+
data: series,
|
|
3484
|
+
margin: { top: 4, right: 4, left: 0, bottom: 0 },
|
|
3485
|
+
children: [
|
|
3486
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3487
|
+
recharts.CartesianGrid,
|
|
3488
|
+
{
|
|
3489
|
+
strokeDasharray: "3 3",
|
|
3490
|
+
vertical: false,
|
|
3491
|
+
stroke: "rgba(148,163,184,0.12)"
|
|
3492
|
+
}
|
|
3493
|
+
),
|
|
3494
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "date", hide: true }),
|
|
3495
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3496
|
+
recharts.YAxis,
|
|
3497
|
+
{
|
|
3498
|
+
width: 28,
|
|
3499
|
+
tick: CHART_AXIS_TICK_SM,
|
|
3500
|
+
allowDecimals: false
|
|
3501
|
+
}
|
|
3502
|
+
),
|
|
3503
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(MiniTrendTooltip, {}) }),
|
|
3504
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3505
|
+
recharts.Line,
|
|
3506
|
+
{
|
|
3507
|
+
type: "natural",
|
|
3508
|
+
dataKey: "views",
|
|
3509
|
+
name: "Views",
|
|
3510
|
+
stroke: PRODUCT_CHART_COLORS.views,
|
|
3511
|
+
strokeWidth: 2,
|
|
3512
|
+
dot: false
|
|
3513
|
+
}
|
|
3514
|
+
)
|
|
3515
|
+
]
|
|
3516
|
+
}
|
|
3517
|
+
) }) })
|
|
3518
|
+
] }),
|
|
3519
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
|
|
3520
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Revenue" }),
|
|
3521
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[96px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3522
|
+
recharts.LineChart,
|
|
3523
|
+
{
|
|
3524
|
+
data: series,
|
|
3525
|
+
margin: { top: 4, right: 4, left: 0, bottom: 0 },
|
|
3526
|
+
children: [
|
|
3527
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3528
|
+
recharts.CartesianGrid,
|
|
3529
|
+
{
|
|
3530
|
+
strokeDasharray: "3 3",
|
|
3531
|
+
vertical: false,
|
|
3532
|
+
stroke: "rgba(148,163,184,0.12)"
|
|
3533
|
+
}
|
|
3534
|
+
),
|
|
3535
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "date", hide: true }),
|
|
3536
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3537
|
+
recharts.YAxis,
|
|
3538
|
+
{
|
|
3539
|
+
width: 36,
|
|
3540
|
+
tick: CHART_AXIS_TICK_SM,
|
|
3541
|
+
tickFormatter: (v) => formatCompactCurrency(Number(v))
|
|
3542
|
+
}
|
|
3543
|
+
),
|
|
3544
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(MiniTrendTooltip, {}) }),
|
|
3545
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3546
|
+
recharts.Line,
|
|
3547
|
+
{
|
|
3548
|
+
type: "natural",
|
|
3549
|
+
dataKey: "revenue",
|
|
3550
|
+
name: "Revenue",
|
|
3551
|
+
stroke: PRODUCT_CHART_COLORS.revenue,
|
|
3552
|
+
strokeWidth: 2,
|
|
3553
|
+
dot: false
|
|
3554
|
+
}
|
|
3555
|
+
)
|
|
3556
|
+
]
|
|
3557
|
+
}
|
|
3558
|
+
) }) })
|
|
3559
|
+
] })
|
|
3560
|
+
] }) : null,
|
|
3561
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", className: "relative overflow-hidden", children: [
|
|
3562
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-x-6 top-0 h-12 rounded-b-full bg-gradient-to-r from-sky-400/10 via-fuchsia-400/10 to-transparent blur-xl" }),
|
|
3563
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative space-y-1", children: [
|
|
3564
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-1.5", children: [
|
|
3565
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0", children: [
|
|
3566
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Trend overview" }),
|
|
3567
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-sm font-semibold", children: selectedGraphPeriodLabel })
|
|
3568
|
+
] }),
|
|
3569
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap justify-end gap-2 text-[10px] text-ui-fg-muted", children: [
|
|
3570
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
3571
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-sky-400" }),
|
|
3572
|
+
"Units"
|
|
3573
|
+
] }),
|
|
3574
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
3575
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-violet-400" }),
|
|
3576
|
+
"Views"
|
|
3577
|
+
] }),
|
|
3578
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
3579
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-fuchsia-400" }),
|
|
3580
|
+
"Revenue"
|
|
3581
|
+
] })
|
|
3582
|
+
] })
|
|
3583
|
+
] }),
|
|
3584
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(168px,24vh)] min-h-[140px] sm:h-[min(184px,26vh)]", children: overTimeLoading ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3585
|
+
"div",
|
|
3586
|
+
{
|
|
3587
|
+
className: "flex h-full items-center justify-center",
|
|
3588
|
+
role: "status",
|
|
3589
|
+
"aria-label": "Loading product trend chart",
|
|
3590
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading chart…" })
|
|
3591
|
+
}
|
|
3592
|
+
) : overTimeError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: overTimeError }) }) : series.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "No data in this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3593
|
+
recharts.ComposedChart,
|
|
3594
|
+
{
|
|
3595
|
+
data: series,
|
|
3596
|
+
margin: { top: 4, right: 6, left: -6, bottom: 0 },
|
|
3597
|
+
children: [
|
|
3598
|
+
/* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
|
|
3599
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "productUnitsGradient", x1: "0", y1: "0", x2: "1", y2: "0", children: [
|
|
3600
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#67E8F9" }),
|
|
3601
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#3B82F6" })
|
|
3602
|
+
] }),
|
|
3603
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "productRevenueGradient", x1: "0", y1: "0", x2: "1", y2: "0", children: [
|
|
3604
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#F472B6" }),
|
|
3605
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#C084FC" })
|
|
3606
|
+
] }),
|
|
3607
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "productUnitsAreaFill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
3608
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#38BDF8", stopOpacity: 0.22 }),
|
|
3609
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#38BDF8", stopOpacity: 0 })
|
|
3610
|
+
] }),
|
|
3611
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "productRevenueAreaFill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
3612
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#E879F9", stopOpacity: 0.2 }),
|
|
3613
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#E879F9", stopOpacity: 0 })
|
|
3614
|
+
] }),
|
|
3615
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "productViewsAreaFill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
3616
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#C084FC", stopOpacity: 0.18 }),
|
|
3617
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#C084FC", stopOpacity: 0 })
|
|
3618
|
+
] })
|
|
3619
|
+
] }),
|
|
3620
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3621
|
+
recharts.CartesianGrid,
|
|
3622
|
+
{
|
|
3623
|
+
stroke: "rgba(148,163,184,0.14)",
|
|
3624
|
+
strokeDasharray: "3 3",
|
|
3625
|
+
vertical: false
|
|
3626
|
+
}
|
|
3627
|
+
),
|
|
3628
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3629
|
+
recharts.XAxis,
|
|
3630
|
+
{
|
|
3631
|
+
dataKey: "date",
|
|
3632
|
+
tick: CHART_AXIS_TICK,
|
|
3633
|
+
tickLine: false,
|
|
3634
|
+
axisLine: false,
|
|
3635
|
+
tickFormatter: (value, index) => {
|
|
3636
|
+
const row = series[index];
|
|
3637
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel(String(value));
|
|
3638
|
+
}
|
|
3639
|
+
}
|
|
3640
|
+
),
|
|
3641
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3642
|
+
recharts.YAxis,
|
|
3643
|
+
{
|
|
3644
|
+
yAxisId: "counts",
|
|
3645
|
+
width: 32,
|
|
3646
|
+
tick: CHART_AXIS_TICK,
|
|
3647
|
+
tickLine: false,
|
|
3648
|
+
axisLine: false,
|
|
3649
|
+
allowDecimals: false,
|
|
3650
|
+
tickFormatter: (value) => Math.floor(Number(value) || 0).toLocaleString()
|
|
3651
|
+
}
|
|
3652
|
+
),
|
|
3653
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3654
|
+
recharts.YAxis,
|
|
3655
|
+
{
|
|
3656
|
+
yAxisId: "revenue",
|
|
3657
|
+
orientation: "right",
|
|
3658
|
+
width: 40,
|
|
3659
|
+
tick: CHART_AXIS_TICK,
|
|
3660
|
+
tickLine: false,
|
|
3661
|
+
axisLine: false,
|
|
3662
|
+
tickFormatter: (value) => formatCompactCurrency(Number(value) || 0)
|
|
3663
|
+
}
|
|
3664
|
+
),
|
|
3665
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3666
|
+
recharts.Tooltip,
|
|
3667
|
+
{
|
|
3668
|
+
content: /* @__PURE__ */ jsxRuntime.jsx(TrendTooltip, {}),
|
|
3669
|
+
cursor: {
|
|
3670
|
+
stroke: "rgba(248, 250, 252, 0.35)",
|
|
3671
|
+
strokeWidth: 1
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
),
|
|
3675
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3676
|
+
recharts.Area,
|
|
3677
|
+
{
|
|
3678
|
+
yAxisId: "counts",
|
|
3679
|
+
type: "natural",
|
|
3680
|
+
dataKey: "units_sold",
|
|
3681
|
+
stroke: "none",
|
|
3682
|
+
fill: "url(#productUnitsAreaFill)",
|
|
3683
|
+
isAnimationActive: false,
|
|
3684
|
+
legendType: "none"
|
|
3685
|
+
}
|
|
3686
|
+
),
|
|
3687
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3688
|
+
recharts.Area,
|
|
3689
|
+
{
|
|
3690
|
+
yAxisId: "counts",
|
|
3691
|
+
type: "natural",
|
|
3692
|
+
dataKey: "views",
|
|
3693
|
+
stroke: "none",
|
|
3694
|
+
fill: "url(#productViewsAreaFill)",
|
|
3695
|
+
isAnimationActive: false,
|
|
3696
|
+
legendType: "none"
|
|
3697
|
+
}
|
|
3698
|
+
),
|
|
3699
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3700
|
+
recharts.Area,
|
|
3701
|
+
{
|
|
3702
|
+
yAxisId: "revenue",
|
|
3703
|
+
type: "natural",
|
|
3704
|
+
dataKey: "revenue",
|
|
3705
|
+
stroke: "none",
|
|
3706
|
+
fill: "url(#productRevenueAreaFill)",
|
|
3707
|
+
isAnimationActive: false,
|
|
3708
|
+
legendType: "none"
|
|
3709
|
+
}
|
|
3710
|
+
),
|
|
3711
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3712
|
+
recharts.Line,
|
|
3713
|
+
{
|
|
3714
|
+
yAxisId: "counts",
|
|
3715
|
+
type: "natural",
|
|
3716
|
+
dataKey: "units_sold",
|
|
3717
|
+
name: "Units sold",
|
|
3718
|
+
stroke: "url(#productUnitsGradient)",
|
|
3719
|
+
strokeWidth: 2,
|
|
3720
|
+
dot: false,
|
|
3721
|
+
activeDot: { r: 4, fill: PRODUCT_CHART_COLORS.units }
|
|
3722
|
+
}
|
|
3723
|
+
),
|
|
3724
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3725
|
+
recharts.Line,
|
|
3726
|
+
{
|
|
3727
|
+
yAxisId: "counts",
|
|
3728
|
+
type: "natural",
|
|
3729
|
+
dataKey: "views",
|
|
3730
|
+
name: "Views",
|
|
3731
|
+
stroke: PRODUCT_CHART_COLORS.views,
|
|
3732
|
+
strokeWidth: 1.5,
|
|
3733
|
+
strokeDasharray: "4 3",
|
|
3734
|
+
dot: false,
|
|
3735
|
+
activeDot: { r: 4, fill: PRODUCT_CHART_COLORS.views }
|
|
3736
|
+
}
|
|
3737
|
+
),
|
|
3738
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3739
|
+
recharts.Line,
|
|
3740
|
+
{
|
|
3741
|
+
yAxisId: "revenue",
|
|
3742
|
+
type: "natural",
|
|
3743
|
+
dataKey: "revenue",
|
|
3744
|
+
name: "Revenue",
|
|
3745
|
+
stroke: "url(#productRevenueGradient)",
|
|
3746
|
+
strokeWidth: 2,
|
|
3747
|
+
dot: false,
|
|
3748
|
+
activeDot: { r: 4, fill: PRODUCT_CHART_COLORS.revenue }
|
|
3749
|
+
}
|
|
3750
|
+
)
|
|
3751
|
+
]
|
|
3752
|
+
}
|
|
3753
|
+
) }) })
|
|
3754
|
+
] })
|
|
3755
|
+
] })
|
|
3756
|
+
] })
|
|
3757
|
+
}
|
|
3758
|
+
) }),
|
|
3759
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-12 flex flex-col gap-2 lg:col-span-6", children: [
|
|
3760
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3761
|
+
AnalyticsSection,
|
|
3762
|
+
{
|
|
3763
|
+
variant: "atlas",
|
|
3764
|
+
title: "Pulse & range",
|
|
3765
|
+
description: "Totals for the chart window above — independent of the summary period filter.",
|
|
3766
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2", children: quickPulseMetrics.map((metric) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3767
|
+
"div",
|
|
3768
|
+
{
|
|
3769
|
+
className: `rounded-lg border border-ui-border-base bg-ui-bg-base px-2 py-2 shadow-sm ${metric.accentClassName}`.trim(),
|
|
3770
|
+
children: [
|
|
3771
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: metric.label }),
|
|
3772
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base mt-0.5 text-sm font-semibold tracking-tight", children: metric.value }),
|
|
3773
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted mt-0.5 text-[9px] leading-snug", children: metric.helper })
|
|
3774
|
+
]
|
|
3775
|
+
},
|
|
3776
|
+
metric.label
|
|
3777
|
+
)) })
|
|
3778
|
+
}
|
|
3779
|
+
),
|
|
3780
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3781
|
+
AnalyticsSection,
|
|
3782
|
+
{
|
|
3783
|
+
variant: "atlas",
|
|
3784
|
+
actionsBare: true,
|
|
3785
|
+
title: "Best sellers",
|
|
3786
|
+
description: bestSellerTrendProductTitle ? `Revenue and units over time for “${bestSellerTrendProductTitle}” (#1 by revenue in this window; all-time uses a 12‑month chart).` : "Revenue and units over time for the top product by revenue in this window (all-time leaderboard uses a 12‑month chart).",
|
|
3787
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
3788
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3789
|
+
ui.Label,
|
|
3790
|
+
{
|
|
3791
|
+
htmlFor: "products-top-seller-period",
|
|
3792
|
+
className: "shrink-0 text-ui-fg-muted text-xs font-medium",
|
|
3793
|
+
children: "Window"
|
|
3794
|
+
}
|
|
3795
|
+
),
|
|
3796
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3797
|
+
"select",
|
|
3798
|
+
{
|
|
3799
|
+
id: "products-top-seller-period",
|
|
3800
|
+
value: topSellerPeriod,
|
|
3801
|
+
onChange: (event) => setTopSellerPeriod(event.target.value),
|
|
3802
|
+
className: SELECT_CLASS_NAME,
|
|
3803
|
+
children: TOP_SELLER_PERIODS.map((period) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: period.value, children: period.label }, period.value))
|
|
3804
|
+
}
|
|
3805
|
+
)
|
|
3806
|
+
] }),
|
|
3807
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", className: "min-w-0 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[212px] min-w-[280px] w-full sm:h-[228px]", children: bestSellersTrendLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) : bestSellersTrendError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: bestSellersTrendError }) }) : bestSellersTrendSeries.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "No product sales for this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", minWidth: 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3808
|
+
recharts.ComposedChart,
|
|
3809
|
+
{
|
|
3810
|
+
data: bestSellersTrendSeries,
|
|
3811
|
+
margin: { left: 0, right: 8, top: 6, bottom: 2 },
|
|
3812
|
+
children: [
|
|
3813
|
+
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3814
|
+
"linearGradient",
|
|
3815
|
+
{
|
|
3816
|
+
id: "bestSellersRevenueArea",
|
|
3817
|
+
x1: "0",
|
|
3818
|
+
y1: "0",
|
|
3819
|
+
x2: "0",
|
|
3820
|
+
y2: "1",
|
|
3821
|
+
children: [
|
|
3822
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3823
|
+
"stop",
|
|
3824
|
+
{
|
|
3825
|
+
offset: "0%",
|
|
3826
|
+
stopColor: PRODUCT_CHART_COLORS.revenue,
|
|
3827
|
+
stopOpacity: 0.22
|
|
3828
|
+
}
|
|
3829
|
+
),
|
|
3830
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3831
|
+
"stop",
|
|
3832
|
+
{
|
|
3833
|
+
offset: "100%",
|
|
3834
|
+
stopColor: PRODUCT_CHART_COLORS.revenue,
|
|
3835
|
+
stopOpacity: 0
|
|
3836
|
+
}
|
|
3837
|
+
)
|
|
3838
|
+
]
|
|
3839
|
+
}
|
|
3840
|
+
) }),
|
|
3841
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3842
|
+
recharts.CartesianGrid,
|
|
3843
|
+
{
|
|
3844
|
+
strokeDasharray: "3 3",
|
|
3845
|
+
vertical: false,
|
|
3846
|
+
stroke: "rgba(148,163,184,0.12)"
|
|
3847
|
+
}
|
|
3848
|
+
),
|
|
3849
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3850
|
+
recharts.XAxis,
|
|
3851
|
+
{
|
|
3852
|
+
dataKey: "date",
|
|
3853
|
+
tick: CHART_AXIS_TICK_SM,
|
|
3854
|
+
tickFormatter: (value) => formatChartLabel(String(value)),
|
|
3855
|
+
tickLine: false,
|
|
3856
|
+
axisLine: { stroke: CHART_AXIS_LINE }
|
|
3857
|
+
}
|
|
3858
|
+
),
|
|
3859
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3860
|
+
recharts.YAxis,
|
|
3861
|
+
{
|
|
3862
|
+
yAxisId: "units",
|
|
3863
|
+
tick: CHART_AXIS_TICK_SM,
|
|
3864
|
+
tickFormatter: (value) => formatShortNumber(Number(value)),
|
|
3865
|
+
width: 36,
|
|
3866
|
+
tickLine: false,
|
|
3867
|
+
axisLine: { stroke: CHART_AXIS_LINE },
|
|
3868
|
+
allowDecimals: false
|
|
3869
|
+
}
|
|
3870
|
+
),
|
|
3871
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3872
|
+
recharts.YAxis,
|
|
3873
|
+
{
|
|
3874
|
+
yAxisId: "revenue",
|
|
3875
|
+
orientation: "right",
|
|
3876
|
+
tick: CHART_AXIS_TICK_SM,
|
|
3877
|
+
tickFormatter: (value) => formatCompactCurrency(Number(value)),
|
|
3878
|
+
width: 44,
|
|
3879
|
+
tickLine: false,
|
|
3880
|
+
axisLine: { stroke: CHART_AXIS_LINE }
|
|
3881
|
+
}
|
|
3882
|
+
),
|
|
3883
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3884
|
+
recharts.Tooltip,
|
|
3885
|
+
{
|
|
3886
|
+
content: (tooltipProps) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3887
|
+
MiniTrendTooltip,
|
|
3888
|
+
{
|
|
3889
|
+
...tooltipProps,
|
|
3890
|
+
productHeadline: bestSellerTrendProductTitle,
|
|
3891
|
+
hideViewsRow: true
|
|
3892
|
+
}
|
|
3893
|
+
)
|
|
3894
|
+
}
|
|
3895
|
+
),
|
|
3896
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3897
|
+
recharts.Area,
|
|
3898
|
+
{
|
|
3899
|
+
yAxisId: "revenue",
|
|
3900
|
+
type: "natural",
|
|
3901
|
+
dataKey: "revenue",
|
|
3902
|
+
name: "Revenue",
|
|
3903
|
+
stroke: "transparent",
|
|
3904
|
+
fill: "url(#bestSellersRevenueArea)",
|
|
3905
|
+
isAnimationActive: false
|
|
3906
|
+
}
|
|
3907
|
+
),
|
|
3908
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3909
|
+
recharts.Line,
|
|
3910
|
+
{
|
|
3911
|
+
yAxisId: "revenue",
|
|
3912
|
+
type: "natural",
|
|
3913
|
+
dataKey: "revenue",
|
|
3914
|
+
name: "Revenue",
|
|
3915
|
+
stroke: PRODUCT_CHART_COLORS.revenue,
|
|
3916
|
+
strokeWidth: 2,
|
|
3917
|
+
dot: false,
|
|
3918
|
+
activeDot: { r: 4, fill: PRODUCT_CHART_COLORS.revenue },
|
|
3919
|
+
isAnimationActive: false
|
|
3920
|
+
}
|
|
3921
|
+
),
|
|
3922
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3923
|
+
recharts.Line,
|
|
3924
|
+
{
|
|
3925
|
+
yAxisId: "units",
|
|
3926
|
+
type: "natural",
|
|
3927
|
+
dataKey: "units_sold",
|
|
3928
|
+
name: "Units sold",
|
|
3929
|
+
stroke: PRODUCT_CHART_COLORS.units,
|
|
3930
|
+
strokeWidth: 2,
|
|
3931
|
+
dot: false,
|
|
3932
|
+
activeDot: { r: 4, fill: PRODUCT_CHART_COLORS.units },
|
|
3933
|
+
isAnimationActive: false
|
|
3934
|
+
}
|
|
3935
|
+
)
|
|
3936
|
+
]
|
|
3937
|
+
}
|
|
3938
|
+
) }) }) })
|
|
3939
|
+
}
|
|
3940
|
+
)
|
|
3941
|
+
] })
|
|
3942
|
+
] }),
|
|
3943
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 grid grid-cols-1 gap-2 md:grid-cols-2 md:items-stretch xl:grid-cols-3 xl:gap-3", children: [
|
|
3944
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3945
|
+
AnalyticsSection,
|
|
3946
|
+
{
|
|
3947
|
+
variant: "atlas",
|
|
3948
|
+
className: "min-w-0 md:col-span-1 xl:col-span-1",
|
|
3949
|
+
title: "Leaderboard table",
|
|
3950
|
+
description: "Products ranked by revenue — same window as Best sellers.",
|
|
3951
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-[212px] min-w-0 sm:min-h-[228px]", children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTableSurface, { className: "shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { className: LEADERBOARD_TABLE_CLASS, children: [
|
|
3952
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3953
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: TABLE_HEAD_CELL_NOWRAP, children: "Product" }),
|
|
3954
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: TABLE_HEAD_CELL_NOWRAP, children: "Units" }),
|
|
3955
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: TABLE_HEAD_CELL_NOWRAP, children: "Orders" }),
|
|
3956
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: TABLE_HEAD_CELL_NOWRAP, children: "Revenue" })
|
|
3957
|
+
] }) }),
|
|
3958
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Body, { children: [
|
|
3959
|
+
topSellersError ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3960
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-xs", children: topSellersError }) }),
|
|
3961
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
3962
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
3963
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {})
|
|
3964
|
+
] }) : null,
|
|
3965
|
+
!topSellersError ? ((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 8).map((product) => {
|
|
3966
|
+
var _a2, _b2;
|
|
3967
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3968
|
+
ui.Table.Row,
|
|
3969
|
+
{
|
|
3970
|
+
className: product.product_id === ((_b2 = (_a2 = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.product_id) ? "bg-emerald-500/5" : void 0,
|
|
3971
|
+
children: [
|
|
3972
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.product_title }),
|
|
3973
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.units_sold.toLocaleString() }),
|
|
3974
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.order_count.toLocaleString() }),
|
|
3975
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatCurrency(product.revenue) })
|
|
3976
|
+
]
|
|
3977
|
+
},
|
|
3978
|
+
product.product_id
|
|
3979
|
+
);
|
|
3980
|
+
}) : null,
|
|
3981
|
+
!topSellersLoading && !topSellersError && (((_j = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _j.length) ?? 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
3982
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "No best seller data yet." }) }),
|
|
3983
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
3984
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
3985
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {})
|
|
3986
|
+
] }) : null
|
|
3987
|
+
] })
|
|
3988
|
+
] }) }) })
|
|
3989
|
+
}
|
|
3990
|
+
),
|
|
3991
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3992
|
+
AnalyticsSection,
|
|
3993
|
+
{
|
|
3994
|
+
variant: "atlas",
|
|
3995
|
+
className: "min-w-0 md:col-span-1 xl:col-span-1",
|
|
3996
|
+
title: "Most viewed products",
|
|
3997
|
+
description: mostViewedTrendProductTitle ? `Views over time for “${mostViewedTrendProductTitle}” (top of the list for your summary period; buckets follow period: today / 7d → week, 30d → month, 90d / all → year).` : "Views over time for the most-viewed product in your summary period (buckets: today / 7d → week, 30d → month, 90d / all → year).",
|
|
3998
|
+
children: performanceLoading ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[212px] items-center justify-center sm:h-[228px]", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) }) : performanceError ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[212px] items-center justify-center px-2 sm:h-[228px]", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: performanceError }) }) }) : !(performance == null ? void 0 : performance.productViewsConnected) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-[212px] sm:min-h-[228px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
3999
|
+
EmptyAnalyticsPanel,
|
|
4000
|
+
{
|
|
4001
|
+
title: "Views unavailable",
|
|
4002
|
+
description: "Product view tracking is not available in this environment."
|
|
4003
|
+
}
|
|
4004
|
+
) }) : ((performance == null ? void 0 : performance.topViewedProducts) ?? []).length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-[212px] sm:min-h-[228px]", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4005
|
+
EmptyAnalyticsPanel,
|
|
4006
|
+
{
|
|
4007
|
+
title: "No views",
|
|
4008
|
+
description: "No product views found for this summary period."
|
|
4009
|
+
}
|
|
4010
|
+
) }) : /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", className: "min-w-0 overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[212px] min-w-[280px] w-full sm:h-[228px]", children: mostViewedTrendLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading chart…" }) }) : mostViewedTrendError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: mostViewedTrendError }) }) : mostViewedTrendSeries.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "No view data in this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", minWidth: 0, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4011
|
+
recharts.ComposedChart,
|
|
4012
|
+
{
|
|
4013
|
+
data: mostViewedTrendSeries,
|
|
4014
|
+
margin: { left: 0, right: 8, top: 6, bottom: 2 },
|
|
4015
|
+
children: [
|
|
4016
|
+
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4017
|
+
"linearGradient",
|
|
4018
|
+
{
|
|
4019
|
+
id: "mostViewedViewsArea",
|
|
4020
|
+
x1: "0",
|
|
4021
|
+
y1: "0",
|
|
4022
|
+
x2: "0",
|
|
4023
|
+
y2: "1",
|
|
4024
|
+
children: [
|
|
4025
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4026
|
+
"stop",
|
|
4027
|
+
{
|
|
4028
|
+
offset: "0%",
|
|
4029
|
+
stopColor: PRODUCT_CHART_COLORS.views,
|
|
4030
|
+
stopOpacity: 0.22
|
|
4031
|
+
}
|
|
4032
|
+
),
|
|
4033
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4034
|
+
"stop",
|
|
4035
|
+
{
|
|
4036
|
+
offset: "100%",
|
|
4037
|
+
stopColor: PRODUCT_CHART_COLORS.views,
|
|
4038
|
+
stopOpacity: 0
|
|
4039
|
+
}
|
|
4040
|
+
)
|
|
4041
|
+
]
|
|
4042
|
+
}
|
|
4043
|
+
) }),
|
|
4044
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4045
|
+
recharts.CartesianGrid,
|
|
4046
|
+
{
|
|
4047
|
+
strokeDasharray: "3 3",
|
|
4048
|
+
vertical: false,
|
|
4049
|
+
stroke: "rgba(148,163,184,0.12)"
|
|
4050
|
+
}
|
|
4051
|
+
),
|
|
4052
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4053
|
+
recharts.XAxis,
|
|
4054
|
+
{
|
|
4055
|
+
dataKey: "date",
|
|
4056
|
+
tick: CHART_AXIS_TICK_SM,
|
|
4057
|
+
tickFormatter: (value) => formatChartLabel(String(value)),
|
|
4058
|
+
tickLine: false,
|
|
4059
|
+
axisLine: { stroke: CHART_AXIS_LINE }
|
|
4060
|
+
}
|
|
4061
|
+
),
|
|
4062
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4063
|
+
recharts.YAxis,
|
|
4064
|
+
{
|
|
4065
|
+
tick: CHART_AXIS_TICK_SM,
|
|
4066
|
+
tickFormatter: (value) => formatShortNumber(Number(value)),
|
|
4067
|
+
width: 36,
|
|
4068
|
+
tickLine: false,
|
|
4069
|
+
axisLine: { stroke: CHART_AXIS_LINE },
|
|
4070
|
+
allowDecimals: false
|
|
4071
|
+
}
|
|
4072
|
+
),
|
|
4073
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4074
|
+
recharts.Tooltip,
|
|
4075
|
+
{
|
|
4076
|
+
content: (tooltipProps) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4077
|
+
MiniTrendTooltip,
|
|
4078
|
+
{
|
|
4079
|
+
...tooltipProps,
|
|
4080
|
+
productHeadline: mostViewedTrendProductTitle,
|
|
4081
|
+
hideUnitsRow: true,
|
|
4082
|
+
hideRevenueRow: true
|
|
4083
|
+
}
|
|
4084
|
+
)
|
|
4085
|
+
}
|
|
4086
|
+
),
|
|
4087
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4088
|
+
recharts.Area,
|
|
4089
|
+
{
|
|
4090
|
+
type: "natural",
|
|
4091
|
+
dataKey: "views",
|
|
4092
|
+
name: "Views",
|
|
4093
|
+
stroke: "transparent",
|
|
4094
|
+
fill: "url(#mostViewedViewsArea)",
|
|
4095
|
+
isAnimationActive: false
|
|
4096
|
+
}
|
|
4097
|
+
),
|
|
4098
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4099
|
+
recharts.Line,
|
|
4100
|
+
{
|
|
4101
|
+
type: "natural",
|
|
4102
|
+
dataKey: "views",
|
|
4103
|
+
name: "Views",
|
|
4104
|
+
stroke: PRODUCT_CHART_COLORS.views,
|
|
4105
|
+
strokeWidth: 2,
|
|
4106
|
+
dot: false,
|
|
4107
|
+
activeDot: { r: 4, fill: PRODUCT_CHART_COLORS.views },
|
|
4108
|
+
isAnimationActive: false
|
|
4109
|
+
}
|
|
4110
|
+
)
|
|
4111
|
+
]
|
|
4112
|
+
}
|
|
4113
|
+
) }) }) })
|
|
4114
|
+
}
|
|
4115
|
+
),
|
|
4116
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4117
|
+
AnalyticsSection,
|
|
4118
|
+
{
|
|
4119
|
+
variant: "atlas",
|
|
4120
|
+
className: "min-w-0 md:col-span-2 xl:col-span-1",
|
|
4121
|
+
title: "View-to-sales opportunities",
|
|
4122
|
+
description: "Products with attention but weaker unit conversion.",
|
|
4123
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-[212px] min-w-0 sm:min-h-[228px]", children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTableSurface, { className: "shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { className: OPPORTUNITIES_TABLE_CLASS, children: [
|
|
4124
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
4125
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: TABLE_HEAD_CELL_NOWRAP, children: "Product" }),
|
|
4126
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: TABLE_HEAD_CELL_NOWRAP, children: "Views" }),
|
|
4127
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: TABLE_HEAD_CELL_NOWRAP, children: "Units" }),
|
|
4128
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: TABLE_HEAD_CELL_NOWRAP, children: "Views / unit" })
|
|
4129
|
+
] }) }),
|
|
4130
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Body, { children: [
|
|
4131
|
+
((performance == null ? void 0 : performance.viewOpportunities) ?? []).slice(0, 8).map((product) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
4132
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.product_title }),
|
|
4133
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.total_views.toLocaleString() }),
|
|
4134
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.units_sold.toLocaleString() }),
|
|
4135
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatRatio(product.views_per_unit) })
|
|
4136
|
+
] }, product.product_id)),
|
|
4137
|
+
!performanceLoading && !performanceError && (((_k = performance == null ? void 0 : performance.viewOpportunities) == null ? void 0 : _k.length) ?? 0) === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
4138
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "No product view opportunities yet." }) }),
|
|
4139
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
4140
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
4141
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {})
|
|
4142
|
+
] }) : null
|
|
4143
|
+
] })
|
|
4144
|
+
] }) }) })
|
|
4145
|
+
}
|
|
4146
|
+
)
|
|
4147
|
+
] }),
|
|
4148
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4149
|
+
AnalyticsSection,
|
|
4150
|
+
{
|
|
4151
|
+
variant: "atlas",
|
|
4152
|
+
className: "mt-2",
|
|
4153
|
+
title: "Views vs units sold",
|
|
4154
|
+
description: "Each point is a product (top viewed in this period). Requires product view tracking.",
|
|
4155
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[220px] w-full sm:h-[248px]", children: performanceLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading scatter…" }) }) : performanceError || !performance ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center px-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: performanceError ?? "Failed to load product performance" }) }) : !performance.productViewsConnected ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Product view tracking is not available in this environment." }) }) : viewsVsUnitsScatterData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "No products with views or sales in this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.ScatterChart, { margin: { top: 12, right: 16, bottom: 8, left: 8 }, children: [
|
|
4156
|
+
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("radialGradient", { id: "productScatterGlow", cx: "50%", cy: "50%", r: "50%", children: [
|
|
4157
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#E879F9", stopOpacity: 0.95 }),
|
|
4158
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#6366F1", stopOpacity: 0.65 })
|
|
4159
|
+
] }) }),
|
|
4160
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4161
|
+
recharts.CartesianGrid,
|
|
4162
|
+
{
|
|
4163
|
+
strokeDasharray: "3 3",
|
|
4164
|
+
stroke: "rgba(148,163,184,0.14)"
|
|
4165
|
+
}
|
|
4166
|
+
),
|
|
4167
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4168
|
+
recharts.XAxis,
|
|
4169
|
+
{
|
|
4170
|
+
type: "number",
|
|
4171
|
+
dataKey: "total_views",
|
|
4172
|
+
name: "Views",
|
|
4173
|
+
tick: CHART_AXIS_TICK_SM,
|
|
4174
|
+
stroke: CHART_AXIS_LINE,
|
|
4175
|
+
tickLine: { stroke: CHART_AXIS_LINE },
|
|
4176
|
+
allowDecimals: false,
|
|
4177
|
+
label: {
|
|
4178
|
+
value: "Views",
|
|
4179
|
+
position: "insideBottom",
|
|
4180
|
+
offset: -4,
|
|
4181
|
+
fontSize: 10,
|
|
4182
|
+
fill: "#9ca3af"
|
|
4183
|
+
}
|
|
4184
|
+
}
|
|
4185
|
+
),
|
|
4186
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4187
|
+
recharts.YAxis,
|
|
4188
|
+
{
|
|
4189
|
+
type: "number",
|
|
4190
|
+
dataKey: "units_sold",
|
|
4191
|
+
name: "Units sold",
|
|
4192
|
+
tick: CHART_AXIS_TICK_SM,
|
|
4193
|
+
stroke: CHART_AXIS_LINE,
|
|
4194
|
+
tickLine: { stroke: CHART_AXIS_LINE },
|
|
4195
|
+
allowDecimals: false,
|
|
4196
|
+
label: {
|
|
4197
|
+
value: "Units sold",
|
|
4198
|
+
angle: -90,
|
|
4199
|
+
position: "insideLeft",
|
|
4200
|
+
fontSize: 10,
|
|
4201
|
+
fill: "#9ca3af"
|
|
4202
|
+
}
|
|
4203
|
+
}
|
|
4204
|
+
),
|
|
4205
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4206
|
+
recharts.Tooltip,
|
|
4207
|
+
{
|
|
4208
|
+
cursor: { strokeDasharray: "4 4", stroke: "rgba(148,163,184,0.45)" },
|
|
4209
|
+
content: /* @__PURE__ */ jsxRuntime.jsx(ProductBarTooltip, {})
|
|
4210
|
+
}
|
|
4211
|
+
),
|
|
4212
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4213
|
+
recharts.Scatter,
|
|
4214
|
+
{
|
|
4215
|
+
name: "Products",
|
|
4216
|
+
data: viewsVsUnitsScatterData,
|
|
4217
|
+
fill: "url(#productScatterGlow)"
|
|
4218
|
+
}
|
|
4219
|
+
)
|
|
4220
|
+
] }) }) }) })
|
|
4221
|
+
}
|
|
4222
|
+
)
|
|
4223
|
+
] })
|
|
4224
|
+
] }) });
|
|
4225
|
+
}
|
|
4226
|
+
const ANALYTICS_MODULES = [
|
|
4227
|
+
{ id: "orders", label: "Orders" },
|
|
4228
|
+
{ id: "customers", label: "Customers" },
|
|
4229
|
+
{ id: "products", label: "Products" }
|
|
4230
|
+
];
|
|
4231
|
+
const AnalyticsPage = () => {
|
|
4232
|
+
const [activeModule, setActiveModule] = react.useState("orders");
|
|
4233
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsx("main", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4234
|
+
"div",
|
|
4235
|
+
{
|
|
4236
|
+
className: `mx-auto flex w-full min-w-0 flex-col ${activeModule === "products" ? "max-w-full gap-2 px-3 py-3 sm:px-4 sm:py-3" : "max-w-[1600px] gap-4 p-4"}`.trim(),
|
|
4237
|
+
children: [
|
|
4238
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "overflow-hidden rounded-xl border border-ui-border-base bg-ui-bg-base p-0 shadow-sm", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 px-4 py-4 sm:gap-5 sm:px-5 sm:py-5 xl:flex-row xl:items-end xl:justify-between", children: [
|
|
4239
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 space-y-1.5", children: [
|
|
4240
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", className: "tracking-tight", children: "Analytics" }),
|
|
4241
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Review orders, customers, and products in one organized workspace." })
|
|
4242
|
+
] }),
|
|
4243
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4244
|
+
"div",
|
|
4245
|
+
{
|
|
4246
|
+
className: "flex shrink-0 flex-wrap items-center gap-1",
|
|
4247
|
+
role: "tablist",
|
|
4248
|
+
"aria-label": "Analytics modules",
|
|
4249
|
+
children: ANALYTICS_MODULES.map((m) => {
|
|
4250
|
+
const active = activeModule === m.id;
|
|
4251
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4252
|
+
"button",
|
|
4253
|
+
{
|
|
4254
|
+
type: "button",
|
|
4255
|
+
role: "tab",
|
|
4256
|
+
"aria-selected": active,
|
|
4257
|
+
onClick: () => setActiveModule(m.id),
|
|
4258
|
+
className: `rounded-md px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ui-fg-interactive focus-visible:ring-offset-2 focus-visible:ring-offset-ui-bg-base ${active ? "bg-ui-bg-base text-ui-fg-base shadow-sm" : "text-ui-fg-muted hover:bg-ui-bg-base/60 hover:text-ui-fg-base"}`.trim(),
|
|
4259
|
+
children: m.label
|
|
4260
|
+
},
|
|
4261
|
+
m.id
|
|
4262
|
+
);
|
|
4263
|
+
})
|
|
4264
|
+
}
|
|
4265
|
+
)
|
|
4266
|
+
] }) }),
|
|
4267
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 w-full", children: [
|
|
4268
|
+
activeModule === "orders" && /* @__PURE__ */ jsxRuntime.jsx(OrdersDashboard, {}),
|
|
4269
|
+
activeModule === "customers" && /* @__PURE__ */ jsxRuntime.jsx(CustomersDashboard, {}),
|
|
4270
|
+
activeModule === "products" && /* @__PURE__ */ jsxRuntime.jsx(ProductsDashboard, {})
|
|
4271
|
+
] })
|
|
4272
|
+
]
|
|
4273
|
+
}
|
|
4274
|
+
) }) });
|
|
3331
4275
|
};
|
|
3332
4276
|
const config = adminSdk.defineRouteConfig({
|
|
3333
4277
|
label: "Analytics",
|