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