medusa-analytics 0.0.16 → 0.0.18

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.
@@ -5,61 +5,184 @@ 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-4", children });
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",
51
+ actionsBare = false,
52
+ appearance = "card"
15
53
  }) {
16
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "overflow-hidden rounded-2xl border border-ui-border-base bg-ui-bg-base p-0 shadow-sm", children: [
17
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 border-b border-ui-border-base px-5 py-4 lg:flex-row lg:items-end lg:justify-between", children: [
18
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
19
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "tracking-tight", children: title }),
20
- description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: description }) : null
21
- ] }),
22
- actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex self-start rounded-xl border border-ui-border-base bg-ui-bg-subtle p-2 lg:self-auto", children: actions }) : null
23
- ] }),
24
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1 bg-ui-bg-subtle" })
25
- ] });
26
- }
27
- function AnalyticsStatCard({ label, value, helper }) {
28
- return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "h-full rounded-2xl border border-ui-border-base bg-ui-bg-base p-3 shadow-sm transition-shadow", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-[76px] flex-col justify-between gap-1.5", children: [
29
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs font-medium", children: label }),
30
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "text-2xl leading-none tracking-tight", children: value }),
31
- helper ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs leading-4", children: helper }) : null
32
- ] }) });
54
+ const isPremium = variant === "premium";
55
+ const isInset = appearance === "inset";
56
+ const headerRow = /* @__PURE__ */ jsxRuntime.jsxs(
57
+ "div",
58
+ {
59
+ className: `flex flex-col gap-2 px-4 py-2.5 lg:flex-row lg:items-center lg:justify-between ${isPremium ? "border-b border-ui-border-base/80" : "border-b border-ui-border-base"}`.trim(),
60
+ children: [
61
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
62
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: isPremium ? "text-lg font-semibold tracking-tight" : "tracking-tight", children: title }),
63
+ description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: `text-ui-fg-muted ${isPremium ? "max-w-2xl text-xs leading-snug" : "text-sm"}`.trim(), children: description }) : null
64
+ ] }),
65
+ actions ? actionsBare ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-3 self-start lg:self-auto", children: actions }) : /* @__PURE__ */ jsxRuntime.jsx(
66
+ "div",
67
+ {
68
+ 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(),
69
+ children: actions
70
+ }
71
+ ) : null
72
+ ]
73
+ }
74
+ );
75
+ if (isInset) {
76
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-0 bg-transparent p-0 shadow-none", children: headerRow });
77
+ }
78
+ return /* @__PURE__ */ jsxRuntime.jsxs(
79
+ ui.Container,
80
+ {
81
+ 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(),
82
+ children: [
83
+ headerRow,
84
+ /* @__PURE__ */ jsxRuntime.jsx(
85
+ "div",
86
+ {
87
+ className: `h-px w-full ${isPremium ? "bg-gradient-to-r from-transparent via-ui-border-strong to-transparent" : "bg-ui-bg-subtle"}`.trim()
88
+ }
89
+ )
90
+ ]
91
+ }
92
+ );
33
93
  }
34
- function AnalyticsSection({
35
- title,
36
- description,
37
- actions,
38
- children,
39
- className
94
+ function AnalyticsStatCard({
95
+ label,
96
+ value,
97
+ helper,
98
+ className,
99
+ valueClassName,
100
+ variant = "default",
101
+ accent = "slate"
40
102
  }) {
103
+ const accentStyle = getAccentStyle(accent);
104
+ const isHero = variant === "hero";
105
+ const isCompact = variant === "compact";
41
106
  return /* @__PURE__ */ jsxRuntime.jsxs(
42
107
  ui.Container,
43
108
  {
44
- className: `overflow-hidden rounded-2xl border border-ui-border-base bg-ui-bg-base p-0 shadow-sm ${className ?? ""}`.trim(),
109
+ 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
110
  children: [
46
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 border-b border-ui-border-base px-5 py-4 lg:flex-row lg:items-start lg:justify-between", children: [
47
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
48
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: "tracking-tight", children: title }),
49
- description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: description }) : null
50
- ] }),
51
- actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-xl border border-ui-border-base bg-ui-bg-subtle px-3 py-2", children: actions }) : null
52
- ] }),
53
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-4", children })
111
+ isHero ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
112
+ /* @__PURE__ */ jsxRuntime.jsx(
113
+ "div",
114
+ {
115
+ className: `pointer-events-none absolute inset-x-3 top-0 h-10 rounded-b-full bg-gradient-to-r ${accentStyle.band} blur-xl`
116
+ }
117
+ ),
118
+ /* @__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" })
119
+ ] }) : null,
120
+ /* @__PURE__ */ jsxRuntime.jsxs(
121
+ "div",
122
+ {
123
+ className: `relative flex flex-col justify-between gap-1 ${isCompact ? "min-h-[64px]" : isHero ? "min-h-[72px]" : "min-h-[92px]"}`.trim(),
124
+ children: [
125
+ /* @__PURE__ */ jsxRuntime.jsx(
126
+ ui.Text,
127
+ {
128
+ className: `text-ui-fg-muted font-medium ${isHero ? "text-[10px] uppercase tracking-[0.16em]" : "text-xs"}`.trim(),
129
+ children: label
130
+ }
131
+ ),
132
+ /* @__PURE__ */ jsxRuntime.jsx(
133
+ ui.Heading,
134
+ {
135
+ level: "h2",
136
+ className: `leading-tight tracking-tight ${isHero ? "text-xl sm:text-2xl" : isCompact ? "text-lg" : "text-2xl"} ${valueClassName ?? ""}`.trim(),
137
+ children: value
138
+ }
139
+ ),
140
+ helper ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: `text-ui-fg-muted leading-snug ${isHero ? "text-[10px]" : "text-xs"}`.trim(), children: helper }) : null
141
+ ]
142
+ }
143
+ )
54
144
  ]
55
145
  }
56
146
  );
57
147
  }
58
- function AnalyticsChartSurface({ children, className }) {
148
+ function AnalyticsSection({
149
+ title,
150
+ description,
151
+ actions,
152
+ children,
153
+ className,
154
+ variant = "default",
155
+ actionsBare = false
156
+ }) {
157
+ const isAtlas = variant === "atlas";
158
+ const isPremium = variant !== "default" && !isAtlas;
159
+ 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();
160
+ const headerClass = isAtlas ? "flex flex-col gap-1 border-b border-ui-border-base bg-ui-bg-subtle/40 px-3 py-2 lg:flex-row lg:items-center lg:justify-between" : `flex flex-col gap-2 px-4 py-2.5 lg:flex-row lg:items-center lg:justify-between ${isPremium ? "border-b border-ui-border-base/80" : "border-b border-ui-border-base"}`.trim();
161
+ const titleClass = isAtlas ? "text-sm font-semibold tracking-tight text-ui-fg-base" : isPremium ? "text-base font-semibold tracking-tight" : "tracking-tight";
162
+ const descClass = isAtlas ? "max-w-2xl text-[11px] leading-snug text-ui-fg-muted" : isPremium ? "max-w-2xl text-xs leading-snug" : "text-sm";
163
+ 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();
164
+ const bodyClass = isAtlas ? "p-2" : variant === "hero" ? "px-4 py-3" : "px-4 py-3";
165
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: `${shellClass} ${className ?? ""}`.trim(), children: [
166
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: headerClass, children: [
167
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 space-y-0.5", children: [
168
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: titleClass, children: title }),
169
+ description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: `text-ui-fg-muted ${descClass}`.trim(), children: description }) : null
170
+ ] }),
171
+ actions ? actionsBare ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex shrink-0 flex-wrap items-center gap-2", children: actions }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: actionsShellClass, children: actions }) : null
172
+ ] }),
173
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: bodyClass, children })
174
+ ] });
175
+ }
176
+ function AnalyticsChartSurface({
177
+ children,
178
+ className,
179
+ variant = "default"
180
+ }) {
181
+ const surfaceClassName = variant === "atlas" ? "rounded-lg border border-ui-border-base bg-ui-bg-subtle/50 p-2 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
182
  return /* @__PURE__ */ jsxRuntime.jsx(
60
183
  "div",
61
184
  {
62
- className: `rounded-xl border border-ui-border-base bg-ui-bg-subtle p-3 shadow-inner ${className ?? ""}`.trim(),
185
+ className: `${surfaceClassName} ${className ?? ""}`.trim(),
63
186
  children
64
187
  }
65
188
  );
@@ -73,56 +196,263 @@ function AnalyticsTableSurface({ children, className }) {
73
196
  }
74
197
  );
75
198
  }
76
- function OrdersOverTimeTooltip({
77
- active,
78
- payload,
79
- label
199
+ function isDailyOrderRow(value) {
200
+ if (typeof value !== "object" || value === null) return false;
201
+ const v = value;
202
+ return typeof v.date === "string" && typeof v.orders_count === "number" && typeof v.total_revenue === "number";
203
+ }
204
+ function nonCancelledRevenue(d) {
205
+ const delivered = d.revenue_delivered ?? 0;
206
+ const open = d.revenue_open ?? 0;
207
+ if (delivered > 0 || open > 0 || (d.revenue_cancelled ?? 0) > 0) {
208
+ return delivered + open;
209
+ }
210
+ return Math.max(0, d.total_revenue - (d.revenue_cancelled ?? 0));
211
+ }
212
+ function bucketAov(d) {
213
+ const ordNc = d.orders_count - d.cancelled_count;
214
+ if (ordNc <= 0) return 0;
215
+ return nonCancelledRevenue(d) / ordNc;
216
+ }
217
+ function isOrdersTodayPoint(value) {
218
+ return isDailyOrderRow(value) && "isToday" in value && typeof value.isToday === "boolean";
219
+ }
220
+ function formatCurrency$1(value) {
221
+ return new Intl.NumberFormat("en-IN", {
222
+ style: "currency",
223
+ currency: "INR",
224
+ minimumFractionDigits: 0,
225
+ maximumFractionDigits: 0
226
+ }).format(value);
227
+ }
228
+ function formatCompactCurrency(value) {
229
+ return new Intl.NumberFormat("en-IN", {
230
+ style: "currency",
231
+ currency: "INR",
232
+ notation: "compact",
233
+ maximumFractionDigits: 1
234
+ }).format(value);
235
+ }
236
+ function formatChartLabel(value) {
237
+ if (!value) return "";
238
+ const parsed = new Date(value);
239
+ if (Number.isNaN(parsed.getTime())) return value;
240
+ return `${parsed.getUTCMonth() + 1}/${parsed.getUTCDate()}`;
241
+ }
242
+ function formatPercent(value) {
243
+ return `${value.toFixed(1)}%`;
244
+ }
245
+ function ordersCountFromPiePayload(payload) {
246
+ if (typeof payload !== "object" || payload === null) return 0;
247
+ if (!("orders_count" in payload)) return 0;
248
+ const c = payload.orders_count;
249
+ return Math.floor(Number(c) || 0);
250
+ }
251
+ function formatShortNumber(value) {
252
+ return new Intl.NumberFormat("en-IN", {
253
+ notation: "compact",
254
+ maximumFractionDigits: value < 10 ? 1 : 0
255
+ }).format(value);
256
+ }
257
+ function TooltipCard({
258
+ title,
259
+ children
80
260
  }) {
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
261
  return /* @__PURE__ */ jsxRuntime.jsxs(
87
262
  "div",
88
263
  {
89
264
  style: {
90
- backgroundColor: "rgb(250, 234, 234)",
265
+ background: "linear-gradient(180deg, rgba(255,255,255,0.98) 0%, rgba(248,250,252,0.96) 100%)",
91
266
  color: "#111827",
92
- border: "1px solid rgb(229, 231, 235)",
93
- borderRadius: "8px",
94
- padding: "12px 16px",
95
- boxShadow: "0 4px 14px rgba(102, 102, 102, 0.99)",
96
- fontSize: "14px",
97
- lineHeight: 1.5
267
+ border: "1px solid rgba(148, 163, 184, 0.18)",
268
+ borderRadius: "14px",
269
+ padding: "12px 14px",
270
+ boxShadow: "0 16px 40px rgba(15, 23, 42, 0.12)",
271
+ fontSize: "13px",
272
+ lineHeight: 1.5,
273
+ minWidth: "176px"
98
274
  },
99
275
  children: [
100
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 600, marginBottom: "6px", color: "#111827" }, children: displayLabel }),
101
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "#374151" }, children: [
102
- "Orders: ",
103
- /* @__PURE__ */ jsxRuntime.jsx("strong", { style: { color: "#1f2937" }, children: value })
104
- ] })
276
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 600, marginBottom: "6px" }, children: title }),
277
+ children
105
278
  ]
106
279
  }
107
280
  );
108
281
  }
109
- const STATUS_COLORS = {
110
- pending: "#F59E0B",
111
- cancelled: "#EF4444",
112
- delivered: "#10B981"
282
+ function OrdersTrendTooltip({
283
+ active,
284
+ payload,
285
+ label
286
+ }) {
287
+ var _a, _b, _c, _d;
288
+ if (!active || !(payload == null ? void 0 : payload.length)) return null;
289
+ const row = isDailyOrderRow((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
290
+ const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
291
+ const orders = ((_c = payload.find((entry) => entry.name === "Orders")) == null ? void 0 : _c.value) ?? 0;
292
+ const revenue = ((_d = payload.find((entry) => entry.name === "Revenue")) == null ? void 0 : _d.value) ?? 0;
293
+ return /* @__PURE__ */ jsxRuntime.jsxs(TooltipCard, { title: displayLabel, children: [
294
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
295
+ "Orders: ",
296
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(orders).toLocaleString() })
297
+ ] }),
298
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
299
+ "Revenue: ",
300
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency$1(Number(revenue)) })
301
+ ] })
302
+ ] });
303
+ }
304
+ function OrdersTodayTooltip({
305
+ active,
306
+ payload,
307
+ label
308
+ }) {
309
+ var _a, _b, _c;
310
+ if (!active || !(payload == null ? void 0 : payload.length)) return null;
311
+ const row = isOrdersTodayPoint((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
312
+ const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
313
+ const orders = ((_c = payload[0]) == null ? void 0 : _c.value) ?? 0;
314
+ return /* @__PURE__ */ jsxRuntime.jsxs(TooltipCard, { title: displayLabel, children: [
315
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
316
+ "Orders: ",
317
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(orders).toLocaleString() })
318
+ ] }),
319
+ (row == null ? void 0 : row.isToday) ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#6B7280" }, children: "Current day" }) : null
320
+ ] });
321
+ }
322
+ function OrdersSparklineTooltip({
323
+ active,
324
+ payload,
325
+ label
326
+ }) {
327
+ var _a, _b, _c;
328
+ if (!active || !(payload == null ? void 0 : payload.length)) return null;
329
+ const row = isDailyOrderRow((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
330
+ const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
331
+ const orders = Number((_c = payload[0]) == null ? void 0 : _c.value) || 0;
332
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipCard, { title: displayLabel, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
333
+ "Orders: ",
334
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(orders).toLocaleString() })
335
+ ] }) });
336
+ }
337
+ function OutcomesTrendTooltip({
338
+ active,
339
+ payload,
340
+ label
341
+ }) {
342
+ var _a, _b;
343
+ if (!active || !(payload == null ? void 0 : payload.length)) return null;
344
+ const row = isDailyOrderRow((_a = payload[0]) == null ? void 0 : _a.payload) ? (_b = payload[0]) == null ? void 0 : _b.payload : void 0;
345
+ const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
346
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipCard, { title: displayLabel, children: payload.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
347
+ entry.name,
348
+ ":",
349
+ " ",
350
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(Number(entry.value) || 0).toLocaleString() })
351
+ ] }, String(entry.name))) });
352
+ }
353
+ const KPI_ICON_BG = {
354
+ green: "bg-emerald-500/20",
355
+ blue: "bg-sky-500/20",
356
+ purple: "bg-violet-500/20",
357
+ amber: "bg-amber-500/20"
113
358
  };
114
- const ORDER_CHART_STATUSES = [
115
- { key: "pending", label: "Pending", color: STATUS_COLORS.pending },
116
- { key: "cancelled", label: "Cancelled", color: STATUS_COLORS.cancelled },
117
- { key: "delivered", label: "Delivered", color: STATUS_COLORS.delivered }
118
- ];
119
- function formatCurrency$1(value) {
120
- return new Intl.NumberFormat("en-IN", {
121
- style: "currency",
122
- currency: "INR",
123
- minimumFractionDigits: 0,
124
- maximumFractionDigits: 0
125
- }).format(value);
359
+ const KPI_ICONS = {
360
+ green: icons.CurrencyDollar,
361
+ blue: icons.ShoppingCart,
362
+ purple: icons.TruckFast,
363
+ amber: icons.Cash
364
+ };
365
+ const PIE_PALETTE = ["#6366F1", "#94A3B8", "#10B981", "#F59E0B"];
366
+ function TrafficRevenueDonut({
367
+ traffic,
368
+ compact = false
369
+ }) {
370
+ const innerRadius = compact ? 28 : 34;
371
+ const outerRadius = compact ? 44 : 54;
372
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: compact ? "h-[118px] w-full" : "h-[152px] w-full shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.PieChart, { margin: { top: 2, right: 2, left: 2, bottom: 2 }, children: [
373
+ /* @__PURE__ */ jsxRuntime.jsx(
374
+ recharts.Pie,
375
+ {
376
+ data: traffic,
377
+ dataKey: "revenue",
378
+ nameKey: "label",
379
+ cx: "50%",
380
+ cy: "48%",
381
+ innerRadius,
382
+ outerRadius,
383
+ paddingAngle: 2,
384
+ children: traffic.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(recharts.Cell, { fill: PIE_PALETTE[i % PIE_PALETTE.length] }, `traffic-slice-${i}`))
385
+ }
386
+ ),
387
+ /* @__PURE__ */ jsxRuntime.jsx(
388
+ recharts.Tooltip,
389
+ {
390
+ content: ({ active, payload }) => {
391
+ var _a, _b, _c;
392
+ return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(TooltipCard, { title: String(((_a = payload[0]) == null ? void 0 : _a.name) ?? ""), children: [
393
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
394
+ "Revenue:",
395
+ " ",
396
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency$1(Number((_b = payload[0]) == null ? void 0 : _b.value) || 0) })
397
+ ] }),
398
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
399
+ "Orders:",
400
+ " ",
401
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: ordersCountFromPiePayload((_c = payload[0]) == null ? void 0 : _c.payload).toLocaleString() })
402
+ ] })
403
+ ] }) : null;
404
+ }
405
+ }
406
+ ),
407
+ /* @__PURE__ */ jsxRuntime.jsx(
408
+ recharts.Legend,
409
+ {
410
+ wrapperStyle: { fontSize: compact ? 8 : 9, paddingTop: 0 },
411
+ verticalAlign: "bottom",
412
+ height: compact ? 18 : 22,
413
+ iconType: "circle"
414
+ }
415
+ )
416
+ ] }) }) });
417
+ }
418
+ function TrafficSourcesOverviewCard({
419
+ traffic,
420
+ loading,
421
+ error,
422
+ totalRevenue,
423
+ windowLabel
424
+ }) {
425
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-[132px] flex-col rounded-2xl border border-ui-border-base p-3 shadow-sm", children: [
426
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-xs font-medium text-ui-fg-base", children: "Traffic sources" }),
427
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "mb-1 text-[10px] leading-snug text-ui-fg-muted", children: [
428
+ "Revenue share · ",
429
+ windowLabel
430
+ ] }),
431
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1", children: loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[104px] items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) : error ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[104px] items-center justify-center px-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-center text-[10px] text-ui-fg-danger", children: error }) }) : traffic.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[104px] items-center justify-center px-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-center text-[10px] leading-snug text-ui-fg-muted", children: "No traffic breakdown for this window." }) }) : totalRevenue <= 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-[104px] items-center justify-center px-1", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-center text-[10px] leading-snug text-ui-fg-muted", children: "No recorded revenue in this window." }) }) : /* @__PURE__ */ jsxRuntime.jsx(TrafficRevenueDonut, { traffic, compact: true }) })
432
+ ] });
433
+ }
434
+ function AtlasKpiCard({
435
+ label,
436
+ value,
437
+ helper,
438
+ accent
439
+ }) {
440
+ const Icon = KPI_ICONS[accent];
441
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-[112px] min-w-0 flex-col rounded-xl border border-ui-border-base/80 bg-ui-bg-subtle/25 p-4 shadow-sm", children: [
442
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
443
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "min-w-0 flex-1 text-left text-xs font-medium leading-snug text-ui-fg-base", children: label }),
444
+ /* @__PURE__ */ jsxRuntime.jsx(
445
+ "div",
446
+ {
447
+ className: `flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${KPI_ICON_BG[accent]}`,
448
+ "aria-hidden": true,
449
+ children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4 text-ui-fg-base" })
450
+ }
451
+ )
452
+ ] }),
453
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 text-xl font-semibold tabular-nums tracking-tight text-ui-fg-base sm:text-2xl", children: value }),
454
+ helper ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-2 text-[11px] leading-snug text-ui-fg-muted", children: helper }) : null
455
+ ] });
126
456
  }
127
457
  const SUMMARY_PERIODS$1 = [
128
458
  { value: "all", label: "All time" },
@@ -136,21 +466,52 @@ const OVER_TIME_PERIODS = [
136
466
  { value: "one_month", label: "One month" },
137
467
  { value: "one_year", label: "One year" }
138
468
  ];
469
+ const STATUS_BAR_COLORS = {
470
+ delivered: "#10B981",
471
+ cancelled: "#EF4444",
472
+ exchange: "#F59E0B",
473
+ return: "#8B5CF6",
474
+ today: "var(--medusa-color-ui-fg-interactive)",
475
+ todayMuted: "#BFDBFE",
476
+ orders: "#38BDF8",
477
+ revenue: "#D946EF"
478
+ };
479
+ const CHART_AXIS_TICK = { fontSize: 10, fill: "#e5e7eb" };
480
+ const CHART_AXIS_TICK_SM = { fontSize: 9, fill: "#e5e7eb" };
481
+ const CHART_AXIS_LINE = "#94a3b8";
482
+ const SELECT_CLASS_NAME = "h-9 min-w-[132px] cursor-pointer appearance-none rounded-full border border-ui-border-base bg-ui-bg-base py-1.5 pl-3 pr-8 text-xs text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive";
483
+ function EmptyAnalyticsPanel({
484
+ title,
485
+ description
486
+ }) {
487
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-[88px] flex-col gap-1.5 rounded-lg border border-dashed border-ui-border-base px-3 py-3", children: [
488
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-xs font-semibold", children: title }),
489
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[11px] leading-snug", children: description })
490
+ ] });
491
+ }
139
492
  function OrdersDashboard() {
493
+ var _a, _b, _c, _d;
140
494
  const [data, setData] = react.useState(null);
141
495
  const [loading, setLoading] = react.useState(true);
142
496
  const [error, setError] = react.useState(null);
143
497
  const [filter, setFilter] = react.useState("all");
144
498
  const [dailyOrders, setDailyOrders] = react.useState([]);
499
+ const [previousPeriodDailyOrders, setPreviousPeriodDailyOrders] = react.useState([]);
145
500
  const [overTimePeriod, setOverTimePeriod] = react.useState("one_week");
146
501
  const [overTimeLoading, setOverTimeLoading] = react.useState(true);
147
502
  const [overTimeError, setOverTimeError] = react.useState(null);
503
+ const [ordersInsights, setOrdersInsights] = react.useState(null);
504
+ const [insightsLoading, setInsightsLoading] = react.useState(true);
505
+ const [insightsError, setInsightsError] = react.useState(null);
506
+ const [todayContext, setTodayContext] = react.useState([]);
507
+ const [todayContextLoading, setTodayContextLoading] = react.useState(true);
508
+ const [todayContextError, setTodayContextError] = react.useState(null);
148
509
  react.useEffect(() => {
149
510
  let cancelled = false;
150
511
  setLoading(true);
151
512
  setError(null);
152
513
  const url = `/admin/analytics/orders-summary?days=${filter}`;
153
- fetch(url).then((res) => {
514
+ fetch(url, { credentials: "include" }).then((res) => {
154
515
  if (!res.ok) throw new Error(res.statusText);
155
516
  return res.json();
156
517
  }).then((body) => {
@@ -169,11 +530,14 @@ function OrdersDashboard() {
169
530
  setOverTimeLoading(true);
170
531
  setOverTimeError(null);
171
532
  const url = `/admin/analytics/orders-over-time?period=${overTimePeriod}`;
172
- fetch(url).then((res) => {
533
+ fetch(url, { credentials: "include" }).then((res) => {
173
534
  if (!res.ok) throw new Error(res.statusText);
174
535
  return res.json();
175
536
  }).then((body) => {
176
- if (!cancelled) setDailyOrders(body.dailyOrders ?? []);
537
+ if (!cancelled) {
538
+ setDailyOrders(body.dailyOrders ?? []);
539
+ setPreviousPeriodDailyOrders(body.previousPeriodDailyOrders ?? []);
540
+ }
177
541
  }).catch((e) => {
178
542
  if (!cancelled) setOverTimeError(e instanceof Error ? e.message : String(e));
179
543
  }).finally(() => {
@@ -183,11 +547,240 @@ function OrdersDashboard() {
183
547
  cancelled = true;
184
548
  };
185
549
  }, [overTimePeriod]);
550
+ const insightsWindowDays = react.useMemo(() => {
551
+ if (overTimePeriod === "one_week") return 7;
552
+ if (overTimePeriod === "one_month") return 30;
553
+ return 365;
554
+ }, [overTimePeriod]);
555
+ react.useEffect(() => {
556
+ let cancelled = false;
557
+ setInsightsLoading(true);
558
+ setInsightsError(null);
559
+ fetch(`/admin/analytics/orders-insights?days=${insightsWindowDays}`, {
560
+ credentials: "include"
561
+ }).then((res) => {
562
+ if (!res.ok) throw new Error(res.statusText);
563
+ return res.json();
564
+ }).then((body) => {
565
+ if (!cancelled) setOrdersInsights(body);
566
+ }).catch((e) => {
567
+ if (!cancelled) setInsightsError(e instanceof Error ? e.message : String(e));
568
+ }).finally(() => {
569
+ if (!cancelled) setInsightsLoading(false);
570
+ });
571
+ return () => {
572
+ cancelled = true;
573
+ };
574
+ }, [insightsWindowDays]);
575
+ react.useEffect(() => {
576
+ let cancelled = false;
577
+ setTodayContextLoading(true);
578
+ setTodayContextError(null);
579
+ fetch("/admin/analytics/orders-over-time?period=one_week", {
580
+ credentials: "include"
581
+ }).then((res) => {
582
+ if (!res.ok) throw new Error(res.statusText);
583
+ return res.json();
584
+ }).then((body) => {
585
+ if (!cancelled) setTodayContext(body.dailyOrders ?? []);
586
+ }).catch((e) => {
587
+ if (!cancelled) {
588
+ setTodayContextError(e instanceof Error ? e.message : String(e));
589
+ }
590
+ }).finally(() => {
591
+ if (!cancelled) setTodayContextLoading(false);
592
+ });
593
+ return () => {
594
+ cancelled = true;
595
+ };
596
+ }, []);
597
+ const selectedSummaryPeriod = ((_a = SUMMARY_PERIODS$1.find((period) => period.value === filter)) == null ? void 0 : _a.label) ?? "All time";
598
+ const operationsBreakdown = (data == null ? void 0 : data.operationalCounts) ?? {
599
+ cancelled: (data == null ? void 0 : data.cancelledOrders) ?? 0,
600
+ delivered: (data == null ? void 0 : data.deliveredOrders) ?? 0,
601
+ exchange: (data == null ? void 0 : data.exchangeOrders) ?? 0,
602
+ return: (data == null ? void 0 : data.returnOrders) ?? 0
603
+ };
604
+ const outcomesTimeSeriesHasPoints = react.useMemo(
605
+ () => dailyOrders.some(
606
+ (d) => d.delivered_count > 0 || d.cancelled_count > 0 || d.exchange_count > 0 || d.return_count > 0
607
+ ),
608
+ [dailyOrders]
609
+ );
610
+ const totalResolvedOrders = operationsBreakdown.delivered + operationsBreakdown.cancelled;
611
+ const deliveryRate = totalResolvedOrders > 0 ? operationsBreakdown.delivered / totalResolvedOrders * 100 : 0;
612
+ const primaryStats = data ? [
613
+ {
614
+ label: "Total revenue",
615
+ value: formatCurrency$1(data.totalRevenue),
616
+ helper: "Non-cancelled orders",
617
+ accent: "green"
618
+ },
619
+ {
620
+ label: "Total orders",
621
+ value: data.totalOrders.toLocaleString(),
622
+ helper: selectedSummaryPeriod,
623
+ accent: "blue"
624
+ },
625
+ {
626
+ label: "Delivery rate",
627
+ value: formatPercent(deliveryRate),
628
+ helper: "Delivered vs cancelled",
629
+ accent: "purple"
630
+ },
631
+ {
632
+ label: "Average order value",
633
+ value: formatCurrency$1(data.aov),
634
+ helper: "Per non-cancelled order",
635
+ accent: "amber"
636
+ }
637
+ ] : [];
638
+ const secondaryStats = data ? [
639
+ { label: "Orders today", value: data.ordersToday.toLocaleString() },
640
+ { label: "Pending orders", value: data.pendingOrders.toLocaleString() },
641
+ { label: "Delivered orders", value: data.deliveredOrders.toLocaleString() },
642
+ { label: "Cancelled orders", value: data.cancelledOrders.toLocaleString() },
643
+ { label: "Exchange orders", value: data.exchangeOrders.toLocaleString() },
644
+ {
645
+ label: "Avg. fulfillment time",
646
+ value: data.avgFulfillmentTimeHours != null ? `${data.avgFulfillmentTimeHours}h` : "—",
647
+ helper: "Measured in hours"
648
+ }
649
+ ] : [];
650
+ const todayChartData = react.useMemo(
651
+ () => todayContext.map((point, index) => ({
652
+ ...point,
653
+ isToday: index === todayContext.length - 1
654
+ })),
655
+ [todayContext]
656
+ );
657
+ const todayOrdersAverage = todayChartData.length > 0 ? todayChartData.reduce((sum, point) => sum + point.orders_count, 0) / todayChartData.length : 0;
658
+ const todayPoint = todayChartData[todayChartData.length - 1] ?? null;
659
+ const todayDelta = todayPoint && todayOrdersAverage > 0 ? todayPoint.orders_count - todayOrdersAverage : null;
660
+ const trendRevenueTotal = dailyOrders.reduce(
661
+ (sum, point) => sum + point.total_revenue,
662
+ 0
663
+ );
664
+ const trendOrdersTotal = dailyOrders.reduce(
665
+ (sum, point) => sum + point.orders_count,
666
+ 0
667
+ );
668
+ const trendAverageRevenue = dailyOrders.length > 0 ? trendRevenueTotal / dailyOrders.length : 0;
669
+ const trendAverageOrders = dailyOrders.length > 0 ? trendOrdersTotal / dailyOrders.length : 0;
670
+ const peakRevenuePoint = dailyOrders.length > 0 ? dailyOrders.reduce(
671
+ (peak, point) => point.total_revenue > peak.total_revenue ? point : peak
672
+ ) : null;
673
+ const previousPeriodRevenueTotal = react.useMemo(
674
+ () => previousPeriodDailyOrders.reduce((s, d) => s + d.total_revenue, 0),
675
+ [previousPeriodDailyOrders]
676
+ );
677
+ const previousPeriodOrdersTotal = react.useMemo(
678
+ () => previousPeriodDailyOrders.reduce((s, d) => s + d.orders_count, 0),
679
+ [previousPeriodDailyOrders]
680
+ );
681
+ const revenueVsPreviousPercent = previousPeriodRevenueTotal > 0 ? (trendRevenueTotal - previousPeriodRevenueTotal) / previousPeriodRevenueTotal * 100 : null;
682
+ const ordersVsPreviousPercent = previousPeriodOrdersTotal > 0 ? (trendOrdersTotal - previousPeriodOrdersTotal) / previousPeriodOrdersTotal * 100 : null;
683
+ const trendRowsDetailed = react.useMemo(() => {
684
+ return dailyOrders.map((d, i) => {
685
+ const prev = previousPeriodDailyOrders[i];
686
+ return {
687
+ ...d,
688
+ aov: bucketAov(d),
689
+ prev_revenue: (prev == null ? void 0 : prev.total_revenue) ?? 0,
690
+ prev_orders: (prev == null ? void 0 : prev.orders_count) ?? 0,
691
+ prev_aov: prev ? bucketAov(prev) : 0
692
+ };
693
+ });
694
+ }, [dailyOrders, previousPeriodDailyOrders]);
695
+ const hourlyChartRows = react.useMemo(() => {
696
+ if (!ordersInsights) return [];
697
+ return ordersInsights.byHour.map((h) => ({
698
+ label: `${String(h.hour).padStart(2, "0")}:00`,
699
+ hour: h.hour,
700
+ orders_count: h.orders_count,
701
+ revenue: h.revenue
702
+ }));
703
+ }, [ordersInsights]);
704
+ const weekdayChartRows = react.useMemo(() => {
705
+ if (!ordersInsights) return [];
706
+ return ordersInsights.byWeekday.map((w) => ({
707
+ label: w.label,
708
+ orders_count: w.orders_count,
709
+ revenue: w.revenue
710
+ }));
711
+ }, [ordersInsights]);
712
+ const ordersVsDraftsPie = react.useMemo(() => {
713
+ var _a2;
714
+ if (!ordersInsights) return [];
715
+ const placed = ((_a2 = ordersInsights.funnel[0]) == null ? void 0 : _a2.count) ?? 0;
716
+ const draftsCount = ordersInsights.drafts.available === true ? ordersInsights.drafts.count : 0;
717
+ return [
718
+ { name: "Orders placed (window)", value: placed, key: "placed" },
719
+ {
720
+ name: ordersInsights.drafts.available === true ? "Open draft orders (snapshot)" : "Draft orders (unavailable)",
721
+ value: draftsCount,
722
+ key: "drafts"
723
+ }
724
+ ];
725
+ }, [ordersInsights]);
726
+ const funnelTimeSeriesRows = react.useMemo(() => {
727
+ return dailyOrders.map((d) => {
728
+ const placed = d.orders_count;
729
+ const notCancelled = Math.max(0, d.orders_count - d.cancelled_count);
730
+ const shipped = d.shipped_count ?? 0;
731
+ const delivered = d.delivered_count;
732
+ const pctNotCancelled = placed > 0 ? notCancelled / placed * 100 : 0;
733
+ const pctShippedOfNc = notCancelled > 0 ? shipped / notCancelled * 100 : 0;
734
+ const pctDeliveredOfShipped = shipped > 0 ? delivered / shipped * 100 : 0;
735
+ return {
736
+ date: d.date,
737
+ label: d.label ?? formatChartLabel(d.date),
738
+ placed,
739
+ not_cancelled: notCancelled,
740
+ shipped,
741
+ delivered,
742
+ pct_not_cancelled: Math.round(pctNotCancelled * 10) / 10,
743
+ pct_shipped_of_active: Math.round(pctShippedOfNc * 10) / 10,
744
+ pct_delivered_of_shipped: Math.round(pctDeliveredOfShipped * 10) / 10
745
+ };
746
+ });
747
+ }, [dailyOrders]);
748
+ const trafficTotalRevenue = react.useMemo(
749
+ () => (ordersInsights == null ? void 0 : ordersInsights.traffic.reduce((sum, t) => sum + (t.revenue || 0), 0)) ?? 0,
750
+ [ordersInsights]
751
+ );
752
+ const insightsRangeLabel = ((_b = OVER_TIME_PERIODS.find((p) => p.value === overTimePeriod)) == null ? void 0 : _b.label) ?? "One week";
753
+ const quickPulseMetrics = [
754
+ {
755
+ label: "Range revenue",
756
+ value: formatCompactCurrency(trendRevenueTotal),
757
+ helper: selectedSummaryPeriod,
758
+ accentClassName: "border-emerald-400/25 bg-emerald-500/5"
759
+ },
760
+ {
761
+ label: "Range orders",
762
+ value: formatShortNumber(trendOrdersTotal),
763
+ helper: "Current trend window",
764
+ accentClassName: "border-sky-400/25 bg-sky-500/5"
765
+ },
766
+ {
767
+ label: "Avg/day orders",
768
+ value: trendAverageOrders.toFixed(1),
769
+ helper: "Across selected chart range",
770
+ accentClassName: "border-violet-400/25 bg-violet-500/5"
771
+ },
772
+ {
773
+ label: "Peak revenue day",
774
+ value: peakRevenuePoint ? formatCompactCurrency(peakRevenuePoint.total_revenue) : "—",
775
+ helper: (peakRevenuePoint == null ? void 0 : peakRevenuePoint.label) ?? formatChartLabel(peakRevenuePoint == null ? void 0 : peakRevenuePoint.date),
776
+ accentClassName: "border-amber-400/25 bg-amber-500/5"
777
+ }
778
+ ];
186
779
  if (loading) {
187
780
  return /* @__PURE__ */ jsxRuntime.jsx(
188
781
  "div",
189
782
  {
190
- className: "flex items-center justify-center min-h-[320px]",
783
+ className: "flex min-h-[200px] items-center justify-center",
191
784
  role: "status",
192
785
  "aria-label": "Loading analytics",
193
786
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading…" })
@@ -197,253 +790,1406 @@ function OrdersDashboard() {
197
790
  if (error || !data) {
198
791
  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
792
  }
200
- const pieData = ORDER_CHART_STATUSES.map(({ key, label, color }) => {
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: [
793
+ return /* @__PURE__ */ jsxRuntime.jsx(AnalyticsDashboardShell, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "overflow-hidden rounded-2xl border border-ui-border-base/80 bg-ui-bg-base shadow-sm", children: [
209
794
  /* @__PURE__ */ jsxRuntime.jsx(
210
795
  AnalyticsDashboardHeader,
211
796
  {
797
+ variant: "premium",
798
+ appearance: "inset",
799
+ actionsBare: true,
212
800
  title: "Orders",
213
- description: "Monitor revenue, operational health, and status distribution without leaving the analytics workspace.",
801
+ description: "Revenue, volume, and outcomes in a compact overview sized for a single viewport.",
214
802
  actions: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
215
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
216
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "analytics-period", className: "text-ui-fg-muted text-sm", children: "Period" }),
803
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
804
+ /* @__PURE__ */ jsxRuntime.jsx(
805
+ ui.Label,
806
+ {
807
+ htmlFor: "analytics-period",
808
+ className: "shrink-0 text-ui-fg-muted text-xs font-medium",
809
+ children: "Period"
810
+ }
811
+ ),
217
812
  /* @__PURE__ */ jsxRuntime.jsx(
218
813
  "select",
219
814
  {
220
815
  id: "analytics-period",
221
816
  value: filter,
222
817
  onChange: (e) => setFilter(e.target.value),
223
- 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",
818
+ className: SELECT_CLASS_NAME,
224
819
  children: SUMMARY_PERIODS$1.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.value, children: p.label }, p.value))
225
820
  }
226
821
  )
227
822
  ] }),
228
- filter !== "all" && /* @__PURE__ */ jsxRuntime.jsx(
823
+ filter !== "all" ? /* @__PURE__ */ jsxRuntime.jsx(
229
824
  "button",
230
825
  {
231
- type: "button",
232
- onClick: () => setFilter("all"),
233
- className: "text-xs text-ui-fg-muted hover:text-ui-fg-base transition-colors",
234
- "aria-label": "Show all orders (clear filter)",
235
- children: "Clear filter"
826
+ type: "button",
827
+ onClick: () => setFilter("all"),
828
+ className: "text-xs text-ui-fg-muted transition-colors hover:text-ui-fg-base",
829
+ "aria-label": "Show all orders (clear filter)",
830
+ children: "Clear period"
831
+ }
832
+ ) : null
833
+ ] })
834
+ }
835
+ ),
836
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 p-3 pt-0 md:p-4 md:pt-0", children: [
837
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
838
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Order overview" }) }),
839
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
840
+ /* @__PURE__ */ jsxRuntime.jsx(
841
+ TrafficSourcesOverviewCard,
842
+ {
843
+ traffic: (ordersInsights == null ? void 0 : ordersInsights.traffic) ?? [],
844
+ loading: insightsLoading,
845
+ error: insightsError,
846
+ totalRevenue: trafficTotalRevenue,
847
+ windowLabel: insightsRangeLabel
848
+ }
849
+ ),
850
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-3 lg:grid-cols-4", children: primaryStats.map((stat) => /* @__PURE__ */ jsxRuntime.jsx(
851
+ AtlasKpiCard,
852
+ {
853
+ label: stat.label,
854
+ value: stat.value,
855
+ helper: stat.helper,
856
+ accent: stat.accent
857
+ },
858
+ stat.label
859
+ )) })
860
+ ] })
861
+ ] }),
862
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 grid grid-cols-12 gap-2 xl:items-start", children: [
863
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "col-span-12 xl:col-span-6", children: /* @__PURE__ */ jsxRuntime.jsx(
864
+ AnalyticsSection,
865
+ {
866
+ variant: "atlas",
867
+ actionsBare: true,
868
+ title: "Revenue & orders",
869
+ description: "Time series for the selected range. Window totals below match this chart.",
870
+ actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
871
+ /* @__PURE__ */ jsxRuntime.jsx(
872
+ ui.Label,
873
+ {
874
+ htmlFor: "over-time-period",
875
+ className: "shrink-0 text-ui-fg-muted text-xs font-medium",
876
+ children: "Range"
877
+ }
878
+ ),
879
+ /* @__PURE__ */ jsxRuntime.jsx(
880
+ "select",
881
+ {
882
+ id: "over-time-period",
883
+ value: overTimePeriod,
884
+ onChange: (e) => setOverTimePeriod(e.target.value),
885
+ className: SELECT_CLASS_NAME,
886
+ children: OVER_TIME_PERIODS.map((p) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: p.value, children: p.label }, p.value))
887
+ }
888
+ )
889
+ ] }),
890
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
891
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-1.5 sm:grid-cols-3", children: [
892
+ /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
893
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window revenue" }),
894
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: formatCompactCurrency(trendRevenueTotal) }),
895
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: [
896
+ "Avg/day ",
897
+ formatCompactCurrency(trendAverageRevenue)
898
+ ] })
899
+ ] }) }),
900
+ /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
901
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Window orders" }),
902
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: trendOrdersTotal.toLocaleString() }),
903
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px]", children: [
904
+ "Avg/day ",
905
+ trendAverageOrders.toFixed(1)
906
+ ] })
907
+ ] }) }),
908
+ /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0.5", children: [
909
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "vs previous period" }),
910
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-lg font-semibold tracking-tight", children: revenueVsPreviousPercent === null ? "—" : `${revenueVsPreviousPercent >= 0 ? "+" : ""}${revenueVsPreviousPercent.toFixed(1)}% rev` }),
911
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { className: "text-ui-fg-muted text-[10px] leading-snug", children: [
912
+ "Orders",
913
+ " ",
914
+ ordersVsPreviousPercent === null ? "—" : `${ordersVsPreviousPercent >= 0 ? "+" : ""}${ordersVsPreviousPercent.toFixed(1)}%`
915
+ ] })
916
+ ] }) })
917
+ ] }),
918
+ !overTimeLoading && !overTimeError && dailyOrders.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
919
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-1.5 lg:grid-cols-3", children: [
920
+ /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
921
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Revenue over time" }),
922
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[118px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
923
+ recharts.LineChart,
924
+ {
925
+ data: trendRowsDetailed,
926
+ margin: { top: 4, right: 4, left: 0, bottom: 0 },
927
+ children: [
928
+ /* @__PURE__ */ jsxRuntime.jsx(
929
+ recharts.CartesianGrid,
930
+ {
931
+ strokeDasharray: "3 3",
932
+ vertical: false,
933
+ stroke: "rgba(148,163,184,0.12)"
934
+ }
935
+ ),
936
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "date", hide: true }),
937
+ /* @__PURE__ */ jsxRuntime.jsx(
938
+ recharts.YAxis,
939
+ {
940
+ width: 36,
941
+ tick: CHART_AXIS_TICK_SM,
942
+ tickFormatter: (v) => formatCompactCurrency(Number(v))
943
+ }
944
+ ),
945
+ /* @__PURE__ */ jsxRuntime.jsx(
946
+ recharts.Tooltip,
947
+ {
948
+ content: ({ active, payload, label }) => {
949
+ var _a2, _b2, _c2, _d2;
950
+ return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(
951
+ TooltipCard,
952
+ {
953
+ title: String(
954
+ isDailyOrderRow((_a2 = payload[0]) == null ? void 0 : _a2.payload) ? ((_b2 = payload[0]) == null ? void 0 : _b2.payload.label) ?? formatChartLabel(String(label)) : label
955
+ ),
956
+ children: [
957
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
958
+ "This:",
959
+ " ",
960
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCompactCurrency(
961
+ Number(
962
+ (_c2 = payload.find((p) => p.dataKey === "total_revenue")) == null ? void 0 : _c2.value
963
+ ) || 0
964
+ ) })
965
+ ] }),
966
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
967
+ "Previous:",
968
+ " ",
969
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCompactCurrency(
970
+ Number(
971
+ (_d2 = payload.find((p) => p.dataKey === "prev_revenue")) == null ? void 0 : _d2.value
972
+ ) || 0
973
+ ) })
974
+ ] })
975
+ ]
976
+ }
977
+ ) : null;
978
+ }
979
+ }
980
+ ),
981
+ /* @__PURE__ */ jsxRuntime.jsx(
982
+ recharts.Line,
983
+ {
984
+ type: "natural",
985
+ dataKey: "total_revenue",
986
+ name: "This period",
987
+ stroke: "#D946EF",
988
+ strokeWidth: 2,
989
+ dot: false
990
+ }
991
+ ),
992
+ /* @__PURE__ */ jsxRuntime.jsx(
993
+ recharts.Line,
994
+ {
995
+ type: "natural",
996
+ dataKey: "prev_revenue",
997
+ name: "Previous",
998
+ stroke: "#94a3b8",
999
+ strokeWidth: 1.5,
1000
+ strokeDasharray: "5 4",
1001
+ dot: false
1002
+ }
1003
+ )
1004
+ ]
1005
+ }
1006
+ ) }) })
1007
+ ] }),
1008
+ /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
1009
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Orders count" }),
1010
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[118px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
1011
+ recharts.LineChart,
1012
+ {
1013
+ data: trendRowsDetailed,
1014
+ margin: { top: 4, right: 4, left: 0, bottom: 0 },
1015
+ children: [
1016
+ /* @__PURE__ */ jsxRuntime.jsx(
1017
+ recharts.CartesianGrid,
1018
+ {
1019
+ strokeDasharray: "3 3",
1020
+ vertical: false,
1021
+ stroke: "rgba(148,163,184,0.12)"
1022
+ }
1023
+ ),
1024
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "date", hide: true }),
1025
+ /* @__PURE__ */ jsxRuntime.jsx(
1026
+ recharts.YAxis,
1027
+ {
1028
+ width: 28,
1029
+ tick: CHART_AXIS_TICK_SM,
1030
+ allowDecimals: false
1031
+ }
1032
+ ),
1033
+ /* @__PURE__ */ jsxRuntime.jsx(
1034
+ recharts.Tooltip,
1035
+ {
1036
+ content: ({ active, payload, label }) => {
1037
+ var _a2, _b2;
1038
+ return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(TooltipCard, { title: String(label), children: [
1039
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1040
+ "This:",
1041
+ " ",
1042
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(
1043
+ Number(
1044
+ (_a2 = payload.find((p) => p.dataKey === "orders_count")) == null ? void 0 : _a2.value
1045
+ ) || 0
1046
+ ).toLocaleString() })
1047
+ ] }),
1048
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1049
+ "Previous:",
1050
+ " ",
1051
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(
1052
+ Number(
1053
+ (_b2 = payload.find((p) => p.dataKey === "prev_orders")) == null ? void 0 : _b2.value
1054
+ ) || 0
1055
+ ).toLocaleString() })
1056
+ ] })
1057
+ ] }) : null;
1058
+ }
1059
+ }
1060
+ ),
1061
+ /* @__PURE__ */ jsxRuntime.jsx(
1062
+ recharts.Line,
1063
+ {
1064
+ type: "natural",
1065
+ dataKey: "orders_count",
1066
+ name: "This period",
1067
+ stroke: "#38BDF8",
1068
+ strokeWidth: 2,
1069
+ dot: false
1070
+ }
1071
+ ),
1072
+ /* @__PURE__ */ jsxRuntime.jsx(
1073
+ recharts.Line,
1074
+ {
1075
+ type: "natural",
1076
+ dataKey: "prev_orders",
1077
+ name: "Previous",
1078
+ stroke: "#94a3b8",
1079
+ strokeWidth: 1.5,
1080
+ strokeDasharray: "5 4",
1081
+ dot: false
1082
+ }
1083
+ )
1084
+ ]
1085
+ }
1086
+ ) }) })
1087
+ ] }),
1088
+ /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
1089
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "AOV (non-cancelled)" }),
1090
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[118px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
1091
+ recharts.LineChart,
1092
+ {
1093
+ data: trendRowsDetailed,
1094
+ margin: { top: 4, right: 4, left: 0, bottom: 0 },
1095
+ children: [
1096
+ /* @__PURE__ */ jsxRuntime.jsx(
1097
+ recharts.CartesianGrid,
1098
+ {
1099
+ strokeDasharray: "3 3",
1100
+ vertical: false,
1101
+ stroke: "rgba(148,163,184,0.12)"
1102
+ }
1103
+ ),
1104
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "date", hide: true }),
1105
+ /* @__PURE__ */ jsxRuntime.jsx(
1106
+ recharts.YAxis,
1107
+ {
1108
+ width: 36,
1109
+ tick: CHART_AXIS_TICK_SM,
1110
+ tickFormatter: (v) => formatCompactCurrency(Number(v))
1111
+ }
1112
+ ),
1113
+ /* @__PURE__ */ jsxRuntime.jsx(
1114
+ recharts.Tooltip,
1115
+ {
1116
+ content: ({ active, payload, label }) => {
1117
+ var _a2, _b2;
1118
+ return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(TooltipCard, { title: String(label), children: [
1119
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1120
+ "This:",
1121
+ " ",
1122
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency$1(
1123
+ Number(
1124
+ (_a2 = payload.find((p) => p.dataKey === "aov")) == null ? void 0 : _a2.value
1125
+ ) || 0
1126
+ ) })
1127
+ ] }),
1128
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1129
+ "Previous:",
1130
+ " ",
1131
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency$1(
1132
+ Number(
1133
+ (_b2 = payload.find((p) => p.dataKey === "prev_aov")) == null ? void 0 : _b2.value
1134
+ ) || 0
1135
+ ) })
1136
+ ] })
1137
+ ] }) : null;
1138
+ }
1139
+ }
1140
+ ),
1141
+ /* @__PURE__ */ jsxRuntime.jsx(
1142
+ recharts.Line,
1143
+ {
1144
+ type: "natural",
1145
+ dataKey: "aov",
1146
+ name: "This period",
1147
+ stroke: "#D97706",
1148
+ strokeWidth: 2,
1149
+ dot: false
1150
+ }
1151
+ ),
1152
+ /* @__PURE__ */ jsxRuntime.jsx(
1153
+ recharts.Line,
1154
+ {
1155
+ type: "natural",
1156
+ dataKey: "prev_aov",
1157
+ name: "Previous",
1158
+ stroke: "#94a3b8",
1159
+ strokeWidth: 1.5,
1160
+ strokeDasharray: "5 4",
1161
+ dot: false
1162
+ }
1163
+ )
1164
+ ]
1165
+ }
1166
+ ) }) })
1167
+ ] })
1168
+ ] }),
1169
+ /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", className: "mt-1.5", children: [
1170
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Revenue breakdown (stacked — by order status)" }),
1171
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(168px,26vh)] min-h-[140px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
1172
+ recharts.ComposedChart,
1173
+ {
1174
+ data: dailyOrders,
1175
+ margin: { top: 4, right: 8, left: -4, bottom: 0 },
1176
+ children: [
1177
+ /* @__PURE__ */ jsxRuntime.jsx(
1178
+ recharts.CartesianGrid,
1179
+ {
1180
+ stroke: "rgba(148,163,184,0.14)",
1181
+ strokeDasharray: "3 3",
1182
+ vertical: false
1183
+ }
1184
+ ),
1185
+ /* @__PURE__ */ jsxRuntime.jsx(
1186
+ recharts.XAxis,
1187
+ {
1188
+ dataKey: "date",
1189
+ tick: CHART_AXIS_TICK,
1190
+ tickLine: false,
1191
+ axisLine: false,
1192
+ tickFormatter: (value, index) => {
1193
+ const row = dailyOrders[index];
1194
+ return (row == null ? void 0 : row.label) ?? formatChartLabel(value);
1195
+ }
1196
+ }
1197
+ ),
1198
+ /* @__PURE__ */ jsxRuntime.jsx(
1199
+ recharts.YAxis,
1200
+ {
1201
+ tick: CHART_AXIS_TICK,
1202
+ tickFormatter: (v) => formatCompactCurrency(Number(v))
1203
+ }
1204
+ ),
1205
+ /* @__PURE__ */ jsxRuntime.jsx(
1206
+ recharts.Tooltip,
1207
+ {
1208
+ content: ({ active, payload, label }) => active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(TooltipCard, { title: String(label), children: payload.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1209
+ p.name,
1210
+ ":",
1211
+ " ",
1212
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency$1(Number(p.value) || 0) })
1213
+ ] }, String(p.name))) }) : null
1214
+ }
1215
+ ),
1216
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { wrapperStyle: { fontSize: 10 }, iconType: "circle" }),
1217
+ /* @__PURE__ */ jsxRuntime.jsx(
1218
+ recharts.Area,
1219
+ {
1220
+ type: "natural",
1221
+ dataKey: "revenue_delivered",
1222
+ name: "Delivered",
1223
+ stackId: "r",
1224
+ fill: "#10B981",
1225
+ stroke: "#059669",
1226
+ fillOpacity: 0.85
1227
+ }
1228
+ ),
1229
+ /* @__PURE__ */ jsxRuntime.jsx(
1230
+ recharts.Area,
1231
+ {
1232
+ type: "natural",
1233
+ dataKey: "revenue_open",
1234
+ name: "Open / pending",
1235
+ stackId: "r",
1236
+ fill: "#3B82F6",
1237
+ stroke: "#2563eb",
1238
+ fillOpacity: 0.85
1239
+ }
1240
+ ),
1241
+ /* @__PURE__ */ jsxRuntime.jsx(
1242
+ recharts.Area,
1243
+ {
1244
+ type: "natural",
1245
+ dataKey: "revenue_cancelled",
1246
+ name: "Cancelled",
1247
+ stackId: "r",
1248
+ fill: "#F87171",
1249
+ stroke: "#EF4444",
1250
+ fillOpacity: 0.75
1251
+ }
1252
+ )
1253
+ ]
1254
+ }
1255
+ ) }) })
1256
+ ] })
1257
+ ] }) : null,
1258
+ /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", className: "relative overflow-hidden", children: [
1259
+ /* @__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" }),
1260
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative space-y-1", children: [
1261
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-between gap-1.5", children: [
1262
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-0", children: [
1263
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.16em]", children: "Trend overview" }),
1264
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-sm font-semibold", children: ((_c = OVER_TIME_PERIODS.find((period) => period.value === overTimePeriod)) == null ? void 0 : _c.label) ?? "One week" })
1265
+ ] }),
1266
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap justify-end gap-2 text-[10px] text-ui-fg-muted", children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
1268
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-sky-400" }),
1269
+ "Orders"
1270
+ ] }),
1271
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
1272
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-fuchsia-400" }),
1273
+ "Revenue"
1274
+ ] })
1275
+ ] })
1276
+ ] }),
1277
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(200px,30vh)] min-h-[160px] sm:h-[min(216px,32vh)]", children: overTimeLoading ? /* @__PURE__ */ jsxRuntime.jsx(
1278
+ "div",
1279
+ {
1280
+ className: "flex h-full items-center justify-center",
1281
+ role: "status",
1282
+ "aria-label": "Loading orders over time",
1283
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading chart…" })
1284
+ }
1285
+ ) : 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(
1286
+ recharts.ComposedChart,
1287
+ {
1288
+ data: dailyOrders,
1289
+ margin: { top: 4, right: 6, left: -6, bottom: 0 },
1290
+ children: [
1291
+ /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
1292
+ /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "ordersGradient", x1: "0", y1: "0", x2: "1", y2: "0", children: [
1293
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#67E8F9" }),
1294
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#3B82F6" })
1295
+ ] }),
1296
+ /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "revenueGradient", x1: "0", y1: "0", x2: "1", y2: "0", children: [
1297
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#F472B6" }),
1298
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#C084FC" })
1299
+ ] }),
1300
+ /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "ordersAreaFill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
1301
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#38BDF8", stopOpacity: 0.22 }),
1302
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#38BDF8", stopOpacity: 0 })
1303
+ ] }),
1304
+ /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "revenueAreaFill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
1305
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#E879F9", stopOpacity: 0.2 }),
1306
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#E879F9", stopOpacity: 0 })
1307
+ ] })
1308
+ ] }),
1309
+ /* @__PURE__ */ jsxRuntime.jsx(
1310
+ recharts.CartesianGrid,
1311
+ {
1312
+ stroke: "rgba(148,163,184,0.14)",
1313
+ strokeDasharray: "3 3",
1314
+ vertical: false
1315
+ }
1316
+ ),
1317
+ /* @__PURE__ */ jsxRuntime.jsx(
1318
+ recharts.XAxis,
1319
+ {
1320
+ dataKey: "date",
1321
+ tick: CHART_AXIS_TICK,
1322
+ tickLine: false,
1323
+ axisLine: false,
1324
+ tickFormatter: (value, index) => {
1325
+ const row = dailyOrders[index];
1326
+ return (row == null ? void 0 : row.label) ?? formatChartLabel(value);
1327
+ }
1328
+ }
1329
+ ),
1330
+ /* @__PURE__ */ jsxRuntime.jsx(
1331
+ recharts.YAxis,
1332
+ {
1333
+ yAxisId: "orders",
1334
+ width: 32,
1335
+ tick: CHART_AXIS_TICK,
1336
+ tickLine: false,
1337
+ axisLine: false,
1338
+ allowDecimals: false,
1339
+ tickFormatter: (value) => Math.floor(Number(value) || 0).toLocaleString()
1340
+ }
1341
+ ),
1342
+ /* @__PURE__ */ jsxRuntime.jsx(
1343
+ recharts.YAxis,
1344
+ {
1345
+ yAxisId: "revenue",
1346
+ orientation: "right",
1347
+ width: 40,
1348
+ tick: CHART_AXIS_TICK,
1349
+ tickLine: false,
1350
+ axisLine: false,
1351
+ tickFormatter: (value) => formatCompactCurrency(Number(value) || 0)
1352
+ }
1353
+ ),
1354
+ /* @__PURE__ */ jsxRuntime.jsx(
1355
+ recharts.Tooltip,
1356
+ {
1357
+ content: /* @__PURE__ */ jsxRuntime.jsx(OrdersTrendTooltip, {}),
1358
+ cursor: {
1359
+ stroke: "rgba(248, 250, 252, 0.35)",
1360
+ strokeWidth: 1
1361
+ }
1362
+ }
1363
+ ),
1364
+ /* @__PURE__ */ jsxRuntime.jsx(
1365
+ recharts.Area,
1366
+ {
1367
+ yAxisId: "orders",
1368
+ type: "natural",
1369
+ dataKey: "orders_count",
1370
+ stroke: "none",
1371
+ fill: "url(#ordersAreaFill)",
1372
+ isAnimationActive: false,
1373
+ legendType: "none"
1374
+ }
1375
+ ),
1376
+ /* @__PURE__ */ jsxRuntime.jsx(
1377
+ recharts.Area,
1378
+ {
1379
+ yAxisId: "revenue",
1380
+ type: "natural",
1381
+ dataKey: "total_revenue",
1382
+ stroke: "none",
1383
+ fill: "url(#revenueAreaFill)",
1384
+ isAnimationActive: false,
1385
+ legendType: "none"
1386
+ }
1387
+ ),
1388
+ /* @__PURE__ */ jsxRuntime.jsx(
1389
+ recharts.Line,
1390
+ {
1391
+ yAxisId: "orders",
1392
+ type: "natural",
1393
+ dataKey: "orders_count",
1394
+ name: "Orders",
1395
+ stroke: "url(#ordersGradient)",
1396
+ strokeWidth: 2,
1397
+ dot: false,
1398
+ activeDot: { r: 4, fill: STATUS_BAR_COLORS.orders }
1399
+ }
1400
+ ),
1401
+ /* @__PURE__ */ jsxRuntime.jsx(
1402
+ recharts.Line,
1403
+ {
1404
+ yAxisId: "revenue",
1405
+ type: "natural",
1406
+ dataKey: "total_revenue",
1407
+ name: "Revenue",
1408
+ stroke: "url(#revenueGradient)",
1409
+ strokeWidth: 2,
1410
+ dot: false,
1411
+ activeDot: { r: 4, fill: STATUS_BAR_COLORS.revenue }
1412
+ }
1413
+ )
1414
+ ]
1415
+ }
1416
+ ) }) })
1417
+ ] })
1418
+ ] })
1419
+ ] })
1420
+ }
1421
+ ) }),
1422
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "col-span-12 flex flex-col gap-2 xl:col-span-6", children: [
1423
+ /* @__PURE__ */ jsxRuntime.jsxs(
1424
+ AnalyticsSection,
1425
+ {
1426
+ variant: "atlas",
1427
+ title: "Pulse & range",
1428
+ description: "Window totals and an orders time series for the selected range.",
1429
+ children: [
1430
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 gap-2", children: quickPulseMetrics.map((metric) => /* @__PURE__ */ jsxRuntime.jsxs(
1431
+ "div",
1432
+ {
1433
+ className: `rounded-lg border border-ui-border-base bg-ui-bg-base px-2 py-2 shadow-sm ${metric.accentClassName}`.trim(),
1434
+ children: [
1435
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: metric.label }),
1436
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base mt-0.5 text-sm font-semibold tracking-tight", children: metric.value }),
1437
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted mt-0.5 text-[9px] leading-snug", children: metric.helper })
1438
+ ]
1439
+ },
1440
+ metric.label
1441
+ )) }),
1442
+ !overTimeLoading && !overTimeError && dailyOrders.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", className: "mt-1.5", children: [
1443
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Orders (time series)" }),
1444
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[76px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
1445
+ recharts.LineChart,
1446
+ {
1447
+ data: dailyOrders,
1448
+ margin: { top: 2, right: 4, left: -18, bottom: 2 },
1449
+ children: [
1450
+ /* @__PURE__ */ jsxRuntime.jsx(
1451
+ recharts.CartesianGrid,
1452
+ {
1453
+ strokeDasharray: "3 3",
1454
+ vertical: false,
1455
+ stroke: "rgba(148,163,184,0.12)"
1456
+ }
1457
+ ),
1458
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.XAxis, { dataKey: "date", hide: true }),
1459
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.YAxis, { hide: true, domain: ["auto", "auto"] }),
1460
+ /* @__PURE__ */ jsxRuntime.jsx(
1461
+ recharts.Tooltip,
1462
+ {
1463
+ content: /* @__PURE__ */ jsxRuntime.jsx(OrdersSparklineTooltip, {}),
1464
+ cursor: { stroke: "rgba(148,163,184,0.35)", strokeWidth: 1 }
1465
+ }
1466
+ ),
1467
+ /* @__PURE__ */ jsxRuntime.jsx(
1468
+ recharts.Line,
1469
+ {
1470
+ type: "natural",
1471
+ dataKey: "orders_count",
1472
+ stroke: "#3b82f6",
1473
+ strokeWidth: 2,
1474
+ dot: { r: 2, fill: "#3b82f6" },
1475
+ activeDot: { r: 3 }
1476
+ }
1477
+ )
1478
+ ]
1479
+ }
1480
+ ) }) })
1481
+ ] }) : null
1482
+ ]
1483
+ }
1484
+ ),
1485
+ /* @__PURE__ */ jsxRuntime.jsx(
1486
+ AnalyticsSection,
1487
+ {
1488
+ variant: "atlas",
1489
+ title: "Order → fulfillment funnel",
1490
+ description: `Stages by order created date (UTC), same range as Revenue & orders (${((_d = OVER_TIME_PERIODS.find((p) => p.value === overTimePeriod)) == null ? void 0 : _d.label) ?? "period"}). Not storefront visitors.`,
1491
+ children: overTimeLoading ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[120px] items-center justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) }) : overTimeError ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[120px] items-center justify-center px-2 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: overTimeError }) }) }) : funnelTimeSeriesRows.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1492
+ /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
1493
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Stage counts (time series)" }),
1494
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(200px,30vh)] min-h-[168px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
1495
+ recharts.LineChart,
1496
+ {
1497
+ data: funnelTimeSeriesRows,
1498
+ margin: { top: 4, right: 8, left: 0, bottom: 4 },
1499
+ children: [
1500
+ /* @__PURE__ */ jsxRuntime.jsx(
1501
+ recharts.CartesianGrid,
1502
+ {
1503
+ strokeDasharray: "3 3",
1504
+ stroke: "rgba(148,163,184,0.2)"
1505
+ }
1506
+ ),
1507
+ /* @__PURE__ */ jsxRuntime.jsx(
1508
+ recharts.XAxis,
1509
+ {
1510
+ dataKey: "date",
1511
+ tick: CHART_AXIS_TICK_SM,
1512
+ stroke: CHART_AXIS_LINE,
1513
+ tickLine: { stroke: CHART_AXIS_LINE },
1514
+ tickFormatter: (value) => {
1515
+ const row = dailyOrders.find((r) => r.date === value);
1516
+ return (row == null ? void 0 : row.label) ?? formatChartLabel(String(value));
1517
+ }
1518
+ }
1519
+ ),
1520
+ /* @__PURE__ */ jsxRuntime.jsx(
1521
+ recharts.YAxis,
1522
+ {
1523
+ tick: CHART_AXIS_TICK_SM,
1524
+ stroke: CHART_AXIS_LINE,
1525
+ tickLine: { stroke: CHART_AXIS_LINE },
1526
+ width: 28,
1527
+ allowDecimals: false,
1528
+ tickFormatter: (v) => Math.floor(Number(v) || 0).toLocaleString()
1529
+ }
1530
+ ),
1531
+ /* @__PURE__ */ jsxRuntime.jsx(
1532
+ recharts.Tooltip,
1533
+ {
1534
+ content: ({ active, payload, label }) => {
1535
+ var _a2;
1536
+ return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(
1537
+ TooltipCard,
1538
+ {
1539
+ title: typeof label === "string" ? ((_a2 = dailyOrders.find((r) => r.date === label)) == null ? void 0 : _a2.label) ?? formatChartLabel(label) : String(label),
1540
+ children: payload.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1541
+ String(p.name),
1542
+ ":",
1543
+ " ",
1544
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(Number(p.value) || 0).toLocaleString() })
1545
+ ] }, String(p.dataKey)))
1546
+ }
1547
+ ) : null;
1548
+ }
1549
+ }
1550
+ ),
1551
+ /* @__PURE__ */ jsxRuntime.jsx(
1552
+ recharts.Legend,
1553
+ {
1554
+ wrapperStyle: { fontSize: 9, color: "#e5e7eb", paddingTop: 4 },
1555
+ iconType: "circle",
1556
+ iconSize: 6
1557
+ }
1558
+ ),
1559
+ /* @__PURE__ */ jsxRuntime.jsx(
1560
+ recharts.Line,
1561
+ {
1562
+ type: "natural",
1563
+ dataKey: "placed",
1564
+ name: "Placed",
1565
+ stroke: "#6366F1",
1566
+ strokeWidth: 2,
1567
+ dot: false
1568
+ }
1569
+ ),
1570
+ /* @__PURE__ */ jsxRuntime.jsx(
1571
+ recharts.Line,
1572
+ {
1573
+ type: "natural",
1574
+ dataKey: "not_cancelled",
1575
+ name: "Not cancelled",
1576
+ stroke: "#22d3ee",
1577
+ strokeWidth: 2,
1578
+ dot: false
1579
+ }
1580
+ ),
1581
+ /* @__PURE__ */ jsxRuntime.jsx(
1582
+ recharts.Line,
1583
+ {
1584
+ type: "natural",
1585
+ dataKey: "shipped",
1586
+ name: "Shipped",
1587
+ stroke: "#F59E0B",
1588
+ strokeWidth: 2,
1589
+ dot: false
1590
+ }
1591
+ ),
1592
+ /* @__PURE__ */ jsxRuntime.jsx(
1593
+ recharts.Line,
1594
+ {
1595
+ type: "natural",
1596
+ dataKey: "delivered",
1597
+ name: "Delivered",
1598
+ stroke: "#10B981",
1599
+ strokeWidth: 2,
1600
+ dot: false
1601
+ }
1602
+ )
1603
+ ]
1604
+ }
1605
+ ) }) })
1606
+ ] }),
1607
+ /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
1608
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mb-0.5 text-[10px] font-medium uppercase tracking-[0.14em] text-ui-fg-muted", children: "Stage conversion (% of prior stage, same buckets)" }),
1609
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(168px,26vh)] min-h-[140px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
1610
+ recharts.LineChart,
1611
+ {
1612
+ data: funnelTimeSeriesRows,
1613
+ margin: { top: 4, right: 8, left: 0, bottom: 4 },
1614
+ children: [
1615
+ /* @__PURE__ */ jsxRuntime.jsx(
1616
+ recharts.CartesianGrid,
1617
+ {
1618
+ strokeDasharray: "3 3",
1619
+ stroke: "rgba(148,163,184,0.2)"
1620
+ }
1621
+ ),
1622
+ /* @__PURE__ */ jsxRuntime.jsx(
1623
+ recharts.XAxis,
1624
+ {
1625
+ dataKey: "date",
1626
+ tick: CHART_AXIS_TICK_SM,
1627
+ stroke: CHART_AXIS_LINE,
1628
+ tickLine: { stroke: CHART_AXIS_LINE },
1629
+ tickFormatter: (value) => {
1630
+ const row = dailyOrders.find((r) => r.date === value);
1631
+ return (row == null ? void 0 : row.label) ?? formatChartLabel(String(value));
1632
+ }
1633
+ }
1634
+ ),
1635
+ /* @__PURE__ */ jsxRuntime.jsx(
1636
+ recharts.YAxis,
1637
+ {
1638
+ domain: [0, 100],
1639
+ tick: CHART_AXIS_TICK_SM,
1640
+ stroke: CHART_AXIS_LINE,
1641
+ tickLine: { stroke: CHART_AXIS_LINE },
1642
+ width: 36,
1643
+ tickFormatter: (v) => `${Math.round(Number(v) || 0)}%`
1644
+ }
1645
+ ),
1646
+ /* @__PURE__ */ jsxRuntime.jsx(
1647
+ recharts.Tooltip,
1648
+ {
1649
+ content: ({ active, payload, label }) => {
1650
+ var _a2;
1651
+ return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(
1652
+ TooltipCard,
1653
+ {
1654
+ title: typeof label === "string" ? ((_a2 = dailyOrders.find((r) => r.date === label)) == null ? void 0 : _a2.label) ?? formatChartLabel(label) : String(label),
1655
+ children: payload.map((p) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1656
+ String(p.name),
1657
+ ":",
1658
+ " ",
1659
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatPercent(Number(p.value) || 0) })
1660
+ ] }, String(p.dataKey)))
1661
+ }
1662
+ ) : null;
1663
+ }
1664
+ }
1665
+ ),
1666
+ /* @__PURE__ */ jsxRuntime.jsx(
1667
+ recharts.Legend,
1668
+ {
1669
+ wrapperStyle: { fontSize: 9, color: "#e5e7eb", paddingTop: 4 },
1670
+ iconType: "circle",
1671
+ iconSize: 6
1672
+ }
1673
+ ),
1674
+ /* @__PURE__ */ jsxRuntime.jsx(
1675
+ recharts.Line,
1676
+ {
1677
+ type: "natural",
1678
+ dataKey: "pct_not_cancelled",
1679
+ name: "Not cancelled / placed",
1680
+ stroke: "#6366F1",
1681
+ strokeWidth: 2,
1682
+ dot: false
1683
+ }
1684
+ ),
1685
+ /* @__PURE__ */ jsxRuntime.jsx(
1686
+ recharts.Line,
1687
+ {
1688
+ type: "natural",
1689
+ dataKey: "pct_shipped_of_active",
1690
+ name: "Shipped / not cancelled",
1691
+ stroke: "#F59E0B",
1692
+ strokeWidth: 2,
1693
+ dot: false
1694
+ }
1695
+ ),
1696
+ /* @__PURE__ */ jsxRuntime.jsx(
1697
+ recharts.Line,
1698
+ {
1699
+ type: "natural",
1700
+ dataKey: "pct_delivered_of_shipped",
1701
+ name: "Delivered / shipped",
1702
+ stroke: "#10B981",
1703
+ strokeWidth: 2,
1704
+ dot: false
1705
+ }
1706
+ )
1707
+ ]
1708
+ }
1709
+ ) }) })
1710
+ ] })
1711
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1712
+ EmptyAnalyticsPanel,
1713
+ {
1714
+ title: "No funnel data",
1715
+ description: "No orders in this range to chart."
1716
+ }
1717
+ )
1718
+ }
1719
+ ),
1720
+ /* @__PURE__ */ jsxRuntime.jsx(
1721
+ AnalyticsSection,
1722
+ {
1723
+ variant: "atlas",
1724
+ title: "Order revenue (attribution)",
1725
+ description: "All revenue is from captured Medusa orders until storefront channel data exists.",
1726
+ children: insightsLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) : insightsError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center px-2 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: insightsError }) }) : ordersInsights && ordersInsights.traffic.length > 0 ? trafficTotalRevenue <= 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted px-0.5 text-center text-[11px] leading-snug", children: "No recorded revenue in this window (totals may be zero until orders are paid or finalized)." }) : /* @__PURE__ */ jsxRuntime.jsx(TrafficRevenueDonut, { traffic: ordersInsights.traffic }) : /* @__PURE__ */ jsxRuntime.jsx(
1727
+ EmptyAnalyticsPanel,
1728
+ {
1729
+ title: "No order revenue",
1730
+ description: "No orders in this window."
1731
+ }
1732
+ )
236
1733
  }
237
1734
  )
238
1735
  ] })
239
- }
240
- ),
241
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4", children: [
242
- /* @__PURE__ */ jsxRuntime.jsx(
243
- AnalyticsStatCard,
244
- {
245
- label: "Total orders",
246
- value: data.totalOrders.toLocaleString()
247
- }
248
- ),
249
- /* @__PURE__ */ jsxRuntime.jsx(
250
- AnalyticsStatCard,
251
- {
252
- label: "Total revenue",
253
- value: formatCurrency$1(data.totalRevenue)
254
- }
255
- ),
256
- /* @__PURE__ */ jsxRuntime.jsx(
257
- AnalyticsStatCard,
258
- {
259
- label: "Average order value",
260
- value: formatCurrency$1(data.aov)
261
- }
262
- ),
263
- /* @__PURE__ */ jsxRuntime.jsx(
264
- AnalyticsStatCard,
265
- {
266
- label: "Orders today",
267
- value: data.ordersToday.toLocaleString()
268
- }
269
- ),
270
- /* @__PURE__ */ jsxRuntime.jsx(
271
- AnalyticsStatCard,
272
- {
273
- label: "Pending orders",
274
- value: data.pendingOrders.toLocaleString()
275
- }
276
- ),
277
- /* @__PURE__ */ jsxRuntime.jsx(
278
- AnalyticsStatCard,
279
- {
280
- label: "Avg. fulfillment time",
281
- value: data.avgFulfillmentTimeHours != null ? `${data.avgFulfillmentTimeHours}h` : "—",
282
- helper: "Measured in hours"
283
- }
284
- ),
285
- /* @__PURE__ */ jsxRuntime.jsx(
286
- AnalyticsStatCard,
287
- {
288
- label: "Cancelled orders",
289
- value: data.cancelledOrders.toLocaleString()
290
- }
291
- ),
292
- /* @__PURE__ */ jsxRuntime.jsx(
293
- AnalyticsStatCard,
294
- {
295
- label: "Delivered orders",
296
- value: data.deliveredOrders.toLocaleString()
297
- }
298
- ),
299
- /* @__PURE__ */ jsxRuntime.jsx(
300
- AnalyticsStatCard,
301
- {
302
- label: "Exchange orders",
303
- value: data.exchangeOrders.toLocaleString()
304
- }
305
- ),
306
- /* @__PURE__ */ jsxRuntime.jsx(
307
- AnalyticsStatCard,
308
- {
309
- label: "Return orders",
310
- value: data.returnOrders.toLocaleString()
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: [
1736
+ ] }),
1737
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 space-y-1.5", children: [
1738
+ /* @__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" }) }),
1739
+ /* @__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(
1740
+ AnalyticsStatCard,
1741
+ {
1742
+ label: stat.label,
1743
+ value: stat.value,
1744
+ helper: stat.helper,
1745
+ variant: "compact"
1746
+ },
1747
+ stat.label
1748
+ )) })
1749
+ ] }),
1750
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 grid grid-cols-1 items-start gap-2 xl:grid-cols-12", children: [
316
1751
  /* @__PURE__ */ jsxRuntime.jsx(
317
- recharts.Pie,
1752
+ AnalyticsSection,
318
1753
  {
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}`))
1754
+ variant: "atlas",
1755
+ className: "xl:col-span-7",
1756
+ title: "Order outcomes",
1757
+ description: "Delivered, cancelled, exchanges, and returns per bucket — same window as Revenue & orders.",
1758
+ children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[176px] sm:h-[196px]", 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(
1759
+ recharts.LineChart,
1760
+ {
1761
+ data: dailyOrders,
1762
+ margin: { top: 4, right: 8, left: 0, bottom: 0 },
1763
+ children: [
1764
+ /* @__PURE__ */ jsxRuntime.jsx(
1765
+ recharts.CartesianGrid,
1766
+ {
1767
+ stroke: "rgba(148,163,184,0.14)",
1768
+ strokeDasharray: "3 3",
1769
+ vertical: false
1770
+ }
1771
+ ),
1772
+ /* @__PURE__ */ jsxRuntime.jsx(
1773
+ recharts.XAxis,
1774
+ {
1775
+ dataKey: "date",
1776
+ tick: CHART_AXIS_TICK,
1777
+ tickLine: false,
1778
+ axisLine: false,
1779
+ tickFormatter: (value) => {
1780
+ const dateStr = typeof value === "string" ? value : String(value);
1781
+ const row = dailyOrders.find((d) => d.date === dateStr);
1782
+ return (row == null ? void 0 : row.label) ?? formatChartLabel(dateStr);
1783
+ }
1784
+ }
1785
+ ),
1786
+ /* @__PURE__ */ jsxRuntime.jsx(
1787
+ recharts.YAxis,
1788
+ {
1789
+ width: 28,
1790
+ tick: CHART_AXIS_TICK,
1791
+ tickLine: false,
1792
+ axisLine: false,
1793
+ allowDecimals: false,
1794
+ tickFormatter: (value) => Math.floor(Number(value) || 0).toLocaleString()
1795
+ }
1796
+ ),
1797
+ /* @__PURE__ */ jsxRuntime.jsx(
1798
+ recharts.Tooltip,
1799
+ {
1800
+ content: /* @__PURE__ */ jsxRuntime.jsx(OutcomesTrendTooltip, {}),
1801
+ cursor: { stroke: "rgba(148,163,184,0.35)", strokeWidth: 1 }
1802
+ }
1803
+ ),
1804
+ /* @__PURE__ */ jsxRuntime.jsx(
1805
+ recharts.Legend,
1806
+ {
1807
+ wrapperStyle: { fontSize: 9, paddingTop: 0 },
1808
+ iconType: "circle",
1809
+ iconSize: 6
1810
+ }
1811
+ ),
1812
+ /* @__PURE__ */ jsxRuntime.jsx(
1813
+ recharts.Line,
1814
+ {
1815
+ type: "natural",
1816
+ dataKey: "delivered_count",
1817
+ name: "Delivered",
1818
+ stroke: STATUS_BAR_COLORS.delivered,
1819
+ strokeWidth: 2,
1820
+ dot: false
1821
+ }
1822
+ ),
1823
+ /* @__PURE__ */ jsxRuntime.jsx(
1824
+ recharts.Line,
1825
+ {
1826
+ type: "natural",
1827
+ dataKey: "cancelled_count",
1828
+ name: "Cancelled",
1829
+ stroke: STATUS_BAR_COLORS.cancelled,
1830
+ strokeWidth: 2,
1831
+ dot: false
1832
+ }
1833
+ ),
1834
+ /* @__PURE__ */ jsxRuntime.jsx(
1835
+ recharts.Line,
1836
+ {
1837
+ type: "natural",
1838
+ dataKey: "exchange_count",
1839
+ name: "Exchanges",
1840
+ stroke: STATUS_BAR_COLORS.exchange,
1841
+ strokeWidth: 2,
1842
+ dot: false
1843
+ }
1844
+ ),
1845
+ /* @__PURE__ */ jsxRuntime.jsx(
1846
+ recharts.Line,
1847
+ {
1848
+ type: "natural",
1849
+ dataKey: "return_count",
1850
+ name: "Returns",
1851
+ stroke: STATUS_BAR_COLORS.return,
1852
+ strokeWidth: 2,
1853
+ dot: false
1854
+ }
1855
+ )
1856
+ ]
1857
+ }
1858
+ ) }) }) })
329
1859
  }
330
1860
  ),
331
1861
  /* @__PURE__ */ jsxRuntime.jsx(
332
- recharts.Tooltip,
1862
+ AnalyticsSection,
333
1863
  {
334
- formatter: (value) => [value ?? 0, "Orders"]
1864
+ variant: "atlas",
1865
+ className: "xl:col-span-5",
1866
+ title: "Recent orders (7 days)",
1867
+ description: "Daily order count vs 7-day average — time series.",
1868
+ children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
1869
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-1.5", children: [
1870
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle/50 px-2 py-2", children: [
1871
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Today" }),
1872
+ /* @__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" })
1873
+ ] }),
1874
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle/50 px-2 py-2", children: [
1875
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "7-day avg" }),
1876
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-base text-base font-semibold tracking-tight", children: todayOrdersAverage.toFixed(1) })
1877
+ ] })
1878
+ ] }),
1879
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[140px] sm:h-[156px]", 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(
1880
+ recharts.ComposedChart,
1881
+ {
1882
+ data: todayChartData,
1883
+ margin: { top: 4, right: 4, left: -4, bottom: 0 },
1884
+ children: [
1885
+ /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "todayOrdersFill", x1: "0", y1: "0", x2: "0", y2: "1", children: [
1886
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#3b82f6", stopOpacity: 0.2 }),
1887
+ /* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#3b82f6", stopOpacity: 0 })
1888
+ ] }) }),
1889
+ /* @__PURE__ */ jsxRuntime.jsx(
1890
+ recharts.CartesianGrid,
1891
+ {
1892
+ stroke: "rgba(148,163,184,0.14)",
1893
+ strokeDasharray: "3 3",
1894
+ vertical: false
1895
+ }
1896
+ ),
1897
+ /* @__PURE__ */ jsxRuntime.jsx(
1898
+ recharts.XAxis,
1899
+ {
1900
+ dataKey: "date",
1901
+ tick: CHART_AXIS_TICK,
1902
+ tickLine: false,
1903
+ axisLine: false,
1904
+ tickFormatter: formatChartLabel
1905
+ }
1906
+ ),
1907
+ /* @__PURE__ */ jsxRuntime.jsx(
1908
+ recharts.YAxis,
1909
+ {
1910
+ width: 28,
1911
+ tick: CHART_AXIS_TICK,
1912
+ tickLine: false,
1913
+ axisLine: false,
1914
+ allowDecimals: false,
1915
+ tickFormatter: (value) => Math.floor(Number(value) || 0).toLocaleString()
1916
+ }
1917
+ ),
1918
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(OrdersTodayTooltip, {}), cursor: { stroke: "rgba(148,163,184,0.35)", strokeWidth: 1 } }),
1919
+ /* @__PURE__ */ jsxRuntime.jsx(
1920
+ recharts.ReferenceLine,
1921
+ {
1922
+ y: todayOrdersAverage,
1923
+ stroke: "rgba(148,163,184,0.65)",
1924
+ strokeDasharray: "4 4"
1925
+ }
1926
+ ),
1927
+ /* @__PURE__ */ jsxRuntime.jsx(
1928
+ recharts.Area,
1929
+ {
1930
+ type: "natural",
1931
+ dataKey: "orders_count",
1932
+ stroke: "none",
1933
+ fill: "url(#todayOrdersFill)",
1934
+ isAnimationActive: false
1935
+ }
1936
+ ),
1937
+ /* @__PURE__ */ jsxRuntime.jsx(
1938
+ recharts.Line,
1939
+ {
1940
+ type: "natural",
1941
+ dataKey: "orders_count",
1942
+ name: "Orders",
1943
+ stroke: "#3b82f6",
1944
+ strokeWidth: 2,
1945
+ dot: (props) => {
1946
+ const { cx, cy, payload } = props;
1947
+ if (payload && typeof payload === "object" && "isToday" in payload && payload.isToday) {
1948
+ return /* @__PURE__ */ jsxRuntime.jsx(
1949
+ "circle",
1950
+ {
1951
+ cx,
1952
+ cy,
1953
+ r: 4,
1954
+ fill: STATUS_BAR_COLORS.today,
1955
+ stroke: "var(--medusa-color-ui-bg-base)",
1956
+ strokeWidth: 1
1957
+ }
1958
+ );
1959
+ }
1960
+ return /* @__PURE__ */ jsxRuntime.jsx("circle", { cx, cy, r: 0, fill: "transparent" });
1961
+ }
1962
+ }
1963
+ )
1964
+ ]
1965
+ }
1966
+ ) }) }),
1967
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "rounded-lg border border-ui-border-base bg-ui-bg-subtle/40 px-2 py-1.5 shadow-sm", children: [
1968
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-[10px] font-medium uppercase tracking-[0.14em]", children: "Note" }),
1969
+ /* @__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.` })
1970
+ ] })
1971
+ ] }) })
335
1972
  }
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: [
1973
+ )
1974
+ ] }),
1975
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 grid grid-cols-1 items-start gap-2 md:grid-cols-3", children: [
340
1976
  /* @__PURE__ */ jsxRuntime.jsx(
341
- recharts.Pie,
1977
+ AnalyticsSection,
342
1978
  {
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}`))
1979
+ variant: "atlas",
1980
+ title: "Sales by weekday",
1981
+ description: `Order revenue by UTC weekday — last ${insightsWindowDays} days.`,
1982
+ children: insightsLoading ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) }) : insightsError ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center px-2 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: insightsError }) }) }) : weekdayChartRows.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(176px,28vh)] min-h-[148px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
1983
+ recharts.BarChart,
1984
+ {
1985
+ data: weekdayChartRows,
1986
+ margin: { top: 2, right: 4, left: -8, bottom: 2 },
1987
+ children: [
1988
+ /* @__PURE__ */ jsxRuntime.jsx(
1989
+ recharts.CartesianGrid,
1990
+ {
1991
+ strokeDasharray: "3 3",
1992
+ vertical: false,
1993
+ stroke: "rgba(148,163,184,0.12)"
1994
+ }
1995
+ ),
1996
+ /* @__PURE__ */ jsxRuntime.jsx(
1997
+ recharts.XAxis,
1998
+ {
1999
+ dataKey: "label",
2000
+ tick: CHART_AXIS_TICK
2001
+ }
2002
+ ),
2003
+ /* @__PURE__ */ jsxRuntime.jsx(
2004
+ recharts.YAxis,
2005
+ {
2006
+ width: 40,
2007
+ tick: CHART_AXIS_TICK_SM,
2008
+ tickFormatter: (v) => formatCompactCurrency(Number(v))
2009
+ }
2010
+ ),
2011
+ /* @__PURE__ */ jsxRuntime.jsx(
2012
+ recharts.Tooltip,
2013
+ {
2014
+ content: ({ active, payload, label }) => {
2015
+ var _a2, _b2;
2016
+ return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(TooltipCard, { title: String(label), children: [
2017
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2018
+ "Revenue:",
2019
+ " ",
2020
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency$1(
2021
+ Number(
2022
+ (_a2 = payload.find((p) => p.dataKey === "revenue")) == null ? void 0 : _a2.value
2023
+ ) || 0
2024
+ ) })
2025
+ ] }),
2026
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2027
+ "Orders:",
2028
+ " ",
2029
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(
2030
+ Number(
2031
+ (_b2 = payload.find((p) => p.dataKey === "orders_count")) == null ? void 0 : _b2.value
2032
+ ) || 0
2033
+ ).toLocaleString() })
2034
+ ] })
2035
+ ] }) : null;
2036
+ }
2037
+ }
2038
+ ),
2039
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Bar, { dataKey: "revenue", name: "Revenue", fill: "#D946EF", radius: [4, 4, 0, 0] })
2040
+ ]
2041
+ }
2042
+ ) }) }) }) : /* @__PURE__ */ jsxRuntime.jsx(
2043
+ EmptyAnalyticsPanel,
2044
+ {
2045
+ title: "No weekday data",
2046
+ description: "No orders in this window."
2047
+ }
2048
+ )
353
2049
  }
354
2050
  ),
355
2051
  /* @__PURE__ */ jsxRuntime.jsx(
356
- recharts.Tooltip,
2052
+ AnalyticsSection,
357
2053
  {
358
- formatter: (value) => [value ?? 0, "Count"]
2054
+ variant: "atlas",
2055
+ title: "Placed orders vs drafts",
2056
+ description: "Window order count vs current open draft orders (admin). Not storefront cart abandonment.",
2057
+ children: insightsLoading ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) }) : insightsError ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center px-2 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: insightsError }) }) }) : ordersVsDraftsPie.some((s) => s.value > 0) ? /* @__PURE__ */ jsxRuntime.jsxs(AnalyticsChartSurface, { variant: "atlas", children: [
2058
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(176px,28vh)] min-h-[148px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.PieChart, { margin: { top: 2, bottom: 2, left: 2, right: 2 }, children: [
2059
+ /* @__PURE__ */ jsxRuntime.jsx(
2060
+ recharts.Pie,
2061
+ {
2062
+ data: ordersVsDraftsPie,
2063
+ dataKey: "value",
2064
+ nameKey: "name",
2065
+ cx: "50%",
2066
+ cy: "46%",
2067
+ innerRadius: 40,
2068
+ outerRadius: 58,
2069
+ paddingAngle: 2,
2070
+ children: ordersVsDraftsPie.map((slice, i) => /* @__PURE__ */ jsxRuntime.jsx(
2071
+ recharts.Cell,
2072
+ {
2073
+ fill: PIE_PALETTE[i % PIE_PALETTE.length]
2074
+ },
2075
+ slice.key
2076
+ ))
2077
+ }
2078
+ ),
2079
+ /* @__PURE__ */ jsxRuntime.jsx(
2080
+ recharts.Tooltip,
2081
+ {
2082
+ content: ({ active, payload }) => {
2083
+ var _a2, _b2;
2084
+ return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsx(TooltipCard, { title: String(((_a2 = payload[0]) == null ? void 0 : _a2.name) ?? ""), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2085
+ "Count:",
2086
+ " ",
2087
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(
2088
+ Number((_b2 = payload[0]) == null ? void 0 : _b2.value) || 0
2089
+ ).toLocaleString() })
2090
+ ] }) }) : null;
2091
+ }
2092
+ }
2093
+ ),
2094
+ /* @__PURE__ */ jsxRuntime.jsx(
2095
+ recharts.Legend,
2096
+ {
2097
+ wrapperStyle: { fontSize: 9, paddingTop: 0 },
2098
+ verticalAlign: "bottom",
2099
+ height: 22,
2100
+ iconType: "circle"
2101
+ }
2102
+ )
2103
+ ] }) }) }),
2104
+ (ordersInsights == null ? void 0 : ordersInsights.drafts.available) === false ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted mt-0.5 px-0.5 text-[10px] leading-snug", children: "Draft totals need draft_order remote query access." }) : null
2105
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
2106
+ EmptyAnalyticsPanel,
2107
+ {
2108
+ title: "Nothing to compare",
2109
+ description: "No placed orders and no draft rows in this snapshot."
2110
+ }
2111
+ )
359
2112
  }
360
2113
  ),
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",
372
- {
373
- id: "over-time-period",
374
- value: overTimePeriod,
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))
378
- }
379
- )
380
- ] }),
381
- children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-72", children: overTimeLoading ? /* @__PURE__ */ jsxRuntime.jsx(
382
- "div",
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,
2114
+ /* @__PURE__ */ jsxRuntime.jsx(
2115
+ AnalyticsSection,
391
2116
  {
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
- ]
2117
+ variant: "atlas",
2118
+ title: "Sales by hour (UTC)",
2119
+ description: `Orders created by hour — last ${insightsWindowDays} days.`,
2120
+ children: insightsLoading ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: "Loading…" }) }) }) : insightsError ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[100px] items-center justify-center px-2 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-xs", children: insightsError }) }) }) : hourlyChartRows.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { variant: "atlas", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-[min(176px,28vh)] min-h-[148px] w-full", children: /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(
2121
+ recharts.BarChart,
2122
+ {
2123
+ data: hourlyChartRows,
2124
+ margin: { top: 2, right: 4, left: -8, bottom: 2 },
2125
+ children: [
2126
+ /* @__PURE__ */ jsxRuntime.jsx(
2127
+ recharts.CartesianGrid,
2128
+ {
2129
+ strokeDasharray: "3 3",
2130
+ vertical: false,
2131
+ stroke: "rgba(148,163,184,0.12)"
2132
+ }
2133
+ ),
2134
+ /* @__PURE__ */ jsxRuntime.jsx(
2135
+ recharts.XAxis,
2136
+ {
2137
+ dataKey: "label",
2138
+ interval: 3,
2139
+ tick: CHART_AXIS_TICK_SM
2140
+ }
2141
+ ),
2142
+ /* @__PURE__ */ jsxRuntime.jsx(
2143
+ recharts.YAxis,
2144
+ {
2145
+ width: 28,
2146
+ tick: CHART_AXIS_TICK,
2147
+ allowDecimals: false
2148
+ }
2149
+ ),
2150
+ /* @__PURE__ */ jsxRuntime.jsx(
2151
+ recharts.Tooltip,
2152
+ {
2153
+ content: ({ active, payload, label }) => {
2154
+ var _a2, _b2;
2155
+ return active && (payload == null ? void 0 : payload.length) ? /* @__PURE__ */ jsxRuntime.jsxs(TooltipCard, { title: String(label), children: [
2156
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2157
+ "Orders:",
2158
+ " ",
2159
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(
2160
+ Number(
2161
+ (_a2 = payload.find((p) => p.dataKey === "orders_count")) == null ? void 0 : _a2.value
2162
+ ) || 0
2163
+ ).toLocaleString() })
2164
+ ] }),
2165
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2166
+ "Revenue:",
2167
+ " ",
2168
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency$1(
2169
+ Number(
2170
+ (_b2 = payload.find((p) => p.dataKey === "revenue")) == null ? void 0 : _b2.value
2171
+ ) || 0
2172
+ ) })
2173
+ ] })
2174
+ ] }) : null;
2175
+ }
2176
+ }
2177
+ ),
2178
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Bar, { dataKey: "orders_count", name: "Orders", fill: "#38BDF8", radius: [4, 4, 0, 0] })
2179
+ ]
2180
+ }
2181
+ ) }) }) }) : /* @__PURE__ */ jsxRuntime.jsx(
2182
+ EmptyAnalyticsPanel,
2183
+ {
2184
+ title: "No hourly data",
2185
+ description: "No orders in this window."
2186
+ }
2187
+ )
442
2188
  }
443
- ) }) }) })
444
- }
445
- )
446
- ] });
2189
+ )
2190
+ ] })
2191
+ ] })
2192
+ ] }) });
447
2193
  }
448
2194
  function CustomersOverTimeTooltip({
449
2195
  active,
@@ -483,6 +2229,17 @@ function CustomersOverTimeTooltip({
483
2229
  }
484
2230
  );
485
2231
  }
2232
+ function MixPieTooltip({
2233
+ active,
2234
+ payload
2235
+ }) {
2236
+ if (!active || !(payload == null ? void 0 : payload.length)) return null;
2237
+ const row = payload[0];
2238
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-base bg-ui-bg-base px-3 py-2 text-sm shadow-md", children: [
2239
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-medium text-ui-fg-base", children: row.name }),
2240
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: Math.floor(Number(row.value) || 0).toLocaleString() })
2241
+ ] });
2242
+ }
486
2243
  const COUNT_DAY_PRESETS = [
487
2244
  { value: "all", label: "All time" },
488
2245
  { value: "0", label: "Today" },
@@ -506,12 +2263,15 @@ function CustomersDashboard() {
506
2263
  const [graphPeriod, setGraphPeriod] = react.useState("one_week");
507
2264
  const [graphLoading, setGraphLoading] = react.useState(true);
508
2265
  const [graphError, setGraphError] = react.useState(null);
2266
+ const [repeatStats, setRepeatStats] = react.useState(null);
2267
+ const [repeatLoading, setRepeatLoading] = react.useState(true);
2268
+ const [repeatError, setRepeatError] = react.useState(null);
509
2269
  react.useEffect(() => {
510
2270
  let cancelled = false;
511
2271
  setLoading(true);
512
2272
  setError(null);
513
2273
  const url = `/admin/analytics/customers-summary?days=${countDays}`;
514
- fetch(url).then((res) => {
2274
+ fetch(url, { credentials: "include" }).then((res) => {
515
2275
  if (!res.ok) throw new Error(res.statusText);
516
2276
  return res.json();
517
2277
  }).then((body) => {
@@ -530,7 +2290,7 @@ function CustomersDashboard() {
530
2290
  setGraphLoading(true);
531
2291
  setGraphError(null);
532
2292
  const url = `/admin/analytics/customers-over-time?period=${graphPeriod}`;
533
- fetch(url).then((res) => {
2293
+ fetch(url, { credentials: "include" }).then((res) => {
534
2294
  if (!res.ok) throw new Error(res.statusText);
535
2295
  return res.json();
536
2296
  }).then((body) => {
@@ -544,6 +2304,34 @@ function CustomersDashboard() {
544
2304
  cancelled = true;
545
2305
  };
546
2306
  }, [graphPeriod]);
2307
+ react.useEffect(() => {
2308
+ let cancelled = false;
2309
+ setRepeatLoading(true);
2310
+ setRepeatError(null);
2311
+ fetch(`/admin/analytics/repeat-customers?days=${countDays}`, {
2312
+ credentials: "include"
2313
+ }).then((res) => {
2314
+ if (!res.ok) throw new Error(res.statusText);
2315
+ return res.json();
2316
+ }).then((body) => {
2317
+ if (!cancelled) setRepeatStats(body);
2318
+ }).catch((e) => {
2319
+ if (!cancelled) setRepeatError(e instanceof Error ? e.message : String(e));
2320
+ }).finally(() => {
2321
+ if (!cancelled) setRepeatLoading(false);
2322
+ });
2323
+ return () => {
2324
+ cancelled = true;
2325
+ };
2326
+ }, [countDays]);
2327
+ const guestRegisteredPieData = react.useMemo(() => {
2328
+ const g = Math.max(0, Math.floor((data == null ? void 0 : data.guestCount) ?? 0));
2329
+ const r = Math.max(0, Math.floor((data == null ? void 0 : data.registeredCount) ?? 0));
2330
+ return [
2331
+ { name: "Guest checkout", value: g, key: "guest" },
2332
+ { name: "Registered", value: r, key: "registered" }
2333
+ ].filter((s) => s.value > 0);
2334
+ }, [data]);
547
2335
  if (loading) {
548
2336
  return /* @__PURE__ */ jsxRuntime.jsx(
549
2337
  "div",
@@ -628,6 +2416,70 @@ function CustomersDashboard() {
628
2416
  }
629
2417
  )
630
2418
  ] }),
2419
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 lg:grid-cols-2", children: [
2420
+ /* @__PURE__ */ jsxRuntime.jsx(
2421
+ AnalyticsSection,
2422
+ {
2423
+ title: "Guest vs registered",
2424
+ description: "Customer mix for the selected count period (same filter as the summary cards).",
2425
+ children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-72", children: guestRegisteredPieData.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-sm", children: "No guest or registered customers in this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.PieChart, { children: [
2426
+ /* @__PURE__ */ jsxRuntime.jsx(
2427
+ recharts.Pie,
2428
+ {
2429
+ data: guestRegisteredPieData,
2430
+ dataKey: "value",
2431
+ nameKey: "name",
2432
+ cx: "50%",
2433
+ cy: "50%",
2434
+ innerRadius: 52,
2435
+ outerRadius: 88,
2436
+ paddingAngle: 2,
2437
+ children: guestRegisteredPieData.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
2438
+ recharts.Cell,
2439
+ {
2440
+ fill: entry.key === "guest" ? GUEST_COLOR : REGISTERED_COLOR
2441
+ },
2442
+ entry.key
2443
+ ))
2444
+ }
2445
+ ),
2446
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(MixPieTooltip, {}) }),
2447
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.Legend, { wrapperStyle: { fontSize: 12 }, iconType: "circle" })
2448
+ ] }) }) }) })
2449
+ }
2450
+ ),
2451
+ /* @__PURE__ */ jsxRuntime.jsx(
2452
+ AnalyticsSection,
2453
+ {
2454
+ title: "Repeat purchases",
2455
+ description: "Orders with a customer_id in the same window as the period filter (guest checkouts excluded from rate).",
2456
+ children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: repeatLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[200px] items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Loading…" }) }) : repeatError ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[200px] items-center justify-center px-2", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger text-center text-sm", children: repeatError }) }) : repeatStats ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: [
2457
+ /* @__PURE__ */ jsxRuntime.jsx(
2458
+ AnalyticsStatCard,
2459
+ {
2460
+ label: "Repeat rate",
2461
+ value: `${repeatStats.repeat_rate_percent.toFixed(1)}%`,
2462
+ helper: repeatStats.window_days >= 365 ? "Approx. 12-month window" : `Last ${repeatStats.window_days} days`
2463
+ }
2464
+ ),
2465
+ /* @__PURE__ */ jsxRuntime.jsx(
2466
+ AnalyticsStatCard,
2467
+ {
2468
+ label: "Customers with orders",
2469
+ value: repeatStats.customers_with_orders.toLocaleString()
2470
+ }
2471
+ ),
2472
+ /* @__PURE__ */ jsxRuntime.jsx(
2473
+ AnalyticsStatCard,
2474
+ {
2475
+ label: "Repeat customers (2+)",
2476
+ value: repeatStats.repeat_customers.toLocaleString()
2477
+ }
2478
+ )
2479
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-h-[200px] items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "No data." }) }) })
2480
+ }
2481
+ )
2482
+ ] }),
631
2483
  /* @__PURE__ */ jsxRuntime.jsx(
632
2484
  AnalyticsSection,
633
2485
  {
@@ -1019,6 +2871,10 @@ function ProductsDashboard() {
1019
2871
  })).reverse(),
1020
2872
  [performance]
1021
2873
  );
2874
+ const viewsVsUnitsScatterData = react.useMemo(
2875
+ () => ((performance == null ? void 0 : performance.topViewedProducts) ?? []).slice(0, 48).filter((row) => row.total_views > 0 || row.units_sold > 0),
2876
+ [performance]
2877
+ );
1022
2878
  if (summaryLoading) {
1023
2879
  return /* @__PURE__ */ jsxRuntime.jsx(
1024
2880
  "div",
@@ -1364,7 +3220,66 @@ function ProductsDashboard() {
1364
3220
  ] }) })
1365
3221
  }
1366
3222
  )
1367
- ] })
3223
+ ] }),
3224
+ /* @__PURE__ */ jsxRuntime.jsx(
3225
+ AnalyticsSection,
3226
+ {
3227
+ title: "Views vs units sold",
3228
+ description: "Each point is a product (top viewed in this period). Requires product view tracking.",
3229
+ children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-80", children: performanceLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading scatter…" }) }) : performanceError || !performance ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: performanceError ?? "Failed to load product performance" }) }) : !performance.productViewsConnected ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Product view tracking is not available in this environment." }) }) : viewsVsUnitsScatterData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "No products with views or sales in this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.ScatterChart, { margin: { top: 12, right: 16, bottom: 8, left: 8 }, children: [
3230
+ /* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", stroke: "rgba(148,163,184,0.2)" }),
3231
+ /* @__PURE__ */ jsxRuntime.jsx(
3232
+ recharts.XAxis,
3233
+ {
3234
+ type: "number",
3235
+ dataKey: "total_views",
3236
+ name: "Views",
3237
+ tick: { fontSize: 11 },
3238
+ allowDecimals: false,
3239
+ label: {
3240
+ value: "Views",
3241
+ position: "insideBottom",
3242
+ offset: -4,
3243
+ fontSize: 11,
3244
+ fill: "#6b7280"
3245
+ }
3246
+ }
3247
+ ),
3248
+ /* @__PURE__ */ jsxRuntime.jsx(
3249
+ recharts.YAxis,
3250
+ {
3251
+ type: "number",
3252
+ dataKey: "units_sold",
3253
+ name: "Units sold",
3254
+ tick: { fontSize: 11 },
3255
+ allowDecimals: false,
3256
+ label: {
3257
+ value: "Units sold",
3258
+ angle: -90,
3259
+ position: "insideLeft",
3260
+ fontSize: 11,
3261
+ fill: "#6b7280"
3262
+ }
3263
+ }
3264
+ ),
3265
+ /* @__PURE__ */ jsxRuntime.jsx(
3266
+ recharts.Tooltip,
3267
+ {
3268
+ cursor: { strokeDasharray: "4 4" },
3269
+ content: /* @__PURE__ */ jsxRuntime.jsx(ProductBarTooltip, {})
3270
+ }
3271
+ ),
3272
+ /* @__PURE__ */ jsxRuntime.jsx(
3273
+ recharts.Scatter,
3274
+ {
3275
+ name: "Products",
3276
+ data: viewsVsUnitsScatterData,
3277
+ fill: VIEWS_COLOR
3278
+ }
3279
+ )
3280
+ ] }) }) }) })
3281
+ }
3282
+ )
1368
3283
  ] });
1369
3284
  }
1370
3285
  const ANALYTICS_MODULES = [
@@ -1381,16 +3296,29 @@ const AnalyticsPage = () => {
1381
3296
  /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", className: "tracking-tight", children: "Analytics" }),
1382
3297
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: "Review orders, customers, and products in one organized workspace." })
1383
3298
  ] }),
1384
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-2xl border border-ui-border-base bg-ui-bg-subtle p-1.5", children: ANALYTICS_MODULES.map((m) => /* @__PURE__ */ jsxRuntime.jsx(
1385
- ui.Button,
3299
+ /* @__PURE__ */ jsxRuntime.jsx(
3300
+ "div",
1386
3301
  {
1387
- variant: activeModule === m.id ? "secondary" : "transparent",
1388
- size: "small",
1389
- onClick: () => setActiveModule(m.id),
1390
- children: m.label
1391
- },
1392
- m.id
1393
- )) })
3302
+ className: "flex flex-wrap items-center gap-1 rounded-2xl border border-ui-border-base bg-ui-bg-subtle p-1",
3303
+ role: "tablist",
3304
+ "aria-label": "Analytics modules",
3305
+ children: ANALYTICS_MODULES.map((m) => {
3306
+ const active = activeModule === m.id;
3307
+ return /* @__PURE__ */ jsxRuntime.jsx(
3308
+ "button",
3309
+ {
3310
+ type: "button",
3311
+ role: "tab",
3312
+ "aria-selected": active,
3313
+ onClick: () => setActiveModule(m.id),
3314
+ className: `rounded-xl px-3 py-1.5 text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ui-fg-interactive focus-visible:ring-offset-2 focus-visible:ring-offset-ui-bg-base ${active ? "bg-ui-bg-base text-ui-fg-base shadow-sm" : "text-ui-fg-muted hover:bg-ui-bg-base/60 hover:text-ui-fg-base"}`.trim(),
3315
+ children: m.label
3316
+ },
3317
+ m.id
3318
+ );
3319
+ })
3320
+ }
3321
+ )
1394
3322
  ] }),
1395
3323
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1 bg-ui-bg-subtle" })
1396
3324
  ] }),