medusa-analytics 0.0.23 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/admin/index.js +530 -338
- package/.medusa/server/src/admin/index.mjs +532 -340
- package/.medusa/server/src/api/admin/analytics/fetch-orders-for-analytics.js +21 -9
- package/.medusa/server/src/api/admin/analytics/orders-analytics-meta.js +18 -0
- package/.medusa/server/src/api/admin/analytics/orders-insights/route.js +3 -2
- package/.medusa/server/src/api/admin/analytics/orders-over-time/route.js +3 -2
- package/.medusa/server/src/api/admin/analytics/orders-summary/route.js +3 -2
- package/.medusa/server/src/api/admin/analytics/repeat-customers/route.js +3 -2
- package/.medusa/server/src/api/admin/analytics/store-context/resolve-default-currency-code.js +86 -0
- package/.medusa/server/src/api/admin/analytics/store-context/route.js +19 -0
- package/.medusa/server/src/api/admin/analytics/store-context/types.js +3 -0
- package/package.json +4 -2
|
@@ -1,9 +1,67 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useMemo,
|
|
2
|
+
import { createContext, useState, useEffect, useCallback, useMemo, useContext, useId } from "react";
|
|
3
3
|
import { defineRouteConfig } from "@medusajs/admin-sdk";
|
|
4
4
|
import { CurrencyDollar, ShoppingCart, TruckFast, Cash, UsersSolid, User, UserGroup, Trash, Tag, CubeSolid, ChartBar } from "@medusajs/icons";
|
|
5
5
|
import { Container, Text, Heading, Label, Table } from "@medusajs/ui";
|
|
6
|
-
import { ResponsiveContainer, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, Bar,
|
|
6
|
+
import { ResponsiveContainer, BarChart, CartesianGrid, XAxis, YAxis, Tooltip, Bar, PieChart, Pie, Cell, ComposedChart, Legend, Area, LineChart, Line, ReferenceLine, Sector, ScatterChart, Scatter } from "recharts";
|
|
7
|
+
const AnalyticsCurrencyContext = createContext(
|
|
8
|
+
null
|
|
9
|
+
);
|
|
10
|
+
function formatMoney(value, currencyCode, compact) {
|
|
11
|
+
return new Intl.NumberFormat(void 0, {
|
|
12
|
+
style: "currency",
|
|
13
|
+
currency: currencyCode,
|
|
14
|
+
minimumFractionDigits: 0,
|
|
15
|
+
maximumFractionDigits: compact ? 1 : 0,
|
|
16
|
+
...compact ? { notation: "compact" } : {}
|
|
17
|
+
}).format(value);
|
|
18
|
+
}
|
|
19
|
+
function AnalyticsCurrencyProvider({ children }) {
|
|
20
|
+
const [currencyCode, setCurrencyCode] = useState("USD");
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
let cancelled = false;
|
|
23
|
+
fetch("/admin/analytics/store-context", { credentials: "include" }).then((res) => {
|
|
24
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
25
|
+
return res.json();
|
|
26
|
+
}).then((body) => {
|
|
27
|
+
if (cancelled) return;
|
|
28
|
+
const code = typeof body.default_currency_code === "string" ? body.default_currency_code.trim().toUpperCase() : "";
|
|
29
|
+
if (code && /^[A-Z]{3}$/.test(code)) {
|
|
30
|
+
setCurrencyCode(code);
|
|
31
|
+
}
|
|
32
|
+
}).catch(() => {
|
|
33
|
+
if (!cancelled) setCurrencyCode("USD");
|
|
34
|
+
});
|
|
35
|
+
return () => {
|
|
36
|
+
cancelled = true;
|
|
37
|
+
};
|
|
38
|
+
}, []);
|
|
39
|
+
const formatCurrency = useCallback(
|
|
40
|
+
(value2) => formatMoney(value2, currencyCode, false),
|
|
41
|
+
[currencyCode]
|
|
42
|
+
);
|
|
43
|
+
const formatCompactCurrency = useCallback(
|
|
44
|
+
(value2) => formatMoney(value2, currencyCode, true),
|
|
45
|
+
[currencyCode]
|
|
46
|
+
);
|
|
47
|
+
const value = useMemo(
|
|
48
|
+
() => ({
|
|
49
|
+
currencyCode,
|
|
50
|
+
formatCurrency,
|
|
51
|
+
formatCompactCurrency
|
|
52
|
+
}),
|
|
53
|
+
[currencyCode, formatCurrency, formatCompactCurrency]
|
|
54
|
+
);
|
|
55
|
+
return /* @__PURE__ */ jsx(AnalyticsCurrencyContext.Provider, { value, children });
|
|
56
|
+
}
|
|
57
|
+
function useAnalyticsCurrency() {
|
|
58
|
+
const ctx = useContext(AnalyticsCurrencyContext);
|
|
59
|
+
if (!ctx) {
|
|
60
|
+
throw new Error("useAnalyticsCurrency must be used within AnalyticsCurrencyProvider");
|
|
61
|
+
}
|
|
62
|
+
return ctx;
|
|
63
|
+
}
|
|
64
|
+
const ORDERS_ANALYTICS_MAX_FETCH = 5e4;
|
|
7
65
|
const ACCENT_STYLES = {
|
|
8
66
|
blue: {
|
|
9
67
|
border: "border-sky-400/30",
|
|
@@ -262,30 +320,9 @@ function nonCancelledRevenue(d) {
|
|
|
262
320
|
}
|
|
263
321
|
return Math.max(0, d.total_revenue - (d.revenue_cancelled ?? 0));
|
|
264
322
|
}
|
|
265
|
-
function bucketAov(d) {
|
|
266
|
-
const ordNc = d.orders_count - d.cancelled_count;
|
|
267
|
-
if (ordNc <= 0) return 0;
|
|
268
|
-
return nonCancelledRevenue(d) / ordNc;
|
|
269
|
-
}
|
|
270
323
|
function isOrdersTodayPoint(value) {
|
|
271
324
|
return isDailyOrderRow(value) && "isToday" in value && typeof value.isToday === "boolean";
|
|
272
325
|
}
|
|
273
|
-
function formatCurrency$1(value) {
|
|
274
|
-
return new Intl.NumberFormat("en-IN", {
|
|
275
|
-
style: "currency",
|
|
276
|
-
currency: "INR",
|
|
277
|
-
minimumFractionDigits: 0,
|
|
278
|
-
maximumFractionDigits: 0
|
|
279
|
-
}).format(value);
|
|
280
|
-
}
|
|
281
|
-
function formatCompactCurrency$1(value) {
|
|
282
|
-
return new Intl.NumberFormat("en-IN", {
|
|
283
|
-
style: "currency",
|
|
284
|
-
currency: "INR",
|
|
285
|
-
notation: "compact",
|
|
286
|
-
maximumFractionDigits: 1
|
|
287
|
-
}).format(value);
|
|
288
|
-
}
|
|
289
326
|
function formatChartLabel$2(value) {
|
|
290
327
|
if (!value) return "";
|
|
291
328
|
const parsed = new Date(value);
|
|
@@ -382,13 +419,14 @@ function SalesBreakdownTooltip({
|
|
|
382
419
|
payload
|
|
383
420
|
}) {
|
|
384
421
|
var _a, _b;
|
|
422
|
+
const { formatCurrency } = useAnalyticsCurrency();
|
|
385
423
|
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
386
424
|
const row = isSalesBreakdownBarRow((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
387
425
|
if (!row) return null;
|
|
388
426
|
return /* @__PURE__ */ jsxs(AnalyticsTooltipCard, { variant: "compact", title: row.label, children: [
|
|
389
427
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
390
428
|
"Revenue: ",
|
|
391
|
-
/* @__PURE__ */ jsx("strong", { children: formatCurrency
|
|
429
|
+
/* @__PURE__ */ jsx("strong", { children: formatCurrency(row.revenue) })
|
|
392
430
|
] }),
|
|
393
431
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
394
432
|
"Orders:",
|
|
@@ -431,52 +469,223 @@ function OutcomesTrendTooltip({
|
|
|
431
469
|
/* @__PURE__ */ jsx("strong", { children: Math.floor(Number(entry.value) || 0).toLocaleString() })
|
|
432
470
|
] }, String(entry.name))) });
|
|
433
471
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
472
|
+
const TREND_MIX_PIE_COLORS = {
|
|
473
|
+
revenue: "#c084fc",
|
|
474
|
+
orders: "#7dd3fc",
|
|
475
|
+
aov: "#fdba74"
|
|
476
|
+
};
|
|
477
|
+
function buildTrendMixPieSlices(dailyOrders, trendRevenueTotal, trendOrdersTotal, formatCompactCurrencyFn, formatCurrencyFn) {
|
|
478
|
+
const ncRev = dailyOrders.reduce((s, d) => s + nonCancelledRevenue(d), 0);
|
|
479
|
+
const ncOrd = dailyOrders.reduce(
|
|
480
|
+
(s, d) => s + Math.max(0, d.orders_count - d.cancelled_count),
|
|
481
|
+
0
|
|
482
|
+
);
|
|
483
|
+
const windowAov = ncOrd > 0 ? ncRev / ncOrd : 0;
|
|
484
|
+
const lr = Math.log1p(Math.max(trendRevenueTotal, 0));
|
|
485
|
+
const lo = Math.log1p(Math.max(trendOrdersTotal, 0));
|
|
486
|
+
const la = Math.log1p(Math.max(windowAov, 0));
|
|
487
|
+
const sumW = lr + lo + la;
|
|
488
|
+
const share = (w) => sumW > 0 ? w / sumW * 100 : 100 / 3;
|
|
489
|
+
const sR = share(lr);
|
|
490
|
+
const sO = share(lo);
|
|
491
|
+
const sA = share(la);
|
|
492
|
+
return [
|
|
493
|
+
{
|
|
494
|
+
key: "revenue",
|
|
495
|
+
name: "Revenue",
|
|
496
|
+
value: sR,
|
|
497
|
+
fill: TREND_MIX_PIE_COLORS.revenue,
|
|
498
|
+
sharePercent: sR,
|
|
499
|
+
formattedPrimary: formatCompactCurrencyFn(trendRevenueTotal),
|
|
500
|
+
formattedDetail: "Total revenue for this range, all days summed."
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
key: "orders",
|
|
504
|
+
name: "Orders",
|
|
505
|
+
value: sO,
|
|
506
|
+
fill: TREND_MIX_PIE_COLORS.orders,
|
|
507
|
+
sharePercent: sO,
|
|
508
|
+
formattedPrimary: Math.floor(trendOrdersTotal).toLocaleString(),
|
|
509
|
+
formattedDetail: "Order count in this range from daily totals."
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
key: "aov",
|
|
513
|
+
name: "AOV",
|
|
514
|
+
value: sA,
|
|
515
|
+
fill: TREND_MIX_PIE_COLORS.aov,
|
|
516
|
+
sharePercent: sA,
|
|
517
|
+
formattedPrimary: formatCurrencyFn(windowAov),
|
|
518
|
+
formattedDetail: "Average per non-cancelled order for the window."
|
|
519
|
+
}
|
|
520
|
+
];
|
|
438
521
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
522
|
+
const TREND_MIX_LEADER_STROKE = "#b8956a";
|
|
523
|
+
function isTrendMixPayload(value) {
|
|
524
|
+
if (typeof value !== "object" || value === null) return false;
|
|
525
|
+
const k = value.key;
|
|
526
|
+
return k === "revenue" || k === "orders" || k === "aov";
|
|
527
|
+
}
|
|
528
|
+
function splitDetailTwoLines(s, firstLineMax) {
|
|
529
|
+
const t = s.trim();
|
|
530
|
+
if (t.length <= firstLineMax) return [t, ""];
|
|
531
|
+
const sliceEnd = t.lastIndexOf(" ", firstLineMax);
|
|
532
|
+
const cut = sliceEnd > firstLineMax * 0.45 ? sliceEnd : firstLineMax;
|
|
533
|
+
const a = t.slice(0, cut).trim();
|
|
534
|
+
const b = t.slice(cut).trim();
|
|
535
|
+
return b ? [a, b] : [a, ""];
|
|
536
|
+
}
|
|
537
|
+
function TrendMixExplodedSector(props) {
|
|
538
|
+
const {
|
|
539
|
+
cx,
|
|
540
|
+
cy,
|
|
541
|
+
innerRadius,
|
|
542
|
+
outerRadius,
|
|
543
|
+
startAngle,
|
|
544
|
+
endAngle,
|
|
545
|
+
fill,
|
|
546
|
+
stroke,
|
|
547
|
+
strokeWidth,
|
|
548
|
+
cornerRadius
|
|
549
|
+
} = props;
|
|
550
|
+
const midDeg = (Number(startAngle) + Number(endAngle)) / 2;
|
|
551
|
+
const rad = -midDeg * Math.PI / 180;
|
|
552
|
+
const offset = 19;
|
|
553
|
+
const ncx = (Number(cx) || 0) + offset * Math.cos(rad);
|
|
554
|
+
const ncy = (Number(cy) || 0) + offset * Math.sin(rad);
|
|
555
|
+
return /* @__PURE__ */ jsx(
|
|
556
|
+
Sector,
|
|
557
|
+
{
|
|
558
|
+
cx: ncx,
|
|
559
|
+
cy: ncy,
|
|
560
|
+
innerRadius,
|
|
561
|
+
outerRadius,
|
|
562
|
+
startAngle,
|
|
563
|
+
endAngle,
|
|
564
|
+
fill,
|
|
565
|
+
stroke,
|
|
566
|
+
strokeWidth,
|
|
567
|
+
cornerRadius,
|
|
568
|
+
style: {
|
|
569
|
+
filter: "drop-shadow(0 5px 6px rgba(0,0,0,0.5)) drop-shadow(0 2px 2px rgba(0,0,0,0.35))"
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
);
|
|
573
|
+
}
|
|
574
|
+
function createTrendMixInfographicLabel(slices) {
|
|
575
|
+
return function TrendMixInfographicLabel(props) {
|
|
576
|
+
const { cx, cy, midAngle, outerRadius, percent, payload, index } = props;
|
|
577
|
+
const slice = typeof index === "number" && slices[index] !== void 0 ? slices[index] : isTrendMixPayload(payload) ? payload : null;
|
|
578
|
+
if (slice === null || cx === void 0 || cy === void 0 || midAngle === void 0) {
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
const p = percent ?? 0;
|
|
582
|
+
const ang = midAngle;
|
|
583
|
+
const RAD = Math.PI / 180;
|
|
584
|
+
const cos = Math.cos(-RAD * ang);
|
|
585
|
+
const sin = Math.sin(-RAD * ang);
|
|
586
|
+
const or = Number(outerRadius);
|
|
587
|
+
if (!Number.isFinite(or)) return null;
|
|
588
|
+
const rimPad = 24;
|
|
589
|
+
const sx = Number(cx) + (or + rimPad) * cos;
|
|
590
|
+
const sy = Number(cy) + (or + rimPad) * sin;
|
|
591
|
+
const elbow = 30;
|
|
592
|
+
const mx = sx + elbow * cos;
|
|
593
|
+
const my = sy;
|
|
594
|
+
const extend = 58;
|
|
595
|
+
const rightSide = cos >= 0;
|
|
596
|
+
const hx = mx + (rightSide ? extend : -58);
|
|
597
|
+
const anchor = rightSide ? "start" : "end";
|
|
598
|
+
const tx = hx + (rightSide ? 4 : -4);
|
|
599
|
+
const yRule = my + 14;
|
|
600
|
+
const pctStr = `${(p * 100).toFixed(0)}%`;
|
|
601
|
+
const ruleHalfW = 52;
|
|
602
|
+
const xRuleA = rightSide ? tx - 2 : tx - ruleHalfW;
|
|
603
|
+
const xRuleB = rightSide ? tx + ruleHalfW : tx + 2;
|
|
604
|
+
const [detail1, detail2] = splitDetailTwoLines(slice.formattedDetail, 46);
|
|
605
|
+
return /* @__PURE__ */ jsxs("g", { "aria-hidden": true, children: [
|
|
606
|
+
/* @__PURE__ */ jsx(
|
|
607
|
+
"polyline",
|
|
608
|
+
{
|
|
609
|
+
points: `${sx},${sy} ${mx},${my} ${hx},${yRule - 18}`,
|
|
610
|
+
fill: "none",
|
|
611
|
+
stroke: TREND_MIX_LEADER_STROKE,
|
|
612
|
+
strokeWidth: 1.15,
|
|
613
|
+
strokeLinecap: "round",
|
|
614
|
+
strokeLinejoin: "round"
|
|
615
|
+
}
|
|
616
|
+
),
|
|
617
|
+
/* @__PURE__ */ jsx(
|
|
618
|
+
"circle",
|
|
619
|
+
{
|
|
620
|
+
cx: sx,
|
|
621
|
+
cy: sy,
|
|
622
|
+
r: 3.5,
|
|
623
|
+
fill: "none",
|
|
624
|
+
stroke: TREND_MIX_LEADER_STROKE,
|
|
625
|
+
strokeWidth: 1.15
|
|
626
|
+
}
|
|
627
|
+
),
|
|
628
|
+
/* @__PURE__ */ jsx(
|
|
629
|
+
"line",
|
|
630
|
+
{
|
|
631
|
+
x1: xRuleA,
|
|
632
|
+
y1: yRule,
|
|
633
|
+
x2: xRuleB,
|
|
634
|
+
y2: yRule,
|
|
635
|
+
stroke: TREND_MIX_LEADER_STROKE,
|
|
636
|
+
strokeWidth: 1.15,
|
|
637
|
+
strokeLinecap: "round"
|
|
638
|
+
}
|
|
639
|
+
),
|
|
640
|
+
/* @__PURE__ */ jsx(
|
|
641
|
+
"text",
|
|
642
|
+
{
|
|
643
|
+
x: tx,
|
|
644
|
+
y: yRule - 8,
|
|
645
|
+
textAnchor: anchor,
|
|
646
|
+
fill: "#f8fafc",
|
|
647
|
+
className: "text-[13px] font-bold tabular-nums",
|
|
648
|
+
children: pctStr
|
|
649
|
+
}
|
|
650
|
+
),
|
|
651
|
+
/* @__PURE__ */ jsx(
|
|
652
|
+
"text",
|
|
653
|
+
{
|
|
654
|
+
x: tx,
|
|
655
|
+
y: yRule + 16,
|
|
656
|
+
textAnchor: anchor,
|
|
657
|
+
fill: "#f1f5f9",
|
|
658
|
+
className: "text-[9px] font-semibold uppercase tracking-[0.16em]",
|
|
659
|
+
children: slice.name
|
|
660
|
+
}
|
|
661
|
+
),
|
|
662
|
+
/* @__PURE__ */ jsx(
|
|
663
|
+
"text",
|
|
664
|
+
{
|
|
665
|
+
x: tx,
|
|
666
|
+
y: yRule + 32,
|
|
667
|
+
textAnchor: anchor,
|
|
668
|
+
fill: "#f8fafc",
|
|
669
|
+
className: "text-[11px] font-bold tabular-nums",
|
|
670
|
+
children: slice.formattedPrimary
|
|
671
|
+
}
|
|
672
|
+
),
|
|
673
|
+
/* @__PURE__ */ jsxs(
|
|
674
|
+
"text",
|
|
675
|
+
{
|
|
676
|
+
x: tx,
|
|
677
|
+
y: yRule + 46,
|
|
678
|
+
textAnchor: anchor,
|
|
679
|
+
fill: "#94a3b8",
|
|
680
|
+
className: "text-[8px] leading-snug",
|
|
681
|
+
children: [
|
|
682
|
+
/* @__PURE__ */ jsx("tspan", { x: tx, dy: 0, children: detail1 }),
|
|
683
|
+
detail2 ? /* @__PURE__ */ jsx("tspan", { x: tx, dy: 11, children: detail2 }) : null
|
|
684
|
+
]
|
|
685
|
+
}
|
|
686
|
+
)
|
|
687
|
+
] });
|
|
688
|
+
};
|
|
480
689
|
}
|
|
481
690
|
const KPI_ICON_BG$2 = {
|
|
482
691
|
green: "bg-emerald-500/20",
|
|
@@ -550,7 +759,8 @@ function EmptyAnalyticsPanel$1({
|
|
|
550
759
|
] });
|
|
551
760
|
}
|
|
552
761
|
function OrdersDashboard() {
|
|
553
|
-
var _a, _b;
|
|
762
|
+
var _a, _b, _c, _d, _e;
|
|
763
|
+
const { formatCurrency, formatCompactCurrency } = useAnalyticsCurrency();
|
|
554
764
|
const [data, setData] = useState(null);
|
|
555
765
|
const [loading, setLoading] = useState(true);
|
|
556
766
|
const [error, setError] = useState(null);
|
|
@@ -560,6 +770,7 @@ function OrdersDashboard() {
|
|
|
560
770
|
const [overTimePeriod, setOverTimePeriod] = useState("one_week");
|
|
561
771
|
const [overTimeLoading, setOverTimeLoading] = useState(true);
|
|
562
772
|
const [overTimeError, setOverTimeError] = useState(null);
|
|
773
|
+
const [overTimeOrdersMeta, setOverTimeOrdersMeta] = useState(null);
|
|
563
774
|
const [ordersInsights, setOrdersInsights] = useState(null);
|
|
564
775
|
const [insightsLoading, setInsightsLoading] = useState(true);
|
|
565
776
|
const [insightsError, setInsightsError] = useState(null);
|
|
@@ -673,9 +884,13 @@ function OrdersDashboard() {
|
|
|
673
884
|
if (!cancelled) {
|
|
674
885
|
setDailyOrders(body.dailyOrders ?? []);
|
|
675
886
|
setPreviousPeriodDailyOrders(body.previousPeriodDailyOrders ?? []);
|
|
887
|
+
setOverTimeOrdersMeta(body.ordersAnalyticsMeta ?? null);
|
|
676
888
|
}
|
|
677
889
|
}).catch((e) => {
|
|
678
|
-
if (!cancelled)
|
|
890
|
+
if (!cancelled) {
|
|
891
|
+
setOverTimeError(e instanceof Error ? e.message : String(e));
|
|
892
|
+
setOverTimeOrdersMeta(null);
|
|
893
|
+
}
|
|
679
894
|
}).finally(() => {
|
|
680
895
|
if (!cancelled) setOverTimeLoading(false);
|
|
681
896
|
});
|
|
@@ -748,7 +963,7 @@ function OrdersDashboard() {
|
|
|
748
963
|
const primaryStats = data ? [
|
|
749
964
|
{
|
|
750
965
|
label: "Total revenue",
|
|
751
|
-
value: formatCurrency
|
|
966
|
+
value: formatCurrency(data.totalRevenue),
|
|
752
967
|
helper: "Non-cancelled orders",
|
|
753
968
|
accent: "green"
|
|
754
969
|
},
|
|
@@ -766,7 +981,7 @@ function OrdersDashboard() {
|
|
|
766
981
|
},
|
|
767
982
|
{
|
|
768
983
|
label: "Average order value",
|
|
769
|
-
value: formatCurrency
|
|
984
|
+
value: formatCurrency(data.aov),
|
|
770
985
|
helper: "Per non-cancelled order",
|
|
771
986
|
accent: "amber"
|
|
772
987
|
}
|
|
@@ -816,18 +1031,45 @@ function OrdersDashboard() {
|
|
|
816
1031
|
);
|
|
817
1032
|
const revenueVsPreviousPercent = previousPeriodRevenueTotal > 0 ? (trendRevenueTotal - previousPeriodRevenueTotal) / previousPeriodRevenueTotal * 100 : null;
|
|
818
1033
|
const ordersVsPreviousPercent = previousPeriodOrdersTotal > 0 ? (trendOrdersTotal - previousPeriodOrdersTotal) / previousPeriodOrdersTotal * 100 : null;
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
1034
|
+
const trendMixPieSlices = useMemo(
|
|
1035
|
+
() => buildTrendMixPieSlices(
|
|
1036
|
+
dailyOrders,
|
|
1037
|
+
trendRevenueTotal,
|
|
1038
|
+
trendOrdersTotal,
|
|
1039
|
+
formatCompactCurrency,
|
|
1040
|
+
formatCurrency
|
|
1041
|
+
),
|
|
1042
|
+
[
|
|
1043
|
+
dailyOrders,
|
|
1044
|
+
trendRevenueTotal,
|
|
1045
|
+
trendOrdersTotal,
|
|
1046
|
+
formatCompactCurrency,
|
|
1047
|
+
formatCurrency
|
|
1048
|
+
]
|
|
1049
|
+
);
|
|
1050
|
+
const trendMixPieLabelRenderer = useMemo(
|
|
1051
|
+
() => createTrendMixInfographicLabel(trendMixPieSlices),
|
|
1052
|
+
[trendMixPieSlices]
|
|
1053
|
+
);
|
|
1054
|
+
const trendMixRangeFootnote = useMemo(() => {
|
|
1055
|
+
const ncRev = dailyOrders.reduce((s, d) => s + nonCancelledRevenue(d), 0);
|
|
1056
|
+
const ncOrd = dailyOrders.reduce(
|
|
1057
|
+
(s, d) => s + Math.max(0, d.orders_count - d.cancelled_count),
|
|
1058
|
+
0
|
|
1059
|
+
);
|
|
1060
|
+
const windowAov = ncOrd > 0 ? ncRev / ncOrd : 0;
|
|
1061
|
+
return {
|
|
1062
|
+
revenue: formatCompactCurrency(trendRevenueTotal),
|
|
1063
|
+
orders: Math.floor(trendOrdersTotal).toLocaleString(),
|
|
1064
|
+
aov: formatCurrency(windowAov)
|
|
1065
|
+
};
|
|
1066
|
+
}, [
|
|
1067
|
+
dailyOrders,
|
|
1068
|
+
trendRevenueTotal,
|
|
1069
|
+
trendOrdersTotal,
|
|
1070
|
+
formatCompactCurrency,
|
|
1071
|
+
formatCurrency
|
|
1072
|
+
]);
|
|
831
1073
|
const salesBreakdownChartRows = useMemo(() => {
|
|
832
1074
|
if (salesGranularity === "hour") {
|
|
833
1075
|
return salesBreakdownHourlyRolling.map((b, i) => ({
|
|
@@ -933,11 +1175,13 @@ function OrdersDashboard() {
|
|
|
933
1175
|
};
|
|
934
1176
|
});
|
|
935
1177
|
}, [dailyOrders]);
|
|
1178
|
+
const selectedTrendPeriodLabel = ((_b = OVER_TIME_PERIODS.find((p) => p.value === overTimePeriod)) == null ? void 0 : _b.label) ?? "Selected range";
|
|
1179
|
+
const orderSampleLikelyTruncated = (((_c = data == null ? void 0 : data.ordersAnalyticsMeta) == null ? void 0 : _c.likely_truncated) ?? false) || ((overTimeOrdersMeta == null ? void 0 : overTimeOrdersMeta.likely_truncated) ?? false) || (((_d = ordersInsights == null ? void 0 : ordersInsights.ordersAnalyticsMeta) == null ? void 0 : _d.likely_truncated) ?? false);
|
|
936
1180
|
const quickPulseMetrics = [
|
|
937
1181
|
{
|
|
938
1182
|
label: "Range revenue",
|
|
939
|
-
value: formatCompactCurrency
|
|
940
|
-
helper:
|
|
1183
|
+
value: formatCompactCurrency(trendRevenueTotal),
|
|
1184
|
+
helper: selectedTrendPeriodLabel,
|
|
941
1185
|
accentClassName: "border-emerald-400/25 bg-emerald-500/5"
|
|
942
1186
|
},
|
|
943
1187
|
{
|
|
@@ -954,7 +1198,7 @@ function OrdersDashboard() {
|
|
|
954
1198
|
},
|
|
955
1199
|
{
|
|
956
1200
|
label: "Peak revenue day",
|
|
957
|
-
value: peakRevenuePoint ? formatCompactCurrency
|
|
1201
|
+
value: peakRevenuePoint ? formatCompactCurrency(peakRevenuePoint.total_revenue) : "—",
|
|
958
1202
|
helper: (peakRevenuePoint == null ? void 0 : peakRevenuePoint.label) ?? formatChartLabel$2(peakRevenuePoint == null ? void 0 : peakRevenuePoint.date),
|
|
959
1203
|
accentClassName: "border-amber-400/25 bg-amber-500/5"
|
|
960
1204
|
}
|
|
@@ -1022,7 +1266,7 @@ function OrdersDashboard() {
|
|
|
1022
1266
|
tick: CHART_AXIS_TICK_SM$1,
|
|
1023
1267
|
tickLine: false,
|
|
1024
1268
|
axisLine: false,
|
|
1025
|
-
tickFormatter: (v) => formatCompactCurrency
|
|
1269
|
+
tickFormatter: (v) => formatCompactCurrency(Number(v))
|
|
1026
1270
|
}
|
|
1027
1271
|
),
|
|
1028
1272
|
/* @__PURE__ */ jsx(
|
|
@@ -1046,6 +1290,19 @@ function OrdersDashboard() {
|
|
|
1046
1290
|
) }) }) });
|
|
1047
1291
|
})();
|
|
1048
1292
|
return /* @__PURE__ */ jsx(AnalyticsDashboardShell, { children: /* @__PURE__ */ jsxs("div", { className: "overflow-hidden rounded-2xl border border-ui-border-base/80 bg-ui-bg-base shadow-sm", children: [
|
|
1293
|
+
orderSampleLikelyTruncated ? /* @__PURE__ */ jsx(
|
|
1294
|
+
"div",
|
|
1295
|
+
{
|
|
1296
|
+
className: "border-b border-amber-400/30 bg-amber-500/10 px-4 py-2.5 sm:px-5",
|
|
1297
|
+
role: "status",
|
|
1298
|
+
children: /* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-base text-xs leading-snug", children: [
|
|
1299
|
+
"Order analytics loaded up to ",
|
|
1300
|
+
ORDERS_ANALYTICS_MAX_FETCH.toLocaleString(),
|
|
1301
|
+
" ",
|
|
1302
|
+
"orders per request. If you have more orders, KPIs and charts may under-count. Plan server-side aggregation or pagination for exact totals at very high volume."
|
|
1303
|
+
] })
|
|
1304
|
+
}
|
|
1305
|
+
) : null,
|
|
1049
1306
|
/* @__PURE__ */ jsx(
|
|
1050
1307
|
AnalyticsDashboardHeader,
|
|
1051
1308
|
{
|
|
@@ -1134,10 +1391,10 @@ function OrdersDashboard() {
|
|
|
1134
1391
|
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-1.5 sm:grid-cols-3", children: [
|
|
1135
1392
|
/* @__PURE__ */ jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
|
|
1136
1393
|
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window revenue" }),
|
|
1137
|
-
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: formatCompactCurrency
|
|
1394
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: formatCompactCurrency(trendRevenueTotal) }),
|
|
1138
1395
|
/* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-muted text-[10px]", children: [
|
|
1139
1396
|
"Avg/day ",
|
|
1140
|
-
formatCompactCurrency
|
|
1397
|
+
formatCompactCurrency(trendAverageRevenue)
|
|
1141
1398
|
] })
|
|
1142
1399
|
] }) }),
|
|
1143
1400
|
/* @__PURE__ */ jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
|
|
@@ -1160,187 +1417,63 @@ function OrdersDashboard() {
|
|
|
1160
1417
|
] }),
|
|
1161
1418
|
!overTimeLoading && !overTimeError && dailyOrders.length > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1162
1419
|
/* @__PURE__ */ jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
|
|
1163
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-
|
|
1164
|
-
/* @__PURE__ */
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-fuchsia-500" }),
|
|
1171
|
-
"Revenue"
|
|
1172
|
-
] }),
|
|
1173
|
-
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
1174
|
-
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-sky-400" }),
|
|
1175
|
-
"Orders"
|
|
1176
|
-
] }),
|
|
1177
|
-
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
1178
|
-
/* @__PURE__ */ jsx("span", { className: "h-2 w-2 shrink-0 rounded-full bg-amber-500" }),
|
|
1179
|
-
"AOV"
|
|
1180
|
-
] }),
|
|
1181
|
-
/* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
1182
|
-
/* @__PURE__ */ jsx(
|
|
1183
|
-
"span",
|
|
1184
|
-
{
|
|
1185
|
-
className: "h-0 w-4 shrink-0 border-t border-dashed border-slate-400",
|
|
1186
|
-
"aria-hidden": true
|
|
1187
|
-
}
|
|
1188
|
-
),
|
|
1189
|
-
"Previous"
|
|
1190
|
-
] })
|
|
1420
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 min-w-0", children: [
|
|
1421
|
+
/* @__PURE__ */ jsx(Text, { className: "text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Revenue, orders & AOV" }),
|
|
1422
|
+
/* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-muted text-[9px] leading-snug", children: [
|
|
1423
|
+
"Floating wedges with open center · leader labels only (no hover). Window:",
|
|
1424
|
+
" ",
|
|
1425
|
+
/* @__PURE__ */ jsx("span", { className: "text-ui-fg-subtle", children: selectedTrendPeriodLabel }),
|
|
1426
|
+
"."
|
|
1191
1427
|
] })
|
|
1192
1428
|
] }),
|
|
1193
|
-
/* @__PURE__ */
|
|
1194
|
-
|
|
1429
|
+
/* @__PURE__ */ jsxs(
|
|
1430
|
+
"div",
|
|
1195
1431
|
{
|
|
1196
|
-
|
|
1197
|
-
|
|
1432
|
+
className: "w-full rounded-2xl bg-[radial-gradient(ellipse_at_50%_42%,rgba(148,163,184,0.14)_0%,transparent_58%)] py-1",
|
|
1433
|
+
role: "img",
|
|
1434
|
+
"aria-label": `Revenue, orders, and AOV for ${selectedTrendPeriodLabel}. Values are shown on leader labels.`,
|
|
1198
1435
|
children: [
|
|
1199
|
-
/* @__PURE__ */ jsx(
|
|
1200
|
-
|
|
1201
|
-
{
|
|
1202
|
-
strokeDasharray: "3 3",
|
|
1203
|
-
vertical: false,
|
|
1204
|
-
stroke: "rgba(148,163,184,0.12)"
|
|
1205
|
-
}
|
|
1206
|
-
),
|
|
1207
|
-
/* @__PURE__ */ jsx(
|
|
1208
|
-
XAxis,
|
|
1436
|
+
/* @__PURE__ */ jsx("div", { className: "h-[min(340px,48vh)] min-h-[288px] w-full", children: /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsx(
|
|
1437
|
+
PieChart,
|
|
1209
1438
|
{
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
{
|
|
1235
|
-
yAxisId: "revenue",
|
|
1236
|
-
orientation: "right",
|
|
1237
|
-
width: 42,
|
|
1238
|
-
tick: CHART_AXIS_TICK_SM$1,
|
|
1239
|
-
tickLine: false,
|
|
1240
|
-
axisLine: false,
|
|
1241
|
-
tickFormatter: (v) => formatCompactCurrency$1(Number(v))
|
|
1242
|
-
}
|
|
1243
|
-
),
|
|
1244
|
-
/* @__PURE__ */ jsx(
|
|
1245
|
-
YAxis,
|
|
1246
|
-
{
|
|
1247
|
-
yAxisId: "aov",
|
|
1248
|
-
orientation: "right",
|
|
1249
|
-
width: 40,
|
|
1250
|
-
tick: CHART_AXIS_TICK_SM$1,
|
|
1251
|
-
tickLine: false,
|
|
1252
|
-
axisLine: false,
|
|
1253
|
-
tickFormatter: (v) => formatCompactCurrency$1(Number(v))
|
|
1254
|
-
}
|
|
1255
|
-
),
|
|
1256
|
-
/* @__PURE__ */ jsx(
|
|
1257
|
-
Tooltip,
|
|
1258
|
-
{
|
|
1259
|
-
content: /* @__PURE__ */ jsx(CombinedMetricsTooltip, {}),
|
|
1260
|
-
cursor: {
|
|
1261
|
-
stroke: "rgba(248, 250, 252, 0.35)",
|
|
1262
|
-
strokeWidth: 1
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
|
-
),
|
|
1266
|
-
/* @__PURE__ */ jsx(
|
|
1267
|
-
Line,
|
|
1268
|
-
{
|
|
1269
|
-
yAxisId: "orders",
|
|
1270
|
-
type: "natural",
|
|
1271
|
-
dataKey: "orders_count",
|
|
1272
|
-
name: "Orders",
|
|
1273
|
-
stroke: "#38BDF8",
|
|
1274
|
-
strokeWidth: 2,
|
|
1275
|
-
dot: false
|
|
1276
|
-
}
|
|
1277
|
-
),
|
|
1278
|
-
/* @__PURE__ */ jsx(
|
|
1279
|
-
Line,
|
|
1280
|
-
{
|
|
1281
|
-
yAxisId: "orders",
|
|
1282
|
-
type: "natural",
|
|
1283
|
-
dataKey: "prev_orders",
|
|
1284
|
-
name: "Orders (prev)",
|
|
1285
|
-
stroke: "#64748b",
|
|
1286
|
-
strokeWidth: 1.5,
|
|
1287
|
-
strokeDasharray: "5 4",
|
|
1288
|
-
dot: false
|
|
1289
|
-
}
|
|
1290
|
-
),
|
|
1291
|
-
/* @__PURE__ */ jsx(
|
|
1292
|
-
Line,
|
|
1293
|
-
{
|
|
1294
|
-
yAxisId: "revenue",
|
|
1295
|
-
type: "natural",
|
|
1296
|
-
dataKey: "total_revenue",
|
|
1297
|
-
name: "Revenue",
|
|
1298
|
-
stroke: "#D946EF",
|
|
1299
|
-
strokeWidth: 2,
|
|
1300
|
-
dot: false
|
|
1301
|
-
}
|
|
1302
|
-
),
|
|
1303
|
-
/* @__PURE__ */ jsx(
|
|
1304
|
-
Line,
|
|
1305
|
-
{
|
|
1306
|
-
yAxisId: "revenue",
|
|
1307
|
-
type: "natural",
|
|
1308
|
-
dataKey: "prev_revenue",
|
|
1309
|
-
name: "Revenue (prev)",
|
|
1310
|
-
stroke: "#64748b",
|
|
1311
|
-
strokeWidth: 1.5,
|
|
1312
|
-
strokeDasharray: "5 4",
|
|
1313
|
-
dot: false
|
|
1314
|
-
}
|
|
1315
|
-
),
|
|
1316
|
-
/* @__PURE__ */ jsx(
|
|
1317
|
-
Line,
|
|
1318
|
-
{
|
|
1319
|
-
yAxisId: "aov",
|
|
1320
|
-
type: "natural",
|
|
1321
|
-
dataKey: "aov",
|
|
1322
|
-
name: "AOV",
|
|
1323
|
-
stroke: "#D97706",
|
|
1324
|
-
strokeWidth: 2,
|
|
1325
|
-
dot: false
|
|
1326
|
-
}
|
|
1327
|
-
),
|
|
1328
|
-
/* @__PURE__ */ jsx(
|
|
1329
|
-
Line,
|
|
1330
|
-
{
|
|
1331
|
-
yAxisId: "aov",
|
|
1332
|
-
type: "natural",
|
|
1333
|
-
dataKey: "prev_aov",
|
|
1334
|
-
name: "AOV (prev)",
|
|
1335
|
-
stroke: "#64748b",
|
|
1336
|
-
strokeWidth: 1.5,
|
|
1337
|
-
strokeDasharray: "5 4",
|
|
1338
|
-
dot: false
|
|
1439
|
+
margin: { top: 20, right: 132, bottom: 28, left: 132 },
|
|
1440
|
+
children: /* @__PURE__ */ jsx(
|
|
1441
|
+
Pie,
|
|
1442
|
+
{
|
|
1443
|
+
data: trendMixPieSlices,
|
|
1444
|
+
dataKey: "value",
|
|
1445
|
+
nameKey: "name",
|
|
1446
|
+
cx: "50%",
|
|
1447
|
+
cy: "50%",
|
|
1448
|
+
startAngle: 90,
|
|
1449
|
+
endAngle: -270,
|
|
1450
|
+
innerRadius: "26%",
|
|
1451
|
+
outerRadius: "46%",
|
|
1452
|
+
paddingAngle: 9,
|
|
1453
|
+
cornerRadius: 8,
|
|
1454
|
+
stroke: "rgba(15,23,42,0.55)",
|
|
1455
|
+
strokeWidth: 2,
|
|
1456
|
+
shape: TrendMixExplodedSector,
|
|
1457
|
+
label: trendMixPieLabelRenderer,
|
|
1458
|
+
labelLine: false,
|
|
1459
|
+
isAnimationActive: false,
|
|
1460
|
+
children: trendMixPieSlices.map((slice) => /* @__PURE__ */ jsx(Cell, { fill: slice.fill }, slice.key))
|
|
1461
|
+
}
|
|
1462
|
+
)
|
|
1339
1463
|
}
|
|
1340
|
-
)
|
|
1464
|
+
) }) }),
|
|
1465
|
+
/* @__PURE__ */ jsxs(Text, { className: "mt-2 px-1 text-center text-[9px] leading-snug text-ui-fg-muted", children: [
|
|
1466
|
+
"Range snapshot — Revenue ",
|
|
1467
|
+
trendMixRangeFootnote.revenue,
|
|
1468
|
+
" · Orders",
|
|
1469
|
+
" ",
|
|
1470
|
+
trendMixRangeFootnote.orders,
|
|
1471
|
+
" · AOV ",
|
|
1472
|
+
trendMixRangeFootnote.aov
|
|
1473
|
+
] })
|
|
1341
1474
|
]
|
|
1342
1475
|
}
|
|
1343
|
-
)
|
|
1476
|
+
)
|
|
1344
1477
|
] }),
|
|
1345
1478
|
/* @__PURE__ */ jsxs(AnalyticsChartSurface, { variant: "atlas", className: "mt-1.5", children: [
|
|
1346
1479
|
/* @__PURE__ */ jsx(Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Revenue breakdown (stacked — by order status)" }),
|
|
@@ -1365,9 +1498,11 @@ function OrdersDashboard() {
|
|
|
1365
1498
|
tick: CHART_AXIS_TICK$2,
|
|
1366
1499
|
tickLine: false,
|
|
1367
1500
|
axisLine: false,
|
|
1368
|
-
tickFormatter: (value
|
|
1369
|
-
const row = dailyOrders
|
|
1370
|
-
|
|
1501
|
+
tickFormatter: (value) => {
|
|
1502
|
+
const row = dailyOrders.find(
|
|
1503
|
+
(d) => d.date === String(value)
|
|
1504
|
+
);
|
|
1505
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel$2(String(value));
|
|
1371
1506
|
}
|
|
1372
1507
|
}
|
|
1373
1508
|
),
|
|
@@ -1375,7 +1510,7 @@ function OrdersDashboard() {
|
|
|
1375
1510
|
YAxis,
|
|
1376
1511
|
{
|
|
1377
1512
|
tick: CHART_AXIS_TICK$2,
|
|
1378
|
-
tickFormatter: (v) => formatCompactCurrency
|
|
1513
|
+
tickFormatter: (v) => formatCompactCurrency(Number(v))
|
|
1379
1514
|
}
|
|
1380
1515
|
),
|
|
1381
1516
|
/* @__PURE__ */ jsx(
|
|
@@ -1385,7 +1520,7 @@ function OrdersDashboard() {
|
|
|
1385
1520
|
p.name,
|
|
1386
1521
|
":",
|
|
1387
1522
|
" ",
|
|
1388
|
-
/* @__PURE__ */ jsx("strong", { children: formatCurrency
|
|
1523
|
+
/* @__PURE__ */ jsx("strong", { children: formatCurrency(Number(p.value) || 0) })
|
|
1389
1524
|
] }, String(p.name))) }) : null
|
|
1390
1525
|
}
|
|
1391
1526
|
),
|
|
@@ -1460,7 +1595,7 @@ function OrdersDashboard() {
|
|
|
1460
1595
|
{
|
|
1461
1596
|
variant: "atlas",
|
|
1462
1597
|
title: "Order → fulfillment funnel",
|
|
1463
|
-
description: `Stages by order created date (UTC), same range as Revenue & orders (${((
|
|
1598
|
+
description: `Stages by order created date (UTC), same range as Revenue & orders (${((_e = OVER_TIME_PERIODS.find((p) => p.value === overTimePeriod)) == null ? void 0 : _e.label) ?? "period"}). Not storefront visitors.`,
|
|
1464
1599
|
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: [
|
|
1465
1600
|
/* @__PURE__ */ jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
|
|
1466
1601
|
/* @__PURE__ */ jsx(Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Stage counts (time series)" }),
|
|
@@ -2130,7 +2265,7 @@ function formatChartLabel$1(value) {
|
|
|
2130
2265
|
return `${parsed.getUTCMonth() + 1}/${parsed.getUTCDate()}`;
|
|
2131
2266
|
}
|
|
2132
2267
|
function CustomersDashboard() {
|
|
2133
|
-
var _a, _b;
|
|
2268
|
+
var _a, _b, _c;
|
|
2134
2269
|
const [data, setData] = useState(null);
|
|
2135
2270
|
const [loading, setLoading] = useState(true);
|
|
2136
2271
|
const [error, setError] = useState(null);
|
|
@@ -2369,6 +2504,13 @@ function CustomersDashboard() {
|
|
|
2369
2504
|
title: "Repeat purchases",
|
|
2370
2505
|
description: "Orders with a customer_id in the same window (guest checkouts excluded from rate).",
|
|
2371
2506
|
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: [
|
|
2507
|
+
((_b = repeatStats.ordersAnalyticsMeta) == null ? void 0 : _b.likely_truncated) ? /* @__PURE__ */ jsx("div", { className: "col-span-full rounded-md border border-amber-400/25 bg-amber-500/10 px-2 py-1.5", children: /* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-base text-[10px] leading-snug", children: [
|
|
2508
|
+
"Repeat stats are computed from a sample of up to",
|
|
2509
|
+
" ",
|
|
2510
|
+
repeatStats.ordersAnalyticsMeta.max_fetch.toLocaleString(),
|
|
2511
|
+
" ",
|
|
2512
|
+
"orders; your true repeat rate may differ if you exceed that sample."
|
|
2513
|
+
] }) }) : null,
|
|
2372
2514
|
/* @__PURE__ */ jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
|
|
2373
2515
|
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Repeat rate" }),
|
|
2374
2516
|
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: `${repeatStats.repeat_rate_percent.toFixed(1)}%` }),
|
|
@@ -2433,7 +2575,7 @@ function CustomersDashboard() {
|
|
|
2433
2575
|
] }) }),
|
|
2434
2576
|
/* @__PURE__ */ jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
|
|
2435
2577
|
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Chart period" }),
|
|
2436
|
-
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: ((
|
|
2578
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: ((_c = GRAPH_PERIODS$1.find((p) => p.value === graphPeriod)) == null ? void 0 : _c.label) ?? graphPeriod }),
|
|
2437
2579
|
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-[10px] leading-snug", children: "Independent of summary period filter" })
|
|
2438
2580
|
] }) })
|
|
2439
2581
|
] }),
|
|
@@ -2467,12 +2609,12 @@ function CustomersDashboard() {
|
|
|
2467
2609
|
stroke: CHART_AXIS_LINE$1,
|
|
2468
2610
|
tickLine: false,
|
|
2469
2611
|
axisLine: { stroke: CHART_AXIS_LINE$1 },
|
|
2470
|
-
tickFormatter: (
|
|
2471
|
-
|
|
2472
|
-
const row = series[index];
|
|
2612
|
+
tickFormatter: (value) => {
|
|
2613
|
+
const row = series.find((p) => p.date === String(value));
|
|
2473
2614
|
if (row == null ? void 0 : row.label) return row.label;
|
|
2474
|
-
|
|
2475
|
-
|
|
2615
|
+
return formatChartLabel$1(
|
|
2616
|
+
(row == null ? void 0 : row.date) ?? (typeof value === "string" ? value : String(value))
|
|
2617
|
+
);
|
|
2476
2618
|
}
|
|
2477
2619
|
}
|
|
2478
2620
|
),
|
|
@@ -2527,6 +2669,16 @@ function CustomersDashboard() {
|
|
|
2527
2669
|
] })
|
|
2528
2670
|
] }) });
|
|
2529
2671
|
}
|
|
2672
|
+
function buildAnalyticsUrl(path, params) {
|
|
2673
|
+
const search = new URLSearchParams();
|
|
2674
|
+
for (const [key, value] of Object.entries(params)) {
|
|
2675
|
+
if (value !== void 0 && value !== null) {
|
|
2676
|
+
search.set(key, String(value));
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
const query = search.toString();
|
|
2680
|
+
return query ? `${path}?${query}` : path;
|
|
2681
|
+
}
|
|
2530
2682
|
const SUMMARY_PERIODS = [
|
|
2531
2683
|
{ value: "all", label: "All time" },
|
|
2532
2684
|
{ value: "0", label: "Today" },
|
|
@@ -2571,22 +2723,6 @@ const SELECT_CLASS_NAME = "h-9 w-full min-w-[8rem] max-w-full cursor-pointer app
|
|
|
2571
2723
|
const LEADERBOARD_TABLE_CLASS = "min-w-[32rem] sm:min-w-[36rem] max-w-none";
|
|
2572
2724
|
const OPPORTUNITIES_TABLE_CLASS = "min-w-[30rem] sm:min-w-[34rem] max-w-none";
|
|
2573
2725
|
const TABLE_HEAD_CELL_NOWRAP = "whitespace-nowrap";
|
|
2574
|
-
function formatCurrency(value) {
|
|
2575
|
-
return new Intl.NumberFormat("en-IN", {
|
|
2576
|
-
style: "currency",
|
|
2577
|
-
currency: "INR",
|
|
2578
|
-
minimumFractionDigits: 0,
|
|
2579
|
-
maximumFractionDigits: 0
|
|
2580
|
-
}).format(value);
|
|
2581
|
-
}
|
|
2582
|
-
function formatCompactCurrency(value) {
|
|
2583
|
-
return new Intl.NumberFormat("en-IN", {
|
|
2584
|
-
style: "currency",
|
|
2585
|
-
currency: "INR",
|
|
2586
|
-
notation: "compact",
|
|
2587
|
-
maximumFractionDigits: 1
|
|
2588
|
-
}).format(value);
|
|
2589
|
-
}
|
|
2590
2726
|
function formatChartLabel(value) {
|
|
2591
2727
|
if (!value) return "";
|
|
2592
2728
|
const parsed = new Date(value);
|
|
@@ -2613,16 +2749,6 @@ function summaryDaysToGraphPeriod(days) {
|
|
|
2613
2749
|
if (days === "all" || days === "90") return "one_year";
|
|
2614
2750
|
return "one_week";
|
|
2615
2751
|
}
|
|
2616
|
-
function buildAnalyticsUrl(path, params) {
|
|
2617
|
-
const search = new URLSearchParams();
|
|
2618
|
-
for (const [key, value] of Object.entries(params)) {
|
|
2619
|
-
if (value) {
|
|
2620
|
-
search.set(key, value);
|
|
2621
|
-
}
|
|
2622
|
-
}
|
|
2623
|
-
const query = search.toString();
|
|
2624
|
-
return query ? `${path}?${query}` : path;
|
|
2625
|
-
}
|
|
2626
2752
|
function isProductOverTimePoint(value) {
|
|
2627
2753
|
if (typeof value !== "object" || value === null) return false;
|
|
2628
2754
|
const v = value;
|
|
@@ -2634,6 +2760,7 @@ function TrendTooltip({
|
|
|
2634
2760
|
label
|
|
2635
2761
|
}) {
|
|
2636
2762
|
var _a, _b, _c, _d, _e;
|
|
2763
|
+
const { formatCurrency } = useAnalyticsCurrency();
|
|
2637
2764
|
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
2638
2765
|
const row = isProductOverTimePoint((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
2639
2766
|
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
@@ -2665,6 +2792,7 @@ function MiniTrendTooltip({
|
|
|
2665
2792
|
hideRevenueRow
|
|
2666
2793
|
}) {
|
|
2667
2794
|
var _a, _b, _c, _d, _e;
|
|
2795
|
+
const { formatCurrency } = useAnalyticsCurrency();
|
|
2668
2796
|
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
2669
2797
|
const row = isProductOverTimePoint((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
2670
2798
|
const labelKey = label !== void 0 && label !== null ? String(label) : "";
|
|
@@ -2695,6 +2823,7 @@ function ProductBarTooltip({
|
|
|
2695
2823
|
showViews = true
|
|
2696
2824
|
}) {
|
|
2697
2825
|
var _a;
|
|
2826
|
+
const { formatCurrency } = useAnalyticsCurrency();
|
|
2698
2827
|
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
2699
2828
|
const row = (_a = payload[0]) == null ? void 0 : _a.payload;
|
|
2700
2829
|
if (!row) return null;
|
|
@@ -2747,6 +2876,9 @@ function EmptyAnalyticsPanel({ title, description }) {
|
|
|
2747
2876
|
}
|
|
2748
2877
|
function ProductsDashboard() {
|
|
2749
2878
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
2879
|
+
const { formatCurrency, formatCompactCurrency } = useAnalyticsCurrency();
|
|
2880
|
+
const chartGradientPrefix = useId().replace(/:/g, "");
|
|
2881
|
+
const gid = (suffix) => `${chartGradientPrefix}-${suffix}`;
|
|
2750
2882
|
const [summaryDays, setSummaryDays] = useState("all");
|
|
2751
2883
|
const [graphPeriod, setGraphPeriod] = useState("one_week");
|
|
2752
2884
|
const [topSellerPeriod, setTopSellerPeriod] = useState("week");
|
|
@@ -3333,26 +3465,76 @@ function ProductsDashboard() {
|
|
|
3333
3465
|
margin: { top: 4, right: 6, left: -6, bottom: 0 },
|
|
3334
3466
|
children: [
|
|
3335
3467
|
/* @__PURE__ */ jsxs("defs", { children: [
|
|
3336
|
-
/* @__PURE__ */ jsxs(
|
|
3337
|
-
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3468
|
+
/* @__PURE__ */ jsxs(
|
|
3469
|
+
"linearGradient",
|
|
3470
|
+
{
|
|
3471
|
+
id: gid("trend-units-line"),
|
|
3472
|
+
x1: "0",
|
|
3473
|
+
y1: "0",
|
|
3474
|
+
x2: "1",
|
|
3475
|
+
y2: "0",
|
|
3476
|
+
children: [
|
|
3477
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#67E8F9" }),
|
|
3478
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#3B82F6" })
|
|
3479
|
+
]
|
|
3480
|
+
}
|
|
3481
|
+
),
|
|
3482
|
+
/* @__PURE__ */ jsxs(
|
|
3483
|
+
"linearGradient",
|
|
3484
|
+
{
|
|
3485
|
+
id: gid("trend-revenue-line"),
|
|
3486
|
+
x1: "0",
|
|
3487
|
+
y1: "0",
|
|
3488
|
+
x2: "1",
|
|
3489
|
+
y2: "0",
|
|
3490
|
+
children: [
|
|
3491
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#F472B6" }),
|
|
3492
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#C084FC" })
|
|
3493
|
+
]
|
|
3494
|
+
}
|
|
3495
|
+
),
|
|
3496
|
+
/* @__PURE__ */ jsxs(
|
|
3497
|
+
"linearGradient",
|
|
3498
|
+
{
|
|
3499
|
+
id: gid("trend-units-area"),
|
|
3500
|
+
x1: "0",
|
|
3501
|
+
y1: "0",
|
|
3502
|
+
x2: "0",
|
|
3503
|
+
y2: "1",
|
|
3504
|
+
children: [
|
|
3505
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#38BDF8", stopOpacity: 0.22 }),
|
|
3506
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#38BDF8", stopOpacity: 0 })
|
|
3507
|
+
]
|
|
3508
|
+
}
|
|
3509
|
+
),
|
|
3510
|
+
/* @__PURE__ */ jsxs(
|
|
3511
|
+
"linearGradient",
|
|
3512
|
+
{
|
|
3513
|
+
id: gid("trend-revenue-area"),
|
|
3514
|
+
x1: "0",
|
|
3515
|
+
y1: "0",
|
|
3516
|
+
x2: "0",
|
|
3517
|
+
y2: "1",
|
|
3518
|
+
children: [
|
|
3519
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#E879F9", stopOpacity: 0.2 }),
|
|
3520
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#E879F9", stopOpacity: 0 })
|
|
3521
|
+
]
|
|
3522
|
+
}
|
|
3523
|
+
),
|
|
3524
|
+
/* @__PURE__ */ jsxs(
|
|
3525
|
+
"linearGradient",
|
|
3526
|
+
{
|
|
3527
|
+
id: gid("trend-views-area"),
|
|
3528
|
+
x1: "0",
|
|
3529
|
+
y1: "0",
|
|
3530
|
+
x2: "0",
|
|
3531
|
+
y2: "1",
|
|
3532
|
+
children: [
|
|
3533
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#C084FC", stopOpacity: 0.18 }),
|
|
3534
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#C084FC", stopOpacity: 0 })
|
|
3535
|
+
]
|
|
3536
|
+
}
|
|
3537
|
+
)
|
|
3356
3538
|
] }),
|
|
3357
3539
|
/* @__PURE__ */ jsx(
|
|
3358
3540
|
CartesianGrid,
|
|
@@ -3369,8 +3551,8 @@ function ProductsDashboard() {
|
|
|
3369
3551
|
tick: CHART_AXIS_TICK,
|
|
3370
3552
|
tickLine: false,
|
|
3371
3553
|
axisLine: false,
|
|
3372
|
-
tickFormatter: (value
|
|
3373
|
-
const row = series
|
|
3554
|
+
tickFormatter: (value) => {
|
|
3555
|
+
const row = series.find((p) => p.date === String(value));
|
|
3374
3556
|
return (row == null ? void 0 : row.label) ?? formatChartLabel(String(value));
|
|
3375
3557
|
}
|
|
3376
3558
|
}
|
|
@@ -3416,7 +3598,7 @@ function ProductsDashboard() {
|
|
|
3416
3598
|
type: "natural",
|
|
3417
3599
|
dataKey: "units_sold",
|
|
3418
3600
|
stroke: "none",
|
|
3419
|
-
fill:
|
|
3601
|
+
fill: `url(#${gid("trend-units-area")})`,
|
|
3420
3602
|
isAnimationActive: false,
|
|
3421
3603
|
legendType: "none"
|
|
3422
3604
|
}
|
|
@@ -3428,7 +3610,7 @@ function ProductsDashboard() {
|
|
|
3428
3610
|
type: "natural",
|
|
3429
3611
|
dataKey: "views",
|
|
3430
3612
|
stroke: "none",
|
|
3431
|
-
fill:
|
|
3613
|
+
fill: `url(#${gid("trend-views-area")})`,
|
|
3432
3614
|
isAnimationActive: false,
|
|
3433
3615
|
legendType: "none"
|
|
3434
3616
|
}
|
|
@@ -3440,7 +3622,7 @@ function ProductsDashboard() {
|
|
|
3440
3622
|
type: "natural",
|
|
3441
3623
|
dataKey: "revenue",
|
|
3442
3624
|
stroke: "none",
|
|
3443
|
-
fill:
|
|
3625
|
+
fill: `url(#${gid("trend-revenue-area")})`,
|
|
3444
3626
|
isAnimationActive: false,
|
|
3445
3627
|
legendType: "none"
|
|
3446
3628
|
}
|
|
@@ -3452,7 +3634,7 @@ function ProductsDashboard() {
|
|
|
3452
3634
|
type: "natural",
|
|
3453
3635
|
dataKey: "units_sold",
|
|
3454
3636
|
name: "Units sold",
|
|
3455
|
-
stroke:
|
|
3637
|
+
stroke: `url(#${gid("trend-units-line")})`,
|
|
3456
3638
|
strokeWidth: 2,
|
|
3457
3639
|
dot: false,
|
|
3458
3640
|
activeDot: { r: 4, fill: PRODUCT_CHART_COLORS.units }
|
|
@@ -3479,7 +3661,7 @@ function ProductsDashboard() {
|
|
|
3479
3661
|
type: "natural",
|
|
3480
3662
|
dataKey: "revenue",
|
|
3481
3663
|
name: "Revenue",
|
|
3482
|
-
stroke:
|
|
3664
|
+
stroke: `url(#${gid("trend-revenue-line")})`,
|
|
3483
3665
|
strokeWidth: 2,
|
|
3484
3666
|
dot: false,
|
|
3485
3667
|
activeDot: { r: 4, fill: PRODUCT_CHART_COLORS.revenue }
|
|
@@ -3550,7 +3732,7 @@ function ProductsDashboard() {
|
|
|
3550
3732
|
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
|
|
3551
3733
|
"linearGradient",
|
|
3552
3734
|
{
|
|
3553
|
-
id: "
|
|
3735
|
+
id: gid("best-revenue-area"),
|
|
3554
3736
|
x1: "0",
|
|
3555
3737
|
y1: "0",
|
|
3556
3738
|
x2: "0",
|
|
@@ -3588,7 +3770,12 @@ function ProductsDashboard() {
|
|
|
3588
3770
|
{
|
|
3589
3771
|
dataKey: "date",
|
|
3590
3772
|
tick: CHART_AXIS_TICK_SM,
|
|
3591
|
-
tickFormatter: (value) =>
|
|
3773
|
+
tickFormatter: (value) => {
|
|
3774
|
+
const row = bestSellersTrendSeries.find(
|
|
3775
|
+
(p) => p.date === String(value)
|
|
3776
|
+
);
|
|
3777
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel(String(value));
|
|
3778
|
+
},
|
|
3592
3779
|
tickLine: false,
|
|
3593
3780
|
axisLine: { stroke: CHART_AXIS_LINE }
|
|
3594
3781
|
}
|
|
@@ -3638,7 +3825,7 @@ function ProductsDashboard() {
|
|
|
3638
3825
|
dataKey: "revenue",
|
|
3639
3826
|
name: "Revenue",
|
|
3640
3827
|
stroke: "transparent",
|
|
3641
|
-
fill:
|
|
3828
|
+
fill: `url(#${gid("best-revenue-area")})`,
|
|
3642
3829
|
isAnimationActive: false
|
|
3643
3830
|
}
|
|
3644
3831
|
),
|
|
@@ -3753,7 +3940,7 @@ function ProductsDashboard() {
|
|
|
3753
3940
|
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs(
|
|
3754
3941
|
"linearGradient",
|
|
3755
3942
|
{
|
|
3756
|
-
id: "
|
|
3943
|
+
id: gid("viewed-views-area"),
|
|
3757
3944
|
x1: "0",
|
|
3758
3945
|
y1: "0",
|
|
3759
3946
|
x2: "0",
|
|
@@ -3791,7 +3978,12 @@ function ProductsDashboard() {
|
|
|
3791
3978
|
{
|
|
3792
3979
|
dataKey: "date",
|
|
3793
3980
|
tick: CHART_AXIS_TICK_SM,
|
|
3794
|
-
tickFormatter: (value) =>
|
|
3981
|
+
tickFormatter: (value) => {
|
|
3982
|
+
const row = mostViewedTrendSeries.find(
|
|
3983
|
+
(p) => p.date === String(value)
|
|
3984
|
+
);
|
|
3985
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel(String(value));
|
|
3986
|
+
},
|
|
3795
3987
|
tickLine: false,
|
|
3796
3988
|
axisLine: { stroke: CHART_AXIS_LINE }
|
|
3797
3989
|
}
|
|
@@ -3828,7 +4020,7 @@ function ProductsDashboard() {
|
|
|
3828
4020
|
dataKey: "views",
|
|
3829
4021
|
name: "Views",
|
|
3830
4022
|
stroke: "transparent",
|
|
3831
|
-
fill:
|
|
4023
|
+
fill: `url(#${gid("viewed-views-area")})`,
|
|
3832
4024
|
isAnimationActive: false
|
|
3833
4025
|
}
|
|
3834
4026
|
),
|
|
@@ -3890,7 +4082,7 @@ function ProductsDashboard() {
|
|
|
3890
4082
|
title: "Views vs units sold",
|
|
3891
4083
|
description: "Each point is a product (top viewed in this period). Requires product view tracking.",
|
|
3892
4084
|
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: [
|
|
3893
|
-
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("radialGradient", { id: "
|
|
4085
|
+
/* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsxs("radialGradient", { id: gid("scatter-glow"), cx: "50%", cy: "50%", r: "50%", children: [
|
|
3894
4086
|
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#E879F9", stopOpacity: 0.95 }),
|
|
3895
4087
|
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#6366F1", stopOpacity: 0.65 })
|
|
3896
4088
|
] }) }),
|
|
@@ -3951,7 +4143,7 @@ function ProductsDashboard() {
|
|
|
3951
4143
|
{
|
|
3952
4144
|
name: "Products",
|
|
3953
4145
|
data: viewsVsUnitsScatterData,
|
|
3954
|
-
fill:
|
|
4146
|
+
fill: `url(#${gid("scatter-glow")})`
|
|
3955
4147
|
}
|
|
3956
4148
|
)
|
|
3957
4149
|
] }) }) }) })
|
|
@@ -3967,7 +4159,7 @@ const ANALYTICS_MODULES = [
|
|
|
3967
4159
|
];
|
|
3968
4160
|
const AnalyticsPage = () => {
|
|
3969
4161
|
const [activeModule, setActiveModule] = useState("orders");
|
|
3970
|
-
return /* @__PURE__ */ jsx("div", { className: "flex h-full bg-ui-bg-subtle", children: /* @__PURE__ */ jsx("main", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsxs(
|
|
4162
|
+
return /* @__PURE__ */ jsx(AnalyticsCurrencyProvider, { children: /* @__PURE__ */ jsx("div", { className: "flex h-full bg-ui-bg-subtle", children: /* @__PURE__ */ jsx("main", { className: "flex-1 overflow-auto", children: /* @__PURE__ */ jsxs(
|
|
3971
4163
|
"div",
|
|
3972
4164
|
{
|
|
3973
4165
|
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(),
|
|
@@ -4008,7 +4200,7 @@ const AnalyticsPage = () => {
|
|
|
4008
4200
|
] })
|
|
4009
4201
|
]
|
|
4010
4202
|
}
|
|
4011
|
-
) }) });
|
|
4203
|
+
) }) }) });
|
|
4012
4204
|
};
|
|
4013
4205
|
const config = defineRouteConfig({
|
|
4014
4206
|
label: "Analytics",
|