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