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