medusa-analytics 0.0.16 → 0.0.17

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