medusa-analytics 0.0.16 → 0.0.17
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 +1063 -273
- package/.medusa/server/src/admin/index.mjs +1064 -274
- package/.medusa/server/src/api/admin/analytics/fetch-orders-for-analytics.js +86 -0
- package/.medusa/server/src/api/admin/analytics/orders-over-time/route.js +167 -92
- package/.medusa/server/src/api/admin/analytics/orders-summary/route.js +9 -38
- package/package.json +1 -1
|
@@ -5,61 +5,176 @@ const adminSdk = require("@medusajs/admin-sdk");
|
|
|
5
5
|
const icons = require("@medusajs/icons");
|
|
6
6
|
const ui = require("@medusajs/ui");
|
|
7
7
|
const recharts = require("recharts");
|
|
8
|
+
const ACCENT_STYLES = {
|
|
9
|
+
blue: {
|
|
10
|
+
border: "border-sky-400/30",
|
|
11
|
+
glow: "shadow-[0_0_0_1px_rgba(56,189,248,0.12),0_18px_40px_-28px_rgba(59,130,246,0.55)]",
|
|
12
|
+
band: "from-sky-400/70 via-blue-400/30 to-transparent"
|
|
13
|
+
},
|
|
14
|
+
purple: {
|
|
15
|
+
border: "border-fuchsia-400/25",
|
|
16
|
+
glow: "shadow-[0_0_0_1px_rgba(217,70,239,0.10),0_18px_40px_-28px_rgba(168,85,247,0.5)]",
|
|
17
|
+
band: "from-fuchsia-400/65 via-violet-400/25 to-transparent"
|
|
18
|
+
},
|
|
19
|
+
green: {
|
|
20
|
+
border: "border-emerald-400/25",
|
|
21
|
+
glow: "shadow-[0_0_0_1px_rgba(52,211,153,0.10),0_18px_40px_-28px_rgba(16,185,129,0.45)]",
|
|
22
|
+
band: "from-emerald-400/65 via-teal-400/25 to-transparent"
|
|
23
|
+
},
|
|
24
|
+
amber: {
|
|
25
|
+
border: "border-amber-400/25",
|
|
26
|
+
glow: "shadow-[0_0_0_1px_rgba(251,191,36,0.10),0_18px_40px_-28px_rgba(245,158,11,0.45)]",
|
|
27
|
+
band: "from-amber-400/65 via-orange-400/25 to-transparent"
|
|
28
|
+
},
|
|
29
|
+
rose: {
|
|
30
|
+
border: "border-rose-400/25",
|
|
31
|
+
glow: "shadow-[0_0_0_1px_rgba(251,113,133,0.10),0_18px_40px_-28px_rgba(244,63,94,0.45)]",
|
|
32
|
+
band: "from-rose-400/65 via-pink-400/25 to-transparent"
|
|
33
|
+
},
|
|
34
|
+
slate: {
|
|
35
|
+
border: "border-ui-border-strong",
|
|
36
|
+
glow: "shadow-[0_0_0_1px_rgba(15,23,42,0.05),0_18px_40px_-28px_rgba(15,23,42,0.22)]",
|
|
37
|
+
band: "from-ui-border-strong via-ui-border-base to-transparent"
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
function getAccentStyle(accent) {
|
|
41
|
+
return ACCENT_STYLES[accent];
|
|
42
|
+
}
|
|
8
43
|
function AnalyticsDashboardShell({ children }) {
|
|
9
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-
|
|
44
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2.5 md:space-y-3", children });
|
|
10
45
|
}
|
|
11
46
|
function AnalyticsDashboardHeader({
|
|
12
47
|
title,
|
|
13
48
|
description,
|
|
14
|
-
actions
|
|
49
|
+
actions,
|
|
50
|
+
variant = "default"
|
|
15
51
|
}) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
52
|
+
const isPremium = variant === "premium";
|
|
53
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
54
|
+
ui.Container,
|
|
55
|
+
{
|
|
56
|
+
className: `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(),
|
|
57
|
+
children: [
|
|
58
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
59
|
+
"div",
|
|
60
|
+
{
|
|
61
|
+
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(),
|
|
62
|
+
children: [
|
|
63
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
64
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: isPremium ? "text-lg font-semibold tracking-tight" : "tracking-tight", children: title }),
|
|
65
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: `text-ui-fg-muted ${isPremium ? "max-w-2xl text-xs leading-snug" : "text-sm"}`.trim(), children: description }) : null
|
|
66
|
+
] }),
|
|
67
|
+
actions ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
68
|
+
"div",
|
|
69
|
+
{
|
|
70
|
+
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(),
|
|
71
|
+
children: actions
|
|
72
|
+
}
|
|
73
|
+
) : null
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
),
|
|
77
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
78
|
+
"div",
|
|
79
|
+
{
|
|
80
|
+
className: `h-px w-full ${isPremium ? "bg-gradient-to-r from-transparent via-ui-border-strong to-transparent" : "bg-ui-bg-subtle"}`.trim()
|
|
81
|
+
}
|
|
82
|
+
)
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
);
|
|
33
86
|
}
|
|
34
|
-
function
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
87
|
+
function AnalyticsStatCard({
|
|
88
|
+
label,
|
|
89
|
+
value,
|
|
90
|
+
helper,
|
|
91
|
+
className,
|
|
92
|
+
valueClassName,
|
|
93
|
+
variant = "default",
|
|
94
|
+
accent = "slate"
|
|
40
95
|
}) {
|
|
96
|
+
const accentStyle = getAccentStyle(accent);
|
|
97
|
+
const isHero = variant === "hero";
|
|
98
|
+
const isCompact = variant === "compact";
|
|
41
99
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
42
100
|
ui.Container,
|
|
43
101
|
{
|
|
44
|
-
className: `overflow-hidden rounded-2xl border border-ui-border-base bg-ui-bg-base p-
|
|
102
|
+
className: `relative h-full overflow-hidden rounded-2xl border bg-ui-bg-base transition-shadow ${isHero ? `p-3 ${accentStyle.border} ${accentStyle.glow} shadow-sm` : isCompact ? "border-ui-border-base bg-ui-bg-subtle/70 p-2.5 shadow-none" : "border-ui-border-base p-4 shadow-sm"} ${className ?? ""}`.trim(),
|
|
45
103
|
children: [
|
|
46
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
47
|
-
/* @__PURE__ */ jsxRuntime.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
104
|
+
isHero ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
105
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
106
|
+
"div",
|
|
107
|
+
{
|
|
108
|
+
className: `pointer-events-none absolute inset-x-3 top-0 h-10 rounded-b-full bg-gradient-to-r ${accentStyle.band} blur-xl`
|
|
109
|
+
}
|
|
110
|
+
),
|
|
111
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-x-3 top-0 h-px bg-gradient-to-r from-transparent via-white/40 to-transparent" })
|
|
112
|
+
] }) : null,
|
|
113
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
114
|
+
"div",
|
|
115
|
+
{
|
|
116
|
+
className: `relative flex flex-col justify-between gap-1 ${isCompact ? "min-h-[64px]" : isHero ? "min-h-[72px]" : "min-h-[92px]"}`.trim(),
|
|
117
|
+
children: [
|
|
118
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
119
|
+
ui.Text,
|
|
120
|
+
{
|
|
121
|
+
className: `text-ui-fg-muted font-medium ${isHero ? "text-[10px] uppercase tracking-[0.16em]" : "text-xs"}`.trim(),
|
|
122
|
+
children: label
|
|
123
|
+
}
|
|
124
|
+
),
|
|
125
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
126
|
+
ui.Heading,
|
|
127
|
+
{
|
|
128
|
+
level: "h2",
|
|
129
|
+
className: `leading-tight tracking-tight ${isHero ? "text-xl sm:text-2xl" : isCompact ? "text-lg" : "text-2xl"} ${valueClassName ?? ""}`.trim(),
|
|
130
|
+
children: value
|
|
131
|
+
}
|
|
132
|
+
),
|
|
133
|
+
helper ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: `text-ui-fg-muted leading-snug ${isHero ? "text-[10px]" : "text-xs"}`.trim(), children: helper }) : null
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
)
|
|
54
137
|
]
|
|
55
138
|
}
|
|
56
139
|
);
|
|
57
140
|
}
|
|
58
|
-
function
|
|
141
|
+
function AnalyticsSection({
|
|
142
|
+
title,
|
|
143
|
+
description,
|
|
144
|
+
actions,
|
|
145
|
+
children,
|
|
146
|
+
className,
|
|
147
|
+
variant = "default"
|
|
148
|
+
}) {
|
|
149
|
+
const isAtlas = variant === "atlas";
|
|
150
|
+
const isPremium = variant !== "default" && !isAtlas;
|
|
151
|
+
const shellClass = isAtlas ? "overflow-hidden 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();
|
|
152
|
+
const headerClass = isAtlas ? "flex flex-col gap-1.5 border-b border-ui-border-base bg-ui-bg-subtle/40 px-4 py-2.5 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();
|
|
153
|
+
const titleClass = isAtlas ? "text-sm font-semibold tracking-tight text-ui-fg-base" : isPremium ? "text-base font-semibold tracking-tight" : "tracking-tight";
|
|
154
|
+
const descClass = isAtlas ? "max-w-2xl text-[11px] leading-snug text-ui-fg-muted" : isPremium ? "max-w-2xl text-xs leading-snug" : "text-sm";
|
|
155
|
+
const actionsShellClass = isAtlas ? "flex shrink-0 flex-wrap items-center gap-1.5 rounded-md border border-ui-border-base bg-ui-bg-base px-2 py-1" : `flex shrink-0 flex-wrap items-center gap-1.5 rounded-xl border px-2 py-1.5 ${isPremium ? "border-ui-border-strong bg-ui-bg-subtle shadow-[inset_0_1px_0_rgba(255,255,255,0.32)]" : "border-ui-border-base bg-ui-bg-subtle"}`.trim();
|
|
156
|
+
const bodyClass = isAtlas ? "p-3" : variant === "hero" ? "px-4 py-3" : "px-4 py-3";
|
|
157
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: `${shellClass} ${className ?? ""}`.trim(), children: [
|
|
158
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: headerClass, children: [
|
|
159
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 space-y-0.5", children: [
|
|
160
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: titleClass, children: title }),
|
|
161
|
+
description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: `text-ui-fg-muted ${descClass}`.trim(), children: description }) : null
|
|
162
|
+
] }),
|
|
163
|
+
actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: actionsShellClass, children: actions }) : null
|
|
164
|
+
] }),
|
|
165
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: bodyClass, children })
|
|
166
|
+
] });
|
|
167
|
+
}
|
|
168
|
+
function AnalyticsChartSurface({
|
|
169
|
+
children,
|
|
170
|
+
className,
|
|
171
|
+
variant = "default"
|
|
172
|
+
}) {
|
|
173
|
+
const surfaceClassName = variant === "atlas" ? "rounded-lg border border-ui-border-base bg-ui-bg-subtle/50 p-2.5 shadow-sm" : variant === "glass" ? "rounded-xl border border-ui-border-strong bg-[linear-gradient(180deg,rgba(255,255,255,0.12),rgba(255,255,255,0.03))] p-2.5 shadow-[inset_0_1px_0_rgba(255,255,255,0.22)]" : variant === "panel" ? "rounded-xl border border-ui-border-base/90 bg-ui-bg-base p-2.5 shadow-[inset_0_1px_0_rgba(255,255,255,0.18)]" : "rounded-lg border border-ui-border-base bg-ui-bg-subtle p-2 shadow-inner";
|
|
59
174
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
60
175
|
"div",
|
|
61
176
|
{
|
|
62
|
-
className:
|
|
177
|
+
className: `${surfaceClassName} ${className ?? ""}`.trim(),
|
|
63
178
|
children
|
|
64
179
|
}
|
|
65
180
|
);
|
|
@@ -73,56 +188,174 @@ function AnalyticsTableSurface({ children, className }) {
|
|
|
73
188
|
}
|
|
74
189
|
);
|
|
75
190
|
}
|
|
76
|
-
function
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
191
|
+
function isDailyOrderRow(value) {
|
|
192
|
+
if (typeof value !== "object" || value === null) return false;
|
|
193
|
+
return "date" in value && "orders_count" in value && "total_revenue" in value && typeof value.date === "string" && typeof value.orders_count === "number" && typeof value.total_revenue === "number";
|
|
194
|
+
}
|
|
195
|
+
function isOrdersTodayPoint(value) {
|
|
196
|
+
return isDailyOrderRow(value) && "isToday" in value && typeof value.isToday === "boolean";
|
|
197
|
+
}
|
|
198
|
+
function formatCurrency$1(value) {
|
|
199
|
+
return new Intl.NumberFormat("en-IN", {
|
|
200
|
+
style: "currency",
|
|
201
|
+
currency: "INR",
|
|
202
|
+
minimumFractionDigits: 0,
|
|
203
|
+
maximumFractionDigits: 0
|
|
204
|
+
}).format(value);
|
|
205
|
+
}
|
|
206
|
+
function formatCompactCurrency(value) {
|
|
207
|
+
return new Intl.NumberFormat("en-IN", {
|
|
208
|
+
style: "currency",
|
|
209
|
+
currency: "INR",
|
|
210
|
+
notation: "compact",
|
|
211
|
+
maximumFractionDigits: 1
|
|
212
|
+
}).format(value);
|
|
213
|
+
}
|
|
214
|
+
function formatChartLabel(value) {
|
|
215
|
+
if (!value) return "";
|
|
216
|
+
const parsed = new Date(value);
|
|
217
|
+
if (Number.isNaN(parsed.getTime())) return value;
|
|
218
|
+
return `${parsed.getUTCMonth() + 1}/${parsed.getUTCDate()}`;
|
|
219
|
+
}
|
|
220
|
+
function formatPercent(value) {
|
|
221
|
+
return `${value.toFixed(1)}%`;
|
|
222
|
+
}
|
|
223
|
+
function formatShortNumber(value) {
|
|
224
|
+
return new Intl.NumberFormat("en-IN", {
|
|
225
|
+
notation: "compact",
|
|
226
|
+
maximumFractionDigits: value < 10 ? 1 : 0
|
|
227
|
+
}).format(value);
|
|
228
|
+
}
|
|
229
|
+
function TooltipCard({
|
|
230
|
+
title,
|
|
231
|
+
children
|
|
80
232
|
}) {
|
|
81
|
-
var _a, _b;
|
|
82
|
-
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
83
|
-
const row = (_a = payload[0]) == null ? void 0 : _a.payload;
|
|
84
|
-
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : label);
|
|
85
|
-
const value = ((_b = payload[0]) == null ? void 0 : _b.value) ?? 0;
|
|
86
233
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
87
234
|
"div",
|
|
88
235
|
{
|
|
89
236
|
style: {
|
|
90
|
-
|
|
237
|
+
background: "linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(248,250,252,0.96) 100%)",
|
|
91
238
|
color: "#111827",
|
|
92
|
-
border: "1px solid
|
|
93
|
-
borderRadius: "
|
|
94
|
-
padding: "12px
|
|
95
|
-
boxShadow: "0
|
|
96
|
-
fontSize: "
|
|
97
|
-
lineHeight: 1.5
|
|
239
|
+
border: "1px solid rgba(148, 163, 184, 0.18)",
|
|
240
|
+
borderRadius: "14px",
|
|
241
|
+
padding: "12px 14px",
|
|
242
|
+
boxShadow: "0 16px 40px rgba(15, 23, 42, 0.12)",
|
|
243
|
+
fontSize: "13px",
|
|
244
|
+
lineHeight: 1.5,
|
|
245
|
+
minWidth: "176px"
|
|
98
246
|
},
|
|
99
247
|
children: [
|
|
100
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 600, marginBottom: "6px"
|
|
101
|
-
|
|
102
|
-
"Orders: ",
|
|
103
|
-
/* @__PURE__ */ jsxRuntime.jsx("strong", { style: { color: "#1f2937" }, children: value })
|
|
104
|
-
] })
|
|
248
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 600, marginBottom: "6px" }, children: title }),
|
|
249
|
+
children
|
|
105
250
|
]
|
|
106
251
|
}
|
|
107
252
|
);
|
|
108
253
|
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
254
|
+
function OrdersTrendTooltip({
|
|
255
|
+
active,
|
|
256
|
+
payload,
|
|
257
|
+
label
|
|
258
|
+
}) {
|
|
259
|
+
var _a, _b, _c, _d;
|
|
260
|
+
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
261
|
+
const row = isDailyOrderRow((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
262
|
+
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
263
|
+
const orders = ((_c = payload.find((entry) => entry.name === "Orders")) == null ? void 0 : _c.value) ?? 0;
|
|
264
|
+
const revenue = ((_d = payload.find((entry) => entry.name === "Revenue")) == null ? void 0 : _d.value) ?? 0;
|
|
265
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(TooltipCard, { title: displayLabel, children: [
|
|
266
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
267
|
+
"Orders: ",
|
|
268
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(orders).toLocaleString() })
|
|
269
|
+
] }),
|
|
270
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
271
|
+
"Revenue: ",
|
|
272
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency$1(Number(revenue)) })
|
|
273
|
+
] })
|
|
274
|
+
] });
|
|
275
|
+
}
|
|
276
|
+
function OrdersTodayTooltip({
|
|
277
|
+
active,
|
|
278
|
+
payload,
|
|
279
|
+
label
|
|
280
|
+
}) {
|
|
281
|
+
var _a, _b, _c;
|
|
282
|
+
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
283
|
+
const row = isOrdersTodayPoint((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
284
|
+
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
285
|
+
const orders = ((_c = payload[0]) == null ? void 0 : _c.value) ?? 0;
|
|
286
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(TooltipCard, { title: displayLabel, children: [
|
|
287
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
288
|
+
"Orders: ",
|
|
289
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(orders).toLocaleString() })
|
|
290
|
+
] }),
|
|
291
|
+
(row == null ? void 0 : row.isToday) ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#6B7280" }, children: "Current day" }) : null
|
|
292
|
+
] });
|
|
293
|
+
}
|
|
294
|
+
function OrdersSparklineTooltip({
|
|
295
|
+
active,
|
|
296
|
+
payload,
|
|
297
|
+
label
|
|
298
|
+
}) {
|
|
299
|
+
var _a, _b, _c;
|
|
300
|
+
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
301
|
+
const row = isDailyOrderRow((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
302
|
+
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
303
|
+
const orders = Number((_c = payload[0]) == null ? void 0 : _c.value) || 0;
|
|
304
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TooltipCard, { title: displayLabel, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
305
|
+
"Orders: ",
|
|
306
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(orders).toLocaleString() })
|
|
307
|
+
] }) });
|
|
308
|
+
}
|
|
309
|
+
function OutcomesTrendTooltip({
|
|
310
|
+
active,
|
|
311
|
+
payload,
|
|
312
|
+
label
|
|
313
|
+
}) {
|
|
314
|
+
var _a, _b;
|
|
315
|
+
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
316
|
+
const row = isDailyOrderRow((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
|
|
317
|
+
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
318
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TooltipCard, { title: displayLabel, children: payload.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
319
|
+
entry.name,
|
|
320
|
+
":",
|
|
321
|
+
" ",
|
|
322
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(Number(entry.value) || 0).toLocaleString() })
|
|
323
|
+
] }, String(entry.name))) });
|
|
324
|
+
}
|
|
325
|
+
const KPI_ICON_BG = {
|
|
326
|
+
green: "bg-emerald-500/20",
|
|
327
|
+
blue: "bg-sky-500/20",
|
|
328
|
+
purple: "bg-violet-500/20",
|
|
329
|
+
amber: "bg-amber-500/20"
|
|
113
330
|
};
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
331
|
+
function AtlasKpiCard({
|
|
332
|
+
index,
|
|
333
|
+
label,
|
|
334
|
+
value,
|
|
335
|
+
helper,
|
|
336
|
+
accent
|
|
337
|
+
}) {
|
|
338
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-base p-3 shadow-sm", children: [
|
|
339
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
340
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-left text-[11px] leading-tight text-ui-fg-muted", children: [
|
|
341
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-semibold text-ui-fg-base", children: [
|
|
342
|
+
index,
|
|
343
|
+
"."
|
|
344
|
+
] }),
|
|
345
|
+
" ",
|
|
346
|
+
label
|
|
347
|
+
] }),
|
|
348
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
349
|
+
"div",
|
|
350
|
+
{
|
|
351
|
+
className: `h-7 w-7 shrink-0 rounded-full ${KPI_ICON_BG[accent]}`,
|
|
352
|
+
"aria-hidden": true
|
|
353
|
+
}
|
|
354
|
+
)
|
|
355
|
+
] }),
|
|
356
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 text-base font-semibold tabular-nums tracking-tight text-ui-fg-base sm:text-lg", children: value }),
|
|
357
|
+
helper ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-1 text-[10px] leading-snug text-ui-fg-muted", children: helper }) : null
|
|
358
|
+
] });
|
|
126
359
|
}
|
|
127
360
|
const SUMMARY_PERIODS$1 = [
|
|
128
361
|
{ value: "all", label: "All time" },
|
|
@@ -136,7 +369,34 @@ const OVER_TIME_PERIODS = [
|
|
|
136
369
|
{ value: "one_month", label: "One month" },
|
|
137
370
|
{ value: "one_year", label: "One year" }
|
|
138
371
|
];
|
|
372
|
+
const STATUS_BAR_COLORS = {
|
|
373
|
+
delivered: "#10B981",
|
|
374
|
+
cancelled: "#EF4444",
|
|
375
|
+
exchange: "#F59E0B",
|
|
376
|
+
return: "#8B5CF6",
|
|
377
|
+
today: "var(--medusa-color-ui-fg-interactive)",
|
|
378
|
+
todayMuted: "#BFDBFE",
|
|
379
|
+
orders: "#38BDF8",
|
|
380
|
+
revenue: "#D946EF"
|
|
381
|
+
};
|
|
382
|
+
const SELECT_CLASS_NAME = "h-8 min-w-[120px] rounded-lg border border-ui-border-strong bg-ui-bg-base px-2 text-xs text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive";
|
|
383
|
+
function EmptyAnalyticsPanel({
|
|
384
|
+
title,
|
|
385
|
+
description
|
|
386
|
+
}) {
|
|
387
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", className: "h-full", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-[120px] flex-col justify-between gap-2 rounded-lg border border-dashed border-ui-border-base bg-ui-bg-subtle/40 p-3", children: [
|
|
388
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
389
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-xs font-semibold", children: title }),
|
|
390
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[11px] leading-snug", children: description })
|
|
391
|
+
] }),
|
|
392
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-base/80 px-2.5 py-2", children: [
|
|
393
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Empty for now" }),
|
|
394
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base mt-0.5 text-[11px] leading-snug", children: "Reserved for live analytics when data is available." })
|
|
395
|
+
] })
|
|
396
|
+
] }) });
|
|
397
|
+
}
|
|
139
398
|
function OrdersDashboard() {
|
|
399
|
+
var _a, _b;
|
|
140
400
|
const [data, setData] = react.useState(null);
|
|
141
401
|
const [loading, setLoading] = react.useState(true);
|
|
142
402
|
const [error, setError] = react.useState(null);
|
|
@@ -145,12 +405,15 @@ function OrdersDashboard() {
|
|
|
145
405
|
const [overTimePeriod, setOverTimePeriod] = react.useState("one_week");
|
|
146
406
|
const [overTimeLoading, setOverTimeLoading] = react.useState(true);
|
|
147
407
|
const [overTimeError, setOverTimeError] = react.useState(null);
|
|
408
|
+
const [todayContext, setTodayContext] = react.useState([]);
|
|
409
|
+
const [todayContextLoading, setTodayContextLoading] = react.useState(true);
|
|
410
|
+
const [todayContextError, setTodayContextError] = react.useState(null);
|
|
148
411
|
react.useEffect(() => {
|
|
149
412
|
let cancelled = false;
|
|
150
413
|
setLoading(true);
|
|
151
414
|
setError(null);
|
|
152
415
|
const url = `/admin/analytics/orders-summary?days=${filter}`;
|
|
153
|
-
fetch(url).then((res) => {
|
|
416
|
+
fetch(url, { credentials: "include" }).then((res) => {
|
|
154
417
|
if (!res.ok) throw new Error(res.statusText);
|
|
155
418
|
return res.json();
|
|
156
419
|
}).then((body) => {
|
|
@@ -169,7 +432,7 @@ function OrdersDashboard() {
|
|
|
169
432
|
setOverTimeLoading(true);
|
|
170
433
|
setOverTimeError(null);
|
|
171
434
|
const url = `/admin/analytics/orders-over-time?period=${overTimePeriod}`;
|
|
172
|
-
fetch(url).then((res) => {
|
|
435
|
+
fetch(url, { credentials: "include" }).then((res) => {
|
|
173
436
|
if (!res.ok) throw new Error(res.statusText);
|
|
174
437
|
return res.json();
|
|
175
438
|
}).then((body) => {
|
|
@@ -183,11 +446,135 @@ function OrdersDashboard() {
|
|
|
183
446
|
cancelled = true;
|
|
184
447
|
};
|
|
185
448
|
}, [overTimePeriod]);
|
|
449
|
+
react.useEffect(() => {
|
|
450
|
+
let cancelled = false;
|
|
451
|
+
setTodayContextLoading(true);
|
|
452
|
+
setTodayContextError(null);
|
|
453
|
+
fetch("/admin/analytics/orders-over-time?period=one_week", {
|
|
454
|
+
credentials: "include"
|
|
455
|
+
}).then((res) => {
|
|
456
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
457
|
+
return res.json();
|
|
458
|
+
}).then((body) => {
|
|
459
|
+
if (!cancelled) setTodayContext(body.dailyOrders ?? []);
|
|
460
|
+
}).catch((e) => {
|
|
461
|
+
if (!cancelled) {
|
|
462
|
+
setTodayContextError(e instanceof Error ? e.message : String(e));
|
|
463
|
+
}
|
|
464
|
+
}).finally(() => {
|
|
465
|
+
if (!cancelled) setTodayContextLoading(false);
|
|
466
|
+
});
|
|
467
|
+
return () => {
|
|
468
|
+
cancelled = true;
|
|
469
|
+
};
|
|
470
|
+
}, []);
|
|
471
|
+
const selectedSummaryPeriod = ((_a = SUMMARY_PERIODS$1.find((period) => period.value === filter)) == null ? void 0 : _a.label) ?? "All time";
|
|
472
|
+
const operationsBreakdown = (data == null ? void 0 : data.operationalCounts) ?? {
|
|
473
|
+
cancelled: (data == null ? void 0 : data.cancelledOrders) ?? 0,
|
|
474
|
+
delivered: (data == null ? void 0 : data.deliveredOrders) ?? 0,
|
|
475
|
+
exchange: (data == null ? void 0 : data.exchangeOrders) ?? 0,
|
|
476
|
+
return: (data == null ? void 0 : data.returnOrders) ?? 0
|
|
477
|
+
};
|
|
478
|
+
const outcomesTimeSeriesHasPoints = react.useMemo(
|
|
479
|
+
() => dailyOrders.some(
|
|
480
|
+
(d) => d.delivered_count > 0 || d.cancelled_count > 0 || d.exchange_count > 0 || d.return_count > 0
|
|
481
|
+
),
|
|
482
|
+
[dailyOrders]
|
|
483
|
+
);
|
|
484
|
+
const totalResolvedOrders = operationsBreakdown.delivered + operationsBreakdown.cancelled;
|
|
485
|
+
const deliveryRate = totalResolvedOrders > 0 ? operationsBreakdown.delivered / totalResolvedOrders * 100 : 0;
|
|
486
|
+
const primaryStats = data ? [
|
|
487
|
+
{
|
|
488
|
+
label: "Total revenue",
|
|
489
|
+
value: formatCurrency$1(data.totalRevenue),
|
|
490
|
+
helper: "Non-cancelled orders",
|
|
491
|
+
accent: "green"
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
label: "Total orders",
|
|
495
|
+
value: data.totalOrders.toLocaleString(),
|
|
496
|
+
helper: selectedSummaryPeriod,
|
|
497
|
+
accent: "blue"
|
|
498
|
+
},
|
|
499
|
+
{
|
|
500
|
+
label: "Delivery rate",
|
|
501
|
+
value: formatPercent(deliveryRate),
|
|
502
|
+
helper: "Delivered vs cancelled",
|
|
503
|
+
accent: "purple"
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
label: "Average order value",
|
|
507
|
+
value: formatCurrency$1(data.aov),
|
|
508
|
+
helper: "Per non-cancelled order",
|
|
509
|
+
accent: "amber"
|
|
510
|
+
}
|
|
511
|
+
] : [];
|
|
512
|
+
const secondaryStats = data ? [
|
|
513
|
+
{ label: "Orders today", value: data.ordersToday.toLocaleString() },
|
|
514
|
+
{ label: "Pending orders", value: data.pendingOrders.toLocaleString() },
|
|
515
|
+
{ label: "Delivered orders", value: data.deliveredOrders.toLocaleString() },
|
|
516
|
+
{ label: "Cancelled orders", value: data.cancelledOrders.toLocaleString() },
|
|
517
|
+
{ label: "Exchange orders", value: data.exchangeOrders.toLocaleString() },
|
|
518
|
+
{
|
|
519
|
+
label: "Avg. fulfillment time",
|
|
520
|
+
value: data.avgFulfillmentTimeHours != null ? `${data.avgFulfillmentTimeHours}h` : "—",
|
|
521
|
+
helper: "Measured in hours"
|
|
522
|
+
}
|
|
523
|
+
] : [];
|
|
524
|
+
const todayChartData = react.useMemo(
|
|
525
|
+
() => todayContext.map((point, index) => ({
|
|
526
|
+
...point,
|
|
527
|
+
isToday: index === todayContext.length - 1
|
|
528
|
+
})),
|
|
529
|
+
[todayContext]
|
|
530
|
+
);
|
|
531
|
+
const todayOrdersAverage = todayChartData.length > 0 ? todayChartData.reduce((sum, point) => sum + point.orders_count, 0) / todayChartData.length : 0;
|
|
532
|
+
const todayPoint = todayChartData[todayChartData.length - 1] ?? null;
|
|
533
|
+
const todayDelta = todayPoint && todayOrdersAverage > 0 ? todayPoint.orders_count - todayOrdersAverage : null;
|
|
534
|
+
const trendRevenueTotal = dailyOrders.reduce(
|
|
535
|
+
(sum, point) => sum + point.total_revenue,
|
|
536
|
+
0
|
|
537
|
+
);
|
|
538
|
+
const trendOrdersTotal = dailyOrders.reduce(
|
|
539
|
+
(sum, point) => sum + point.orders_count,
|
|
540
|
+
0
|
|
541
|
+
);
|
|
542
|
+
const trendAverageRevenue = dailyOrders.length > 0 ? trendRevenueTotal / dailyOrders.length : 0;
|
|
543
|
+
const trendAverageOrders = dailyOrders.length > 0 ? trendOrdersTotal / dailyOrders.length : 0;
|
|
544
|
+
const peakRevenuePoint = dailyOrders.length > 0 ? dailyOrders.reduce(
|
|
545
|
+
(peak, point) => point.total_revenue > peak.total_revenue ? point : peak
|
|
546
|
+
) : null;
|
|
547
|
+
const quickPulseMetrics = [
|
|
548
|
+
{
|
|
549
|
+
label: "Range revenue",
|
|
550
|
+
value: formatCompactCurrency(trendRevenueTotal),
|
|
551
|
+
helper: selectedSummaryPeriod,
|
|
552
|
+
accentClassName: "border-emerald-400/25 bg-emerald-500/5"
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
label: "Range orders",
|
|
556
|
+
value: formatShortNumber(trendOrdersTotal),
|
|
557
|
+
helper: "Current trend window",
|
|
558
|
+
accentClassName: "border-sky-400/25 bg-sky-500/5"
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
label: "Avg/day orders",
|
|
562
|
+
value: trendAverageOrders.toFixed(1),
|
|
563
|
+
helper: "Across selected chart range",
|
|
564
|
+
accentClassName: "border-violet-400/25 bg-violet-500/5"
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
label: "Peak revenue day",
|
|
568
|
+
value: peakRevenuePoint ? formatCompactCurrency(peakRevenuePoint.total_revenue) : "—",
|
|
569
|
+
helper: (peakRevenuePoint == null ? void 0 : peakRevenuePoint.label) ?? formatChartLabel(peakRevenuePoint == null ? void 0 : peakRevenuePoint.date),
|
|
570
|
+
accentClassName: "border-amber-400/25 bg-amber-500/5"
|
|
571
|
+
}
|
|
572
|
+
];
|
|
186
573
|
if (loading) {
|
|
187
574
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
188
575
|
"div",
|
|
189
576
|
{
|
|
190
|
-
className: "flex items-center justify-center
|
|
577
|
+
className: "flex min-h-[200px] items-center justify-center",
|
|
191
578
|
role: "status",
|
|
192
579
|
"aria-label": "Loading analytics",
|
|
193
580
|
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading…" })
|
|
@@ -197,253 +584,656 @@ function OrdersDashboard() {
|
|
|
197
584
|
if (error || !data) {
|
|
198
585
|
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: error ?? "Failed to load analytics" }) });
|
|
199
586
|
}
|
|
200
|
-
|
|
201
|
-
const value = key === "delivered" ? data.statusCounts.delivered ?? data.deliveredOrders ?? 0 : key === "cancelled" ? data.statusCounts.cancelled ?? data.cancelledOrders ?? 0 : data.statusCounts.pending ?? data.pendingOrders ?? 0;
|
|
202
|
-
return { name: label, value, color };
|
|
203
|
-
}).filter((d) => d.value > 0);
|
|
204
|
-
const returnsExchangesData = [
|
|
205
|
-
{ name: "Return orders", value: data.returnOrders, color: "#EF4444" },
|
|
206
|
-
{ name: "Exchange orders", value: data.exchangeOrders, color: "#F59E0B" }
|
|
207
|
-
].filter((d) => d.value > 0);
|
|
208
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsDashboardShell, { children: [
|
|
587
|
+
return /* @__PURE__ */ jsxRuntime.jsx(AnalyticsDashboardShell, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base/70 bg-ui-bg-subtle/35 p-2 md:p-3", children: [
|
|
209
588
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
210
589
|
AnalyticsDashboardHeader,
|
|
211
590
|
{
|
|
591
|
+
variant: "premium",
|
|
212
592
|
title: "Orders",
|
|
213
|
-
description: "
|
|
214
|
-
actions: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
215
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
216
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
593
|
+
description: "Revenue, volume, and outcomes in a compact overview sized for a single viewport.",
|
|
594
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-end gap-2.5", children: [
|
|
595
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
596
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
597
|
+
ui.Label,
|
|
598
|
+
{
|
|
599
|
+
htmlFor: "analytics-period",
|
|
600
|
+
className: "text-ui-fg-muted text-xs font-medium",
|
|
601
|
+
children: "Period"
|
|
602
|
+
}
|
|
603
|
+
),
|
|
217
604
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
218
605
|
"select",
|
|
219
606
|
{
|
|
220
607
|
id: "analytics-period",
|
|
221
608
|
value: filter,
|
|
222
609
|
onChange: (e) => setFilter(e.target.value),
|
|
223
|
-
className:
|
|
610
|
+
className: SELECT_CLASS_NAME,
|
|
224
611
|
children: SUMMARY_PERIODS$1.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.value, children: p.label }, p.value))
|
|
225
612
|
}
|
|
226
613
|
)
|
|
227
614
|
] }),
|
|
228
|
-
filter !== "all" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
615
|
+
filter !== "all" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center self-end pb-1", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
229
616
|
"button",
|
|
230
617
|
{
|
|
231
618
|
type: "button",
|
|
232
619
|
onClick: () => setFilter("all"),
|
|
233
|
-
className: "text-xs text-ui-fg-muted hover:text-ui-fg-base
|
|
620
|
+
className: "text-xs text-ui-fg-muted transition-colors hover:text-ui-fg-base",
|
|
234
621
|
"aria-label": "Show all orders (clear filter)",
|
|
235
|
-
children: "Clear
|
|
622
|
+
children: "Clear period"
|
|
236
623
|
}
|
|
237
|
-
)
|
|
624
|
+
) })
|
|
238
625
|
] })
|
|
239
626
|
}
|
|
240
627
|
),
|
|
241
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
242
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
label: "Total orders",
|
|
246
|
-
value: data.totalOrders.toLocaleString()
|
|
247
|
-
}
|
|
248
|
-
),
|
|
249
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
250
|
-
AnalyticsStatCard,
|
|
628
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
629
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Order overview" }) }),
|
|
630
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2 lg:grid-cols-4", children: primaryStats.map((stat, index) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
631
|
+
AtlasKpiCard,
|
|
251
632
|
{
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
633
|
+
index: index + 1,
|
|
634
|
+
label: stat.label,
|
|
635
|
+
value: stat.value,
|
|
636
|
+
helper: stat.helper,
|
|
637
|
+
accent: stat.accent
|
|
638
|
+
},
|
|
639
|
+
stat.label
|
|
640
|
+
)) })
|
|
641
|
+
] }),
|
|
642
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 grid grid-cols-12 gap-3", children: [
|
|
643
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-12 xl:col-span-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
644
|
+
AnalyticsSection,
|
|
258
645
|
{
|
|
259
|
-
|
|
260
|
-
|
|
646
|
+
variant: "atlas",
|
|
647
|
+
title: "Revenue & orders",
|
|
648
|
+
description: "Time series for the selected range. Window totals below match this chart.",
|
|
649
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
650
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "over-time-period", className: "text-ui-fg-muted text-xs", children: "Range" }),
|
|
651
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
652
|
+
"select",
|
|
653
|
+
{
|
|
654
|
+
id: "over-time-period",
|
|
655
|
+
value: overTimePeriod,
|
|
656
|
+
onChange: (e) => setOverTimePeriod(e.target.value),
|
|
657
|
+
className: SELECT_CLASS_NAME,
|
|
658
|
+
children: OVER_TIME_PERIODS.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.value, children: p.label }, p.value))
|
|
659
|
+
}
|
|
660
|
+
)
|
|
661
|
+
] }),
|
|
662
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
663
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-3", children: [
|
|
664
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
665
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window revenue" }),
|
|
666
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: formatCompactCurrency(trendRevenueTotal) }),
|
|
667
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: [
|
|
668
|
+
"Avg/day ",
|
|
669
|
+
formatCompactCurrency(trendAverageRevenue)
|
|
670
|
+
] })
|
|
671
|
+
] }) }),
|
|
672
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
673
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window orders" }),
|
|
674
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: trendOrdersTotal.toLocaleString() }),
|
|
675
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: [
|
|
676
|
+
"Avg/day ",
|
|
677
|
+
trendAverageOrders.toFixed(1)
|
|
678
|
+
] })
|
|
679
|
+
] }) }),
|
|
680
|
+
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
|
|
681
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Comparison" }),
|
|
682
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-sm font-semibold tracking-tight", children: "Last period" }),
|
|
683
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] leading-snug", children: "Reserved — API unchanged." })
|
|
684
|
+
] }) })
|
|
685
|
+
] }),
|
|
686
|
+
/* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", className: "relative overflow-hidden", children: [
|
|
687
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "pointer-events-none absolute inset-x-6 top-0 h-12 rounded-b-full bg-gradient-to-r from-sky-400/10 via-fuchsia-400/10 to-transparent blur-xl" }),
|
|
688
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative space-y-2", children: [
|
|
689
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [
|
|
690
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0", children: [
|
|
691
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Trend overview" }),
|
|
692
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-sm font-semibold", children: ((_b = OVER_TIME_PERIODS.find((period) => period.value === overTimePeriod)) == null ? void 0 : _b.label) ?? "One week" })
|
|
693
|
+
] }),
|
|
694
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap justify-end gap-2 text-[10px] text-ui-fg-muted", children: [
|
|
695
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
696
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-sky-400" }),
|
|
697
|
+
"Orders"
|
|
698
|
+
] }),
|
|
699
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
|
|
700
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-fuchsia-400" }),
|
|
701
|
+
"Revenue"
|
|
702
|
+
] })
|
|
703
|
+
] })
|
|
704
|
+
] }),
|
|
705
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(220px,32vh)] min-h-[180px] sm:h-[min(240px,34vh)]", children: overTimeLoading ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
706
|
+
"div",
|
|
707
|
+
{
|
|
708
|
+
className: "flex h-full items-center justify-center",
|
|
709
|
+
role: "status",
|
|
710
|
+
"aria-label": "Loading orders over time",
|
|
711
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading chart…" })
|
|
712
|
+
}
|
|
713
|
+
) : overTimeError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-xs", children: overTimeError }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
714
|
+
recharts.ComposedChart,
|
|
715
|
+
{
|
|
716
|
+
data: dailyOrders,
|
|
717
|
+
margin: { top: 4, right: 6, left: -6, bottom: 0 },
|
|
718
|
+
children: [
|
|
719
|
+
/* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
|
|
720
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "ordersGradient", x1: "0", y1: "0", x2: "1", y2: "0", children: [
|
|
721
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#67E8F9" }),
|
|
722
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#3B82F6" })
|
|
723
|
+
] }),
|
|
724
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "revenueGradient", x1: "0", y1: "0", x2: "1", y2: "0", children: [
|
|
725
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#F472B6" }),
|
|
726
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#C084FC" })
|
|
727
|
+
] }),
|
|
728
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "ordersAreaFill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
729
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#38BDF8", stopOpacity: 0.22 }),
|
|
730
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#38BDF8", stopOpacity: 0 })
|
|
731
|
+
] }),
|
|
732
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "revenueAreaFill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
733
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#E879F9", stopOpacity: 0.2 }),
|
|
734
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#E879F9", stopOpacity: 0 })
|
|
735
|
+
] })
|
|
736
|
+
] }),
|
|
737
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
738
|
+
recharts.CartesianGrid,
|
|
739
|
+
{
|
|
740
|
+
stroke: "rgba(148,163,184,0.14)",
|
|
741
|
+
strokeDasharray: "3 3",
|
|
742
|
+
vertical: false
|
|
743
|
+
}
|
|
744
|
+
),
|
|
745
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
746
|
+
recharts.XAxis,
|
|
747
|
+
{
|
|
748
|
+
dataKey: "date",
|
|
749
|
+
tick: { fontSize: 10, fill: "var(--medusa-color-ui-fg-muted)" },
|
|
750
|
+
tickLine: false,
|
|
751
|
+
axisLine: false,
|
|
752
|
+
tickFormatter: (value, index) => {
|
|
753
|
+
const row = dailyOrders[index];
|
|
754
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel(value);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
),
|
|
758
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
759
|
+
recharts.YAxis,
|
|
760
|
+
{
|
|
761
|
+
yAxisId: "orders",
|
|
762
|
+
width: 32,
|
|
763
|
+
tick: { fontSize: 10, fill: "var(--medusa-color-ui-fg-muted)" },
|
|
764
|
+
tickLine: false,
|
|
765
|
+
axisLine: false,
|
|
766
|
+
allowDecimals: false,
|
|
767
|
+
tickFormatter: (value) => Math.floor(Number(value) || 0).toLocaleString()
|
|
768
|
+
}
|
|
769
|
+
),
|
|
770
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
771
|
+
recharts.YAxis,
|
|
772
|
+
{
|
|
773
|
+
yAxisId: "revenue",
|
|
774
|
+
orientation: "right",
|
|
775
|
+
width: 40,
|
|
776
|
+
tick: { fontSize: 10, fill: "var(--medusa-color-ui-fg-muted)" },
|
|
777
|
+
tickLine: false,
|
|
778
|
+
axisLine: false,
|
|
779
|
+
tickFormatter: (value) => formatCompactCurrency(Number(value) || 0)
|
|
780
|
+
}
|
|
781
|
+
),
|
|
782
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
783
|
+
recharts.Tooltip,
|
|
784
|
+
{
|
|
785
|
+
content: /* @__PURE__ */ jsxRuntime.jsx(OrdersTrendTooltip, {}),
|
|
786
|
+
cursor: {
|
|
787
|
+
stroke: "rgba(248, 250, 252, 0.35)",
|
|
788
|
+
strokeWidth: 1
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
),
|
|
792
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
793
|
+
recharts.Legend,
|
|
794
|
+
{
|
|
795
|
+
wrapperStyle: { fontSize: 10, paddingTop: 2 },
|
|
796
|
+
iconType: "circle",
|
|
797
|
+
iconSize: 8
|
|
798
|
+
}
|
|
799
|
+
),
|
|
800
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
801
|
+
recharts.Area,
|
|
802
|
+
{
|
|
803
|
+
yAxisId: "orders",
|
|
804
|
+
type: "natural",
|
|
805
|
+
dataKey: "orders_count",
|
|
806
|
+
stroke: "none",
|
|
807
|
+
fill: "url(#ordersAreaFill)",
|
|
808
|
+
isAnimationActive: false
|
|
809
|
+
}
|
|
810
|
+
),
|
|
811
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
812
|
+
recharts.Area,
|
|
813
|
+
{
|
|
814
|
+
yAxisId: "revenue",
|
|
815
|
+
type: "natural",
|
|
816
|
+
dataKey: "total_revenue",
|
|
817
|
+
stroke: "none",
|
|
818
|
+
fill: "url(#revenueAreaFill)",
|
|
819
|
+
isAnimationActive: false
|
|
820
|
+
}
|
|
821
|
+
),
|
|
822
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
823
|
+
recharts.Line,
|
|
824
|
+
{
|
|
825
|
+
yAxisId: "orders",
|
|
826
|
+
type: "natural",
|
|
827
|
+
dataKey: "orders_count",
|
|
828
|
+
name: "Orders",
|
|
829
|
+
stroke: "url(#ordersGradient)",
|
|
830
|
+
strokeWidth: 2,
|
|
831
|
+
dot: false,
|
|
832
|
+
activeDot: { r: 4, fill: STATUS_BAR_COLORS.orders }
|
|
833
|
+
}
|
|
834
|
+
),
|
|
835
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
836
|
+
recharts.Line,
|
|
837
|
+
{
|
|
838
|
+
yAxisId: "revenue",
|
|
839
|
+
type: "natural",
|
|
840
|
+
dataKey: "total_revenue",
|
|
841
|
+
name: "Revenue",
|
|
842
|
+
stroke: "url(#revenueGradient)",
|
|
843
|
+
strokeWidth: 2,
|
|
844
|
+
dot: false,
|
|
845
|
+
activeDot: { r: 4, fill: STATUS_BAR_COLORS.revenue }
|
|
846
|
+
}
|
|
847
|
+
)
|
|
848
|
+
]
|
|
849
|
+
}
|
|
850
|
+
) }) })
|
|
851
|
+
] })
|
|
852
|
+
] })
|
|
853
|
+
] })
|
|
261
854
|
}
|
|
262
|
-
),
|
|
263
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
264
|
-
|
|
855
|
+
) }),
|
|
856
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-12 flex flex-col gap-3 xl:col-span-3", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
857
|
+
AnalyticsSection,
|
|
265
858
|
{
|
|
266
|
-
|
|
267
|
-
|
|
859
|
+
variant: "atlas",
|
|
860
|
+
title: "Pulse & range",
|
|
861
|
+
description: "Window totals and an orders time series for the selected range.",
|
|
862
|
+
children: [
|
|
863
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2", children: quickPulseMetrics.map((metric) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
864
|
+
"div",
|
|
865
|
+
{
|
|
866
|
+
className: `rounded-lg border border-ui-border-base bg-ui-bg-base px-2 py-2 shadow-sm ${metric.accentClassName}`.trim(),
|
|
867
|
+
children: [
|
|
868
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: metric.label }),
|
|
869
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base mt-0.5 text-sm font-semibold tracking-tight", children: metric.value }),
|
|
870
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted mt-0.5 text-[9px] leading-snug", children: metric.helper })
|
|
871
|
+
]
|
|
872
|
+
},
|
|
873
|
+
metric.label
|
|
874
|
+
)) }),
|
|
875
|
+
!overTimeLoading && !overTimeError && dailyOrders.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", className: "mt-2", children: [
|
|
876
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-1 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Orders (time series)" }),
|
|
877
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[88px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
878
|
+
recharts.LineChart,
|
|
879
|
+
{
|
|
880
|
+
data: dailyOrders,
|
|
881
|
+
margin: { top: 2, right: 4, left: -18, bottom: 2 },
|
|
882
|
+
children: [
|
|
883
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
884
|
+
recharts.CartesianGrid,
|
|
885
|
+
{
|
|
886
|
+
strokeDasharray: "3 3",
|
|
887
|
+
vertical: false,
|
|
888
|
+
stroke: "rgba(148,163,184,0.12)"
|
|
889
|
+
}
|
|
890
|
+
),
|
|
891
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "date", hide: true }),
|
|
892
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { hide: true, domain: ["auto", "auto"] }),
|
|
893
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
894
|
+
recharts.Tooltip,
|
|
895
|
+
{
|
|
896
|
+
content: /* @__PURE__ */ jsxRuntime.jsx(OrdersSparklineTooltip, {}),
|
|
897
|
+
cursor: { stroke: "rgba(148,163,184,0.35)", strokeWidth: 1 }
|
|
898
|
+
}
|
|
899
|
+
),
|
|
900
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
901
|
+
recharts.Line,
|
|
902
|
+
{
|
|
903
|
+
type: "natural",
|
|
904
|
+
dataKey: "orders_count",
|
|
905
|
+
stroke: "#3b82f6",
|
|
906
|
+
strokeWidth: 2,
|
|
907
|
+
dot: { r: 2, fill: "#3b82f6" },
|
|
908
|
+
activeDot: { r: 3 }
|
|
909
|
+
}
|
|
910
|
+
)
|
|
911
|
+
]
|
|
912
|
+
}
|
|
913
|
+
) }) })
|
|
914
|
+
] }) : null
|
|
915
|
+
]
|
|
268
916
|
}
|
|
269
|
-
),
|
|
270
|
-
/* @__PURE__ */ jsxRuntime.
|
|
917
|
+
) }),
|
|
918
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-12 flex flex-col gap-3 xl:col-span-3", children: [
|
|
919
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
920
|
+
AnalyticsSection,
|
|
921
|
+
{
|
|
922
|
+
variant: "atlas",
|
|
923
|
+
title: "Conversion funnel",
|
|
924
|
+
description: "Storefront funnel — connect events to enable this chart.",
|
|
925
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
926
|
+
EmptyAnalyticsPanel,
|
|
927
|
+
{
|
|
928
|
+
title: "Visitors to purchase funnel",
|
|
929
|
+
description: "Visitor, product view, cart, checkout, and purchase steps require storefront analytics."
|
|
930
|
+
}
|
|
931
|
+
)
|
|
932
|
+
}
|
|
933
|
+
),
|
|
934
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
935
|
+
AnalyticsSection,
|
|
936
|
+
{
|
|
937
|
+
variant: "atlas",
|
|
938
|
+
title: "Traffic sources",
|
|
939
|
+
description: "Attribution mix when marketing data is available.",
|
|
940
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
941
|
+
EmptyAnalyticsPanel,
|
|
942
|
+
{
|
|
943
|
+
title: "Source mix",
|
|
944
|
+
description: "Channel breakdown (direct, search, social, email) will appear here once connected."
|
|
945
|
+
}
|
|
946
|
+
)
|
|
947
|
+
}
|
|
948
|
+
)
|
|
949
|
+
] })
|
|
950
|
+
] }),
|
|
951
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 space-y-1.5", children: [
|
|
952
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Operational signals" }) }),
|
|
953
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2 lg:grid-cols-3 xl:grid-cols-6", children: secondaryStats.map((stat) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
271
954
|
AnalyticsStatCard,
|
|
272
955
|
{
|
|
273
|
-
label:
|
|
274
|
-
value:
|
|
275
|
-
|
|
276
|
-
|
|
956
|
+
label: stat.label,
|
|
957
|
+
value: stat.value,
|
|
958
|
+
helper: stat.helper,
|
|
959
|
+
variant: "compact"
|
|
960
|
+
},
|
|
961
|
+
stat.label
|
|
962
|
+
)) })
|
|
963
|
+
] }),
|
|
964
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 grid grid-cols-1 items-start gap-3 xl:grid-cols-12", children: [
|
|
277
965
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
278
|
-
|
|
966
|
+
AnalyticsSection,
|
|
279
967
|
{
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
968
|
+
variant: "atlas",
|
|
969
|
+
className: "xl:col-span-7",
|
|
970
|
+
title: "Order outcomes",
|
|
971
|
+
description: "Delivered, cancelled, exchanges, and returns per bucket — same window as Revenue & orders.",
|
|
972
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[200px] sm:h-[220px]", children: overTimeLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) : overTimeError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-xs", children: overTimeError }) }) : !outcomesTimeSeriesHasPoints ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "No outcome events in this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
973
|
+
recharts.LineChart,
|
|
974
|
+
{
|
|
975
|
+
data: dailyOrders,
|
|
976
|
+
margin: { top: 4, right: 8, left: 0, bottom: 0 },
|
|
977
|
+
children: [
|
|
978
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
979
|
+
recharts.CartesianGrid,
|
|
980
|
+
{
|
|
981
|
+
stroke: "rgba(148,163,184,0.14)",
|
|
982
|
+
strokeDasharray: "3 3",
|
|
983
|
+
vertical: false
|
|
984
|
+
}
|
|
985
|
+
),
|
|
986
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
987
|
+
recharts.XAxis,
|
|
988
|
+
{
|
|
989
|
+
dataKey: "date",
|
|
990
|
+
tick: { fontSize: 10, fill: "var(--medusa-color-ui-fg-muted)" },
|
|
991
|
+
tickLine: false,
|
|
992
|
+
axisLine: false,
|
|
993
|
+
tickFormatter: (value) => {
|
|
994
|
+
const dateStr = typeof value === "string" ? value : String(value);
|
|
995
|
+
const row = dailyOrders.find((d) => d.date === dateStr);
|
|
996
|
+
return (row == null ? void 0 : row.label) ?? formatChartLabel(dateStr);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
),
|
|
1000
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1001
|
+
recharts.YAxis,
|
|
1002
|
+
{
|
|
1003
|
+
width: 28,
|
|
1004
|
+
tick: { fontSize: 10, fill: "var(--medusa-color-ui-fg-muted)" },
|
|
1005
|
+
tickLine: false,
|
|
1006
|
+
axisLine: false,
|
|
1007
|
+
allowDecimals: false,
|
|
1008
|
+
tickFormatter: (value) => Math.floor(Number(value) || 0).toLocaleString()
|
|
1009
|
+
}
|
|
1010
|
+
),
|
|
1011
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1012
|
+
recharts.Tooltip,
|
|
1013
|
+
{
|
|
1014
|
+
content: /* @__PURE__ */ jsxRuntime.jsx(OutcomesTrendTooltip, {}),
|
|
1015
|
+
cursor: { stroke: "rgba(148,163,184,0.35)", strokeWidth: 1 }
|
|
1016
|
+
}
|
|
1017
|
+
),
|
|
1018
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1019
|
+
recharts.Legend,
|
|
1020
|
+
{
|
|
1021
|
+
wrapperStyle: { fontSize: 9, paddingTop: 0 },
|
|
1022
|
+
iconType: "circle",
|
|
1023
|
+
iconSize: 6
|
|
1024
|
+
}
|
|
1025
|
+
),
|
|
1026
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1027
|
+
recharts.Line,
|
|
1028
|
+
{
|
|
1029
|
+
type: "natural",
|
|
1030
|
+
dataKey: "delivered_count",
|
|
1031
|
+
name: "Delivered",
|
|
1032
|
+
stroke: STATUS_BAR_COLORS.delivered,
|
|
1033
|
+
strokeWidth: 2,
|
|
1034
|
+
dot: false
|
|
1035
|
+
}
|
|
1036
|
+
),
|
|
1037
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1038
|
+
recharts.Line,
|
|
1039
|
+
{
|
|
1040
|
+
type: "natural",
|
|
1041
|
+
dataKey: "cancelled_count",
|
|
1042
|
+
name: "Cancelled",
|
|
1043
|
+
stroke: STATUS_BAR_COLORS.cancelled,
|
|
1044
|
+
strokeWidth: 2,
|
|
1045
|
+
dot: false
|
|
1046
|
+
}
|
|
1047
|
+
),
|
|
1048
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1049
|
+
recharts.Line,
|
|
1050
|
+
{
|
|
1051
|
+
type: "natural",
|
|
1052
|
+
dataKey: "exchange_count",
|
|
1053
|
+
name: "Exchanges",
|
|
1054
|
+
stroke: STATUS_BAR_COLORS.exchange,
|
|
1055
|
+
strokeWidth: 2,
|
|
1056
|
+
dot: false
|
|
1057
|
+
}
|
|
1058
|
+
),
|
|
1059
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1060
|
+
recharts.Line,
|
|
1061
|
+
{
|
|
1062
|
+
type: "natural",
|
|
1063
|
+
dataKey: "return_count",
|
|
1064
|
+
name: "Returns",
|
|
1065
|
+
stroke: STATUS_BAR_COLORS.return,
|
|
1066
|
+
strokeWidth: 2,
|
|
1067
|
+
dot: false
|
|
1068
|
+
}
|
|
1069
|
+
)
|
|
1070
|
+
]
|
|
1071
|
+
}
|
|
1072
|
+
) }) }) })
|
|
283
1073
|
}
|
|
284
1074
|
),
|
|
285
1075
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
286
|
-
|
|
1076
|
+
AnalyticsSection,
|
|
287
1077
|
{
|
|
288
|
-
|
|
289
|
-
|
|
1078
|
+
variant: "atlas",
|
|
1079
|
+
className: "xl:col-span-5",
|
|
1080
|
+
title: "Recent orders (7 days)",
|
|
1081
|
+
description: "Daily order count vs 7-day average — time series.",
|
|
1082
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
1083
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1084
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle/50 px-2 py-2", children: [
|
|
1085
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Today" }),
|
|
1086
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-base font-semibold tracking-tight", children: (todayPoint == null ? void 0 : todayPoint.orders_count.toLocaleString()) ?? "0" })
|
|
1087
|
+
] }),
|
|
1088
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle/50 px-2 py-2", children: [
|
|
1089
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "7-day avg" }),
|
|
1090
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-base font-semibold tracking-tight", children: todayOrdersAverage.toFixed(1) })
|
|
1091
|
+
] })
|
|
1092
|
+
] }),
|
|
1093
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[160px] sm:h-[180px]", children: todayContextLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) : todayContextError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-xs", children: todayContextError }) }) : todayChartData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "No recent activity." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1094
|
+
recharts.ComposedChart,
|
|
1095
|
+
{
|
|
1096
|
+
data: todayChartData,
|
|
1097
|
+
margin: { top: 4, right: 4, left: -4, bottom: 0 },
|
|
1098
|
+
children: [
|
|
1099
|
+
/* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "todayOrdersFill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
|
|
1100
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#3b82f6", stopOpacity: 0.2 }),
|
|
1101
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#3b82f6", stopOpacity: 0 })
|
|
1102
|
+
] }) }),
|
|
1103
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1104
|
+
recharts.CartesianGrid,
|
|
1105
|
+
{
|
|
1106
|
+
stroke: "rgba(148,163,184,0.14)",
|
|
1107
|
+
strokeDasharray: "3 3",
|
|
1108
|
+
vertical: false
|
|
1109
|
+
}
|
|
1110
|
+
),
|
|
1111
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1112
|
+
recharts.XAxis,
|
|
1113
|
+
{
|
|
1114
|
+
dataKey: "date",
|
|
1115
|
+
tick: { fontSize: 10, fill: "var(--medusa-color-ui-fg-muted)" },
|
|
1116
|
+
tickLine: false,
|
|
1117
|
+
axisLine: false,
|
|
1118
|
+
tickFormatter: formatChartLabel
|
|
1119
|
+
}
|
|
1120
|
+
),
|
|
1121
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1122
|
+
recharts.YAxis,
|
|
1123
|
+
{
|
|
1124
|
+
width: 28,
|
|
1125
|
+
tick: { fontSize: 10, fill: "var(--medusa-color-ui-fg-muted)" },
|
|
1126
|
+
tickLine: false,
|
|
1127
|
+
axisLine: false,
|
|
1128
|
+
allowDecimals: false,
|
|
1129
|
+
tickFormatter: (value) => Math.floor(Number(value) || 0).toLocaleString()
|
|
1130
|
+
}
|
|
1131
|
+
),
|
|
1132
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(OrdersTodayTooltip, {}), cursor: { stroke: "rgba(148,163,184,0.35)", strokeWidth: 1 } }),
|
|
1133
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1134
|
+
recharts.ReferenceLine,
|
|
1135
|
+
{
|
|
1136
|
+
y: todayOrdersAverage,
|
|
1137
|
+
stroke: "rgba(148,163,184,0.65)",
|
|
1138
|
+
strokeDasharray: "4 4"
|
|
1139
|
+
}
|
|
1140
|
+
),
|
|
1141
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1142
|
+
recharts.Area,
|
|
1143
|
+
{
|
|
1144
|
+
type: "natural",
|
|
1145
|
+
dataKey: "orders_count",
|
|
1146
|
+
stroke: "none",
|
|
1147
|
+
fill: "url(#todayOrdersFill)",
|
|
1148
|
+
isAnimationActive: false
|
|
1149
|
+
}
|
|
1150
|
+
),
|
|
1151
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1152
|
+
recharts.Line,
|
|
1153
|
+
{
|
|
1154
|
+
type: "natural",
|
|
1155
|
+
dataKey: "orders_count",
|
|
1156
|
+
name: "Orders",
|
|
1157
|
+
stroke: "#3b82f6",
|
|
1158
|
+
strokeWidth: 2,
|
|
1159
|
+
dot: (props) => {
|
|
1160
|
+
const { cx, cy, payload } = props;
|
|
1161
|
+
if (payload && typeof payload === "object" && "isToday" in payload && payload.isToday) {
|
|
1162
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1163
|
+
"circle",
|
|
1164
|
+
{
|
|
1165
|
+
cx,
|
|
1166
|
+
cy,
|
|
1167
|
+
r: 4,
|
|
1168
|
+
fill: STATUS_BAR_COLORS.today,
|
|
1169
|
+
stroke: "var(--medusa-color-ui-bg-base)",
|
|
1170
|
+
strokeWidth: 1
|
|
1171
|
+
}
|
|
1172
|
+
);
|
|
1173
|
+
}
|
|
1174
|
+
return /* @__PURE__ */ jsxRuntime.jsx("circle", { cx, cy, r: 0, fill: "transparent" });
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
)
|
|
1178
|
+
]
|
|
1179
|
+
}
|
|
1180
|
+
) }) }),
|
|
1181
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle/40 px-2.5 py-2 shadow-sm", children: [
|
|
1182
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Note" }),
|
|
1183
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-[11px] leading-snug", children: todayDelta === null ? "Compares the latest day to the 7-day mean." : todayDelta >= 0 ? `Latest day is ${todayDelta.toFixed(1)} above the 7-day average.` : `Latest day is ${Math.abs(todayDelta).toFixed(1)} below the 7-day average.` })
|
|
1184
|
+
] })
|
|
1185
|
+
] }) })
|
|
290
1186
|
}
|
|
291
|
-
)
|
|
1187
|
+
)
|
|
1188
|
+
] }),
|
|
1189
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 grid grid-cols-1 items-start gap-3 md:grid-cols-3", children: [
|
|
292
1190
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
293
|
-
|
|
1191
|
+
AnalyticsSection,
|
|
294
1192
|
{
|
|
295
|
-
|
|
296
|
-
|
|
1193
|
+
variant: "atlas",
|
|
1194
|
+
title: "Revenue breakdown",
|
|
1195
|
+
description: "Category or source contribution — time series when connected.",
|
|
1196
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1197
|
+
EmptyAnalyticsPanel,
|
|
1198
|
+
{
|
|
1199
|
+
title: "Stacked breakdown",
|
|
1200
|
+
description: "Connect product or attribution dimensions to show stacked area over time."
|
|
1201
|
+
}
|
|
1202
|
+
)
|
|
297
1203
|
}
|
|
298
1204
|
),
|
|
299
1205
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
300
|
-
|
|
1206
|
+
AnalyticsSection,
|
|
301
1207
|
{
|
|
302
|
-
|
|
303
|
-
|
|
1208
|
+
variant: "atlas",
|
|
1209
|
+
title: "Cart abandonment",
|
|
1210
|
+
description: "Converted vs abandoned — time series when cart analytics exist.",
|
|
1211
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1212
|
+
EmptyAnalyticsPanel,
|
|
1213
|
+
{
|
|
1214
|
+
title: "Checkout leakage",
|
|
1215
|
+
description: "Donut and trend charts will use cart lifecycle data once available."
|
|
1216
|
+
}
|
|
1217
|
+
)
|
|
304
1218
|
}
|
|
305
1219
|
),
|
|
306
1220
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
307
|
-
|
|
1221
|
+
AnalyticsSection,
|
|
308
1222
|
{
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 xl:grid-cols-2", children: [
|
|
315
|
-
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsSection, { title: "Orders by status", children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[300px] items-center justify-center", children: pieData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: 280, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.PieChart, { margin: { top: 8, right: 8, bottom: 8, left: 8 }, children: [
|
|
316
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
317
|
-
recharts.Pie,
|
|
318
|
-
{
|
|
319
|
-
data: pieData,
|
|
320
|
-
cx: "50%",
|
|
321
|
-
cy: "50%",
|
|
322
|
-
innerRadius: 52,
|
|
323
|
-
outerRadius: 78,
|
|
324
|
-
paddingAngle: 2,
|
|
325
|
-
dataKey: "value",
|
|
326
|
-
nameKey: "name",
|
|
327
|
-
isAnimationActive: true,
|
|
328
|
-
children: pieData.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(recharts.Cell, { fill: entry.color }, `cell-${index}`))
|
|
329
|
-
}
|
|
330
|
-
),
|
|
331
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
332
|
-
recharts.Tooltip,
|
|
333
|
-
{
|
|
334
|
-
formatter: (value) => [value ?? 0, "Orders"]
|
|
335
|
-
}
|
|
336
|
-
),
|
|
337
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { verticalAlign: "bottom", height: 36 })
|
|
338
|
-
] }) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "No orders in this period" }) }) }) }),
|
|
339
|
-
/* @__PURE__ */ jsxRuntime.jsx(AnalyticsSection, { title: "Returns & exchanges", children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[300px] items-center justify-center", children: returnsExchangesData.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: 280, children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.PieChart, { margin: { top: 8, right: 8, bottom: 8, left: 8 }, children: [
|
|
340
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
341
|
-
recharts.Pie,
|
|
342
|
-
{
|
|
343
|
-
data: returnsExchangesData,
|
|
344
|
-
cx: "50%",
|
|
345
|
-
cy: "50%",
|
|
346
|
-
innerRadius: 52,
|
|
347
|
-
outerRadius: 78,
|
|
348
|
-
paddingAngle: 2,
|
|
349
|
-
dataKey: "value",
|
|
350
|
-
nameKey: "name",
|
|
351
|
-
isAnimationActive: true,
|
|
352
|
-
children: returnsExchangesData.map((entry, index) => /* @__PURE__ */ jsxRuntime.jsx(recharts.Cell, { fill: entry.color }, `cell-re-${index}`))
|
|
353
|
-
}
|
|
354
|
-
),
|
|
355
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
356
|
-
recharts.Tooltip,
|
|
357
|
-
{
|
|
358
|
-
formatter: (value) => [value ?? 0, "Count"]
|
|
359
|
-
}
|
|
360
|
-
),
|
|
361
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { verticalAlign: "bottom", height: 36 })
|
|
362
|
-
] }) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "No returns or exchanges in this period" }) }) }) })
|
|
363
|
-
] }),
|
|
364
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
365
|
-
AnalyticsSection,
|
|
366
|
-
{
|
|
367
|
-
title: "Orders over time",
|
|
368
|
-
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
369
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "over-time-period", className: "text-ui-fg-muted text-sm", children: "Range" }),
|
|
370
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
371
|
-
"select",
|
|
1223
|
+
variant: "atlas",
|
|
1224
|
+
title: "Sales by time",
|
|
1225
|
+
description: "Hourly or intraday patterns — bar time series when connected.",
|
|
1226
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1227
|
+
EmptyAnalyticsPanel,
|
|
372
1228
|
{
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
onChange: (e) => setOverTimePeriod(e.target.value),
|
|
376
|
-
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",
|
|
377
|
-
children: OVER_TIME_PERIODS.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.value, children: p.label }, p.value))
|
|
1229
|
+
title: "Sales by hour",
|
|
1230
|
+
description: "Order timestamps by hour-of-day will render as a compact bar time series."
|
|
378
1231
|
}
|
|
379
1232
|
)
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
className: "flex items-center justify-center h-full",
|
|
385
|
-
role: "status",
|
|
386
|
-
"aria-label": "Loading orders over time",
|
|
387
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading chart…" })
|
|
388
|
-
}
|
|
389
|
-
) : overTimeError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: overTimeError }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
390
|
-
recharts.LineChart,
|
|
391
|
-
{
|
|
392
|
-
data: dailyOrders,
|
|
393
|
-
margin: { top: 8, right: 8, left: 8, bottom: 8 },
|
|
394
|
-
children: [
|
|
395
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", className: "stroke-ui-border-base" }),
|
|
396
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
397
|
-
recharts.XAxis,
|
|
398
|
-
{
|
|
399
|
-
dataKey: "date",
|
|
400
|
-
tick: { fontSize: 12 },
|
|
401
|
-
tickFormatter: (v, index) => {
|
|
402
|
-
const row = dailyOrders[index];
|
|
403
|
-
if (row == null ? void 0 : row.label) return row.label;
|
|
404
|
-
const d = new Date(v);
|
|
405
|
-
return `${d.getUTCMonth() + 1}/${d.getUTCDate()}`;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
),
|
|
409
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
410
|
-
recharts.YAxis,
|
|
411
|
-
{
|
|
412
|
-
tick: { fontSize: 12 },
|
|
413
|
-
allowDecimals: false,
|
|
414
|
-
domain: [0, "auto"],
|
|
415
|
-
ticks: (() => {
|
|
416
|
-
const max = Math.max(
|
|
417
|
-
1,
|
|
418
|
-
...dailyOrders.map((d) => d.orders_count ?? 0)
|
|
419
|
-
);
|
|
420
|
-
const step = max <= 10 ? 1 : Math.ceil(max / 6);
|
|
421
|
-
const arr = [];
|
|
422
|
-
for (let i = 0; i <= max; i += step) arr.push(i);
|
|
423
|
-
if (arr[arr.length - 1] !== max) arr.push(max);
|
|
424
|
-
return arr;
|
|
425
|
-
})(),
|
|
426
|
-
tickFormatter: (v) => String(Math.floor(Number(v) || 0))
|
|
427
|
-
}
|
|
428
|
-
),
|
|
429
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(OrdersOverTimeTooltip, {}) }),
|
|
430
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
431
|
-
recharts.Line,
|
|
432
|
-
{
|
|
433
|
-
type: "monotone",
|
|
434
|
-
dataKey: "orders_count",
|
|
435
|
-
name: "Orders",
|
|
436
|
-
stroke: "var(--medusa-color-ui-fg-interactive)",
|
|
437
|
-
strokeWidth: 2,
|
|
438
|
-
dot: { r: 3 }
|
|
439
|
-
}
|
|
440
|
-
)
|
|
441
|
-
]
|
|
442
|
-
}
|
|
443
|
-
) }) }) })
|
|
444
|
-
}
|
|
445
|
-
)
|
|
446
|
-
] });
|
|
1233
|
+
}
|
|
1234
|
+
)
|
|
1235
|
+
] })
|
|
1236
|
+
] }) });
|
|
447
1237
|
}
|
|
448
1238
|
function CustomersOverTimeTooltip({
|
|
449
1239
|
active,
|