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