medusa-analytics 0.0.15 → 0.0.16
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.
|
@@ -14,21 +14,21 @@ function AnalyticsDashboardHeader({
|
|
|
14
14
|
actions
|
|
15
15
|
}) {
|
|
16
16
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "overflow-hidden rounded-2xl border border-ui-border-base bg-ui-bg-base p-0 shadow-sm", children: [
|
|
17
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 border-b border-ui-border-base px-5 py-4
|
|
17
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 border-b border-ui-border-base px-5 py-4 lg:flex-row lg:items-end lg:justify-between", children: [
|
|
18
18
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
19
19
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "tracking-tight", children: title }),
|
|
20
20
|
description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: description }) : null
|
|
21
21
|
] }),
|
|
22
|
-
actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex
|
|
22
|
+
actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex self-start rounded-xl border border-ui-border-base bg-ui-bg-subtle p-2 lg:self-auto", children: actions }) : null
|
|
23
23
|
] }),
|
|
24
24
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-1 bg-ui-bg-subtle" })
|
|
25
25
|
] });
|
|
26
26
|
}
|
|
27
27
|
function AnalyticsStatCard({ label, value, helper }) {
|
|
28
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "h-full rounded-2xl border border-ui-border-base bg-ui-bg-base p-
|
|
29
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-
|
|
30
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "tracking-tight", children: value }),
|
|
31
|
-
helper ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs", children: helper }) : null
|
|
28
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "h-full rounded-2xl border border-ui-border-base bg-ui-bg-base p-3 shadow-sm transition-shadow", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-[76px] flex-col justify-between gap-1.5", children: [
|
|
29
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs font-medium", children: label }),
|
|
30
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "text-2xl leading-none tracking-tight", children: value }),
|
|
31
|
+
helper ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-xs leading-4", children: helper }) : null
|
|
32
32
|
] }) });
|
|
33
33
|
}
|
|
34
34
|
function AnalyticsSection({
|
|
@@ -48,7 +48,7 @@ function AnalyticsSection({
|
|
|
48
48
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: "tracking-tight", children: title }),
|
|
49
49
|
description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted text-sm", children: description }) : null
|
|
50
50
|
] }),
|
|
51
|
-
actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-xl border border-ui-border-base bg-ui-bg-subtle
|
|
51
|
+
actions ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-xl border border-ui-border-base bg-ui-bg-subtle px-3 py-2", children: actions }) : null
|
|
52
52
|
] }),
|
|
53
53
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-4", children })
|
|
54
54
|
]
|
|
@@ -746,6 +746,7 @@ const TOP_SELLER_PERIODS = [
|
|
|
746
746
|
const SALES_COLOR = "var(--medusa-color-ui-fg-interactive)";
|
|
747
747
|
const VIEWS_COLOR = "#8B5CF6";
|
|
748
748
|
const REVENUE_COLOR = "#F59E0B";
|
|
749
|
+
const REVENUE_MUTED_COLOR = "#FCD34D";
|
|
749
750
|
function formatCurrency(value) {
|
|
750
751
|
return new Intl.NumberFormat("en-IN", {
|
|
751
752
|
style: "currency",
|
|
@@ -816,7 +817,8 @@ function TrendTooltip({
|
|
|
816
817
|
}
|
|
817
818
|
function ProductBarTooltip({
|
|
818
819
|
active,
|
|
819
|
-
payload
|
|
820
|
+
payload,
|
|
821
|
+
showViews = true
|
|
820
822
|
}) {
|
|
821
823
|
var _a;
|
|
822
824
|
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
@@ -844,10 +846,10 @@ function ProductBarTooltip({
|
|
|
844
846
|
"Units sold: ",
|
|
845
847
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(unitsSold).toLocaleString() })
|
|
846
848
|
] }),
|
|
847
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
849
|
+
showViews ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
848
850
|
"Views: ",
|
|
849
851
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: Math.floor(totalViews).toLocaleString() })
|
|
850
|
-
] }),
|
|
852
|
+
] }) : null,
|
|
851
853
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
852
854
|
"Revenue: ",
|
|
853
855
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: formatCurrency(Number(revenue)) })
|
|
@@ -1000,10 +1002,14 @@ function ProductsDashboard() {
|
|
|
1000
1002
|
};
|
|
1001
1003
|
}, [salesChannelId, summaryDays]);
|
|
1002
1004
|
const topSellerChartData = react.useMemo(
|
|
1003
|
-
() => ((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 7).map((row) =>
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1005
|
+
() => ((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 7).map((row) => {
|
|
1006
|
+
var _a2, _b2;
|
|
1007
|
+
return {
|
|
1008
|
+
...row,
|
|
1009
|
+
isBestSeller: row.product_id === (((_b2 = (_a2 = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.product_id) ?? null),
|
|
1010
|
+
product_title: truncateLabel(row.product_title, 24)
|
|
1011
|
+
};
|
|
1012
|
+
}).reverse(),
|
|
1007
1013
|
[topSellers]
|
|
1008
1014
|
);
|
|
1009
1015
|
const topViewedChartData = react.useMemo(
|
|
@@ -1035,9 +1041,9 @@ function ProductsDashboard() {
|
|
|
1035
1041
|
{
|
|
1036
1042
|
title: "Products",
|
|
1037
1043
|
description: "Track units sold, revenue, best sellers, and product visit behavior in a denser, easier-to-scan layout.",
|
|
1038
|
-
actions: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1039
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
1040
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "products-channel-filter", className: "text-ui-fg-muted text-
|
|
1044
|
+
actions: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-end gap-2.5", children: [
|
|
1045
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
1046
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "products-channel-filter", className: "text-ui-fg-muted text-xs font-medium", children: "Channel" }),
|
|
1041
1047
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1042
1048
|
"select",
|
|
1043
1049
|
{
|
|
@@ -1045,7 +1051,7 @@ function ProductsDashboard() {
|
|
|
1045
1051
|
value: salesChannelId,
|
|
1046
1052
|
onChange: (event) => setSalesChannelId(event.target.value),
|
|
1047
1053
|
disabled: filtersLoading,
|
|
1048
|
-
className: "h-
|
|
1054
|
+
className: "h-8 min-w-[150px] rounded-md border border-ui-border-base bg-ui-bg-base px-2.5 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
1049
1055
|
children: [
|
|
1050
1056
|
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "all", children: "All channels" }),
|
|
1051
1057
|
salesChannels.map((channel) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: channel.id, children: channel.name }, channel.id))
|
|
@@ -1053,39 +1059,41 @@ function ProductsDashboard() {
|
|
|
1053
1059
|
}
|
|
1054
1060
|
)
|
|
1055
1061
|
] }),
|
|
1056
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
1057
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "products-summary-period", className: "text-ui-fg-muted text-
|
|
1062
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
|
|
1063
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "products-summary-period", className: "text-ui-fg-muted text-xs font-medium", children: "Period" }),
|
|
1058
1064
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1059
1065
|
"select",
|
|
1060
1066
|
{
|
|
1061
1067
|
id: "products-summary-period",
|
|
1062
1068
|
value: summaryDays,
|
|
1063
1069
|
onChange: (event) => setSummaryDays(event.target.value),
|
|
1064
|
-
className: "h-
|
|
1070
|
+
className: "h-8 min-w-[132px] rounded-md border border-ui-border-base bg-ui-bg-base px-2.5 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
1065
1071
|
children: SUMMARY_PERIODS.map((period) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: period.value, children: period.label }, period.value))
|
|
1066
1072
|
}
|
|
1067
1073
|
)
|
|
1068
1074
|
] }),
|
|
1069
|
-
summaryDays !== "all" && /* @__PURE__ */ jsxRuntime.
|
|
1070
|
-
"
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
"
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1075
|
+
(summaryDays !== "all" || salesChannelId !== "all") && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 self-end pb-1", children: [
|
|
1076
|
+
summaryDays !== "all" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1077
|
+
"button",
|
|
1078
|
+
{
|
|
1079
|
+
type: "button",
|
|
1080
|
+
onClick: () => setSummaryDays("all"),
|
|
1081
|
+
className: "text-xs text-ui-fg-muted transition-colors hover:text-ui-fg-base",
|
|
1082
|
+
"aria-label": "Show all products analytics",
|
|
1083
|
+
children: "Clear period"
|
|
1084
|
+
}
|
|
1085
|
+
),
|
|
1086
|
+
salesChannelId !== "all" && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1087
|
+
"button",
|
|
1088
|
+
{
|
|
1089
|
+
type: "button",
|
|
1090
|
+
onClick: () => setSalesChannelId("all"),
|
|
1091
|
+
className: "text-xs text-ui-fg-muted transition-colors hover:text-ui-fg-base",
|
|
1092
|
+
"aria-label": "Show all sales channels",
|
|
1093
|
+
children: "Clear channel"
|
|
1094
|
+
}
|
|
1095
|
+
)
|
|
1096
|
+
] })
|
|
1089
1097
|
] })
|
|
1090
1098
|
}
|
|
1091
1099
|
),
|
|
@@ -1223,7 +1231,7 @@ function ProductsDashboard() {
|
|
|
1223
1231
|
AnalyticsSection,
|
|
1224
1232
|
{
|
|
1225
1233
|
title: "Best sellers",
|
|
1226
|
-
description: "
|
|
1234
|
+
description: "Top products by revenue.",
|
|
1227
1235
|
actions: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center gap-2", children: TOP_SELLER_PERIODS.map((period) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1228
1236
|
ui.Button,
|
|
1229
1237
|
{
|
|
@@ -1236,7 +1244,14 @@ function ProductsDashboard() {
|
|
|
1236
1244
|
)) }),
|
|
1237
1245
|
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-80", children: topSellersLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "Loading best sellers…" }) }) : topSellersError || !topSellers ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-danger", children: topSellersError ?? "Failed to load best sellers" }) }) : topSellerChartData.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "No product sales found for this range." }) }) : /* @__PURE__ */ jsxRuntime.jsx(recharts.ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxRuntime.jsxs(recharts.BarChart, { data: topSellerChartData, layout: "vertical", margin: { left: 8, right: 8 }, children: [
|
|
1238
1246
|
/* @__PURE__ */ jsxRuntime.jsx(recharts.CartesianGrid, { strokeDasharray: "3 3", horizontal: false }),
|
|
1239
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1247
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1248
|
+
recharts.XAxis,
|
|
1249
|
+
{
|
|
1250
|
+
type: "number",
|
|
1251
|
+
allowDecimals: false,
|
|
1252
|
+
tickFormatter: (value) => formatCurrency(Number(value))
|
|
1253
|
+
}
|
|
1254
|
+
),
|
|
1240
1255
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1241
1256
|
recharts.YAxis,
|
|
1242
1257
|
{
|
|
@@ -1246,8 +1261,14 @@ function ProductsDashboard() {
|
|
|
1246
1261
|
tickLine: false
|
|
1247
1262
|
}
|
|
1248
1263
|
),
|
|
1249
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(ProductBarTooltip, {}) }),
|
|
1250
|
-
/* @__PURE__ */ jsxRuntime.jsx(recharts.Bar, { dataKey: "
|
|
1264
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Tooltip, { content: /* @__PURE__ */ jsxRuntime.jsx(ProductBarTooltip, { showViews: false }) }),
|
|
1265
|
+
/* @__PURE__ */ jsxRuntime.jsx(recharts.Bar, { dataKey: "revenue", radius: [0, 6, 6, 0], children: topSellerChartData.map((entry) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
1266
|
+
recharts.Cell,
|
|
1267
|
+
{
|
|
1268
|
+
fill: entry.isBestSeller ? REVENUE_COLOR : REVENUE_MUTED_COLOR
|
|
1269
|
+
},
|
|
1270
|
+
entry.product_id
|
|
1271
|
+
)) })
|
|
1251
1272
|
] }) }) }) })
|
|
1252
1273
|
}
|
|
1253
1274
|
),
|
|
@@ -1255,7 +1276,7 @@ function ProductsDashboard() {
|
|
|
1255
1276
|
AnalyticsSection,
|
|
1256
1277
|
{
|
|
1257
1278
|
title: "Top seller breakdown",
|
|
1258
|
-
description: "
|
|
1279
|
+
description: "Products ranked by revenue.",
|
|
1259
1280
|
children: /* @__PURE__ */ jsxRuntime.jsx(AnalyticsTableSurface, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
|
|
1260
1281
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
1261
1282
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Product" }),
|
|
@@ -1264,12 +1285,22 @@ function ProductsDashboard() {
|
|
|
1264
1285
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Revenue" })
|
|
1265
1286
|
] }) }),
|
|
1266
1287
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Body, { children: [
|
|
1267
|
-
((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 8).map((product) =>
|
|
1268
|
-
|
|
1269
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1288
|
+
((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 8).map((product) => {
|
|
1289
|
+
var _a2, _b2;
|
|
1290
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1291
|
+
ui.Table.Row,
|
|
1292
|
+
{
|
|
1293
|
+
className: product.product_id === ((_b2 = (_a2 = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.product_id) ? "bg-ui-bg-subtle" : void 0,
|
|
1294
|
+
children: [
|
|
1295
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.product_title }),
|
|
1296
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.units_sold.toLocaleString() }),
|
|
1297
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: product.order_count.toLocaleString() }),
|
|
1298
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatCurrency(product.revenue) })
|
|
1299
|
+
]
|
|
1300
|
+
},
|
|
1301
|
+
product.product_id
|
|
1302
|
+
);
|
|
1303
|
+
}),
|
|
1273
1304
|
!topSellersLoading && (((_b = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _b.length) ?? 0) === 0 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
|
|
1274
1305
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: "No best seller data yet." }),
|
|
1275
1306
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, {}),
|
|
@@ -13,21 +13,21 @@ function AnalyticsDashboardHeader({
|
|
|
13
13
|
actions
|
|
14
14
|
}) {
|
|
15
15
|
return /* @__PURE__ */ jsxs(Container, { className: "overflow-hidden rounded-2xl border border-ui-border-base bg-ui-bg-base p-0 shadow-sm", children: [
|
|
16
|
-
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 border-b border-ui-border-base px-5 py-4
|
|
16
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 border-b border-ui-border-base px-5 py-4 lg:flex-row lg:items-end lg:justify-between", children: [
|
|
17
17
|
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
18
18
|
/* @__PURE__ */ jsx(Heading, { level: "h2", className: "tracking-tight", children: title }),
|
|
19
19
|
description ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: description }) : null
|
|
20
20
|
] }),
|
|
21
|
-
actions ? /* @__PURE__ */ jsx("div", { className: "flex
|
|
21
|
+
actions ? /* @__PURE__ */ jsx("div", { className: "flex self-start rounded-xl border border-ui-border-base bg-ui-bg-subtle p-2 lg:self-auto", children: actions }) : null
|
|
22
22
|
] }),
|
|
23
23
|
/* @__PURE__ */ jsx("div", { className: "h-1 bg-ui-bg-subtle" })
|
|
24
24
|
] });
|
|
25
25
|
}
|
|
26
26
|
function AnalyticsStatCard({ label, value, helper }) {
|
|
27
|
-
return /* @__PURE__ */ jsx(Container, { className: "h-full rounded-2xl border border-ui-border-base bg-ui-bg-base p-
|
|
28
|
-
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-
|
|
29
|
-
/* @__PURE__ */ jsx(Heading, { level: "h2", className: "tracking-tight", children: value }),
|
|
30
|
-
helper ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-xs", children: helper }) : null
|
|
27
|
+
return /* @__PURE__ */ jsx(Container, { className: "h-full rounded-2xl border border-ui-border-base bg-ui-bg-base p-3 shadow-sm transition-shadow", children: /* @__PURE__ */ jsxs("div", { className: "flex min-h-[76px] flex-col justify-between gap-1.5", children: [
|
|
28
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-xs font-medium", children: label }),
|
|
29
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", className: "text-2xl leading-none tracking-tight", children: value }),
|
|
30
|
+
helper ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-xs leading-4", children: helper }) : null
|
|
31
31
|
] }) });
|
|
32
32
|
}
|
|
33
33
|
function AnalyticsSection({
|
|
@@ -47,7 +47,7 @@ function AnalyticsSection({
|
|
|
47
47
|
/* @__PURE__ */ jsx(Heading, { level: "h3", className: "tracking-tight", children: title }),
|
|
48
48
|
description ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: description }) : null
|
|
49
49
|
] }),
|
|
50
|
-
actions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-xl border border-ui-border-base bg-ui-bg-subtle
|
|
50
|
+
actions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2 rounded-xl border border-ui-border-base bg-ui-bg-subtle px-3 py-2", children: actions }) : null
|
|
51
51
|
] }),
|
|
52
52
|
/* @__PURE__ */ jsx("div", { className: "px-5 py-4", children })
|
|
53
53
|
]
|
|
@@ -745,6 +745,7 @@ const TOP_SELLER_PERIODS = [
|
|
|
745
745
|
const SALES_COLOR = "var(--medusa-color-ui-fg-interactive)";
|
|
746
746
|
const VIEWS_COLOR = "#8B5CF6";
|
|
747
747
|
const REVENUE_COLOR = "#F59E0B";
|
|
748
|
+
const REVENUE_MUTED_COLOR = "#FCD34D";
|
|
748
749
|
function formatCurrency(value) {
|
|
749
750
|
return new Intl.NumberFormat("en-IN", {
|
|
750
751
|
style: "currency",
|
|
@@ -815,7 +816,8 @@ function TrendTooltip({
|
|
|
815
816
|
}
|
|
816
817
|
function ProductBarTooltip({
|
|
817
818
|
active,
|
|
818
|
-
payload
|
|
819
|
+
payload,
|
|
820
|
+
showViews = true
|
|
819
821
|
}) {
|
|
820
822
|
var _a;
|
|
821
823
|
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
@@ -843,10 +845,10 @@ function ProductBarTooltip({
|
|
|
843
845
|
"Units sold: ",
|
|
844
846
|
/* @__PURE__ */ jsx("strong", { children: Math.floor(unitsSold).toLocaleString() })
|
|
845
847
|
] }),
|
|
846
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
848
|
+
showViews ? /* @__PURE__ */ jsxs("div", { children: [
|
|
847
849
|
"Views: ",
|
|
848
850
|
/* @__PURE__ */ jsx("strong", { children: Math.floor(totalViews).toLocaleString() })
|
|
849
|
-
] }),
|
|
851
|
+
] }) : null,
|
|
850
852
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
851
853
|
"Revenue: ",
|
|
852
854
|
/* @__PURE__ */ jsx("strong", { children: formatCurrency(Number(revenue)) })
|
|
@@ -999,10 +1001,14 @@ function ProductsDashboard() {
|
|
|
999
1001
|
};
|
|
1000
1002
|
}, [salesChannelId, summaryDays]);
|
|
1001
1003
|
const topSellerChartData = useMemo(
|
|
1002
|
-
() => ((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 7).map((row) =>
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1004
|
+
() => ((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 7).map((row) => {
|
|
1005
|
+
var _a2, _b2;
|
|
1006
|
+
return {
|
|
1007
|
+
...row,
|
|
1008
|
+
isBestSeller: row.product_id === (((_b2 = (_a2 = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.product_id) ?? null),
|
|
1009
|
+
product_title: truncateLabel(row.product_title, 24)
|
|
1010
|
+
};
|
|
1011
|
+
}).reverse(),
|
|
1006
1012
|
[topSellers]
|
|
1007
1013
|
);
|
|
1008
1014
|
const topViewedChartData = useMemo(
|
|
@@ -1034,9 +1040,9 @@ function ProductsDashboard() {
|
|
|
1034
1040
|
{
|
|
1035
1041
|
title: "Products",
|
|
1036
1042
|
description: "Track units sold, revenue, best sellers, and product visit behavior in a denser, easier-to-scan layout.",
|
|
1037
|
-
actions: /* @__PURE__ */ jsxs(
|
|
1038
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
1039
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "products-channel-filter", className: "text-ui-fg-muted text-
|
|
1043
|
+
actions: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-2.5", children: [
|
|
1044
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1045
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "products-channel-filter", className: "text-ui-fg-muted text-xs font-medium", children: "Channel" }),
|
|
1040
1046
|
/* @__PURE__ */ jsxs(
|
|
1041
1047
|
"select",
|
|
1042
1048
|
{
|
|
@@ -1044,7 +1050,7 @@ function ProductsDashboard() {
|
|
|
1044
1050
|
value: salesChannelId,
|
|
1045
1051
|
onChange: (event) => setSalesChannelId(event.target.value),
|
|
1046
1052
|
disabled: filtersLoading,
|
|
1047
|
-
className: "h-
|
|
1053
|
+
className: "h-8 min-w-[150px] rounded-md border border-ui-border-base bg-ui-bg-base px-2.5 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
1048
1054
|
children: [
|
|
1049
1055
|
/* @__PURE__ */ jsx("option", { value: "all", children: "All channels" }),
|
|
1050
1056
|
salesChannels.map((channel) => /* @__PURE__ */ jsx("option", { value: channel.id, children: channel.name }, channel.id))
|
|
@@ -1052,39 +1058,41 @@ function ProductsDashboard() {
|
|
|
1052
1058
|
}
|
|
1053
1059
|
)
|
|
1054
1060
|
] }),
|
|
1055
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
1056
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "products-summary-period", className: "text-ui-fg-muted text-
|
|
1061
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
1062
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "products-summary-period", className: "text-ui-fg-muted text-xs font-medium", children: "Period" }),
|
|
1057
1063
|
/* @__PURE__ */ jsx(
|
|
1058
1064
|
"select",
|
|
1059
1065
|
{
|
|
1060
1066
|
id: "products-summary-period",
|
|
1061
1067
|
value: summaryDays,
|
|
1062
1068
|
onChange: (event) => setSummaryDays(event.target.value),
|
|
1063
|
-
className: "h-
|
|
1069
|
+
className: "h-8 min-w-[132px] rounded-md border border-ui-border-base bg-ui-bg-base px-2.5 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
1064
1070
|
children: SUMMARY_PERIODS.map((period) => /* @__PURE__ */ jsx("option", { value: period.value, children: period.label }, period.value))
|
|
1065
1071
|
}
|
|
1066
1072
|
)
|
|
1067
1073
|
] }),
|
|
1068
|
-
summaryDays !== "all" && /* @__PURE__ */
|
|
1069
|
-
"
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
"
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1074
|
+
(summaryDays !== "all" || salesChannelId !== "all") && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 self-end pb-1", children: [
|
|
1075
|
+
summaryDays !== "all" && /* @__PURE__ */ jsx(
|
|
1076
|
+
"button",
|
|
1077
|
+
{
|
|
1078
|
+
type: "button",
|
|
1079
|
+
onClick: () => setSummaryDays("all"),
|
|
1080
|
+
className: "text-xs text-ui-fg-muted transition-colors hover:text-ui-fg-base",
|
|
1081
|
+
"aria-label": "Show all products analytics",
|
|
1082
|
+
children: "Clear period"
|
|
1083
|
+
}
|
|
1084
|
+
),
|
|
1085
|
+
salesChannelId !== "all" && /* @__PURE__ */ jsx(
|
|
1086
|
+
"button",
|
|
1087
|
+
{
|
|
1088
|
+
type: "button",
|
|
1089
|
+
onClick: () => setSalesChannelId("all"),
|
|
1090
|
+
className: "text-xs text-ui-fg-muted transition-colors hover:text-ui-fg-base",
|
|
1091
|
+
"aria-label": "Show all sales channels",
|
|
1092
|
+
children: "Clear channel"
|
|
1093
|
+
}
|
|
1094
|
+
)
|
|
1095
|
+
] })
|
|
1088
1096
|
] })
|
|
1089
1097
|
}
|
|
1090
1098
|
),
|
|
@@ -1222,7 +1230,7 @@ function ProductsDashboard() {
|
|
|
1222
1230
|
AnalyticsSection,
|
|
1223
1231
|
{
|
|
1224
1232
|
title: "Best sellers",
|
|
1225
|
-
description: "
|
|
1233
|
+
description: "Top products by revenue.",
|
|
1226
1234
|
actions: /* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: TOP_SELLER_PERIODS.map((period) => /* @__PURE__ */ jsx(
|
|
1227
1235
|
Button,
|
|
1228
1236
|
{
|
|
@@ -1235,7 +1243,14 @@ function ProductsDashboard() {
|
|
|
1235
1243
|
)) }),
|
|
1236
1244
|
children: /* @__PURE__ */ jsx(AnalyticsChartSurface, { children: /* @__PURE__ */ jsx("div", { className: "h-80", children: topSellersLoading ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted", children: "Loading best sellers…" }) }) : topSellersError || !topSellers ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-danger", children: topSellersError ?? "Failed to load best sellers" }) }) : topSellerChartData.length === 0 ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted", children: "No product sales found for this range." }) }) : /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart, { data: topSellerChartData, layout: "vertical", margin: { left: 8, right: 8 }, children: [
|
|
1237
1245
|
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", horizontal: false }),
|
|
1238
|
-
/* @__PURE__ */ jsx(
|
|
1246
|
+
/* @__PURE__ */ jsx(
|
|
1247
|
+
XAxis,
|
|
1248
|
+
{
|
|
1249
|
+
type: "number",
|
|
1250
|
+
allowDecimals: false,
|
|
1251
|
+
tickFormatter: (value) => formatCurrency(Number(value))
|
|
1252
|
+
}
|
|
1253
|
+
),
|
|
1239
1254
|
/* @__PURE__ */ jsx(
|
|
1240
1255
|
YAxis,
|
|
1241
1256
|
{
|
|
@@ -1245,8 +1260,14 @@ function ProductsDashboard() {
|
|
|
1245
1260
|
tickLine: false
|
|
1246
1261
|
}
|
|
1247
1262
|
),
|
|
1248
|
-
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ProductBarTooltip, {}) }),
|
|
1249
|
-
/* @__PURE__ */ jsx(Bar, { dataKey: "
|
|
1263
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ProductBarTooltip, { showViews: false }) }),
|
|
1264
|
+
/* @__PURE__ */ jsx(Bar, { dataKey: "revenue", radius: [0, 6, 6, 0], children: topSellerChartData.map((entry) => /* @__PURE__ */ jsx(
|
|
1265
|
+
Cell,
|
|
1266
|
+
{
|
|
1267
|
+
fill: entry.isBestSeller ? REVENUE_COLOR : REVENUE_MUTED_COLOR
|
|
1268
|
+
},
|
|
1269
|
+
entry.product_id
|
|
1270
|
+
)) })
|
|
1250
1271
|
] }) }) }) })
|
|
1251
1272
|
}
|
|
1252
1273
|
),
|
|
@@ -1254,7 +1275,7 @@ function ProductsDashboard() {
|
|
|
1254
1275
|
AnalyticsSection,
|
|
1255
1276
|
{
|
|
1256
1277
|
title: "Top seller breakdown",
|
|
1257
|
-
description: "
|
|
1278
|
+
description: "Products ranked by revenue.",
|
|
1258
1279
|
children: /* @__PURE__ */ jsx(AnalyticsTableSurface, { children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
1259
1280
|
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1260
1281
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Product" }),
|
|
@@ -1263,12 +1284,22 @@ function ProductsDashboard() {
|
|
|
1263
1284
|
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Revenue" })
|
|
1264
1285
|
] }) }),
|
|
1265
1286
|
/* @__PURE__ */ jsxs(Table.Body, { children: [
|
|
1266
|
-
((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 8).map((product) =>
|
|
1267
|
-
|
|
1268
|
-
/* @__PURE__ */
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1287
|
+
((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 8).map((product) => {
|
|
1288
|
+
var _a2, _b2;
|
|
1289
|
+
return /* @__PURE__ */ jsxs(
|
|
1290
|
+
Table.Row,
|
|
1291
|
+
{
|
|
1292
|
+
className: product.product_id === ((_b2 = (_a2 = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _a2[0]) == null ? void 0 : _b2.product_id) ? "bg-ui-bg-subtle" : void 0,
|
|
1293
|
+
children: [
|
|
1294
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: product.product_title }),
|
|
1295
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: product.units_sold.toLocaleString() }),
|
|
1296
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: product.order_count.toLocaleString() }),
|
|
1297
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: formatCurrency(product.revenue) })
|
|
1298
|
+
]
|
|
1299
|
+
},
|
|
1300
|
+
product.product_id
|
|
1301
|
+
);
|
|
1302
|
+
}),
|
|
1272
1303
|
!topSellersLoading && (((_b = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _b.length) ?? 0) === 0 && /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1273
1304
|
/* @__PURE__ */ jsx(Table.Cell, { children: "No best seller data yet." }),
|
|
1274
1305
|
/* @__PURE__ */ jsx(Table.Cell, {}),
|
|
@@ -13,12 +13,12 @@ async function GET(req, res) {
|
|
|
13
13
|
]);
|
|
14
14
|
const products = (0, shared_1.combineSalesAndViews)(sales.products, views.products)
|
|
15
15
|
.sort((left, right) => {
|
|
16
|
-
if (right.units_sold !== left.units_sold) {
|
|
17
|
-
return right.units_sold - left.units_sold;
|
|
18
|
-
}
|
|
19
16
|
if (right.revenue !== left.revenue) {
|
|
20
17
|
return right.revenue - left.revenue;
|
|
21
18
|
}
|
|
19
|
+
if (right.units_sold !== left.units_sold) {
|
|
20
|
+
return right.units_sold - left.units_sold;
|
|
21
|
+
}
|
|
22
22
|
return right.order_count - left.order_count;
|
|
23
23
|
})
|
|
24
24
|
.slice(0, 10)
|
|
@@ -45,4 +45,4 @@ async function GET(req, res) {
|
|
|
45
45
|
});
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
48
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2FuYWx5dGljcy9wcm9kdWN0cy10b3Atc2VsbGVycy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQVdBLGtCQW1EQztBQTVERCwrQ0FPMkI7QUFFcEIsS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBa0IsRUFDbEIsR0FBbUI7SUFFbkIsTUFBTSxNQUFNLEdBQUcsSUFBQSw2QkFBb0IsRUFBQyxHQUFHLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxDQUFBO0lBQ3RELE1BQU0sY0FBYyxHQUFHLElBQUEsNEJBQW1CLEVBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxnQkFBZ0IsQ0FBQyxDQUFBO0lBQ3ZFLE1BQU0sS0FBSyxHQUFHLElBQUEsb0NBQTJCLEVBQUMsTUFBTSxDQUFDLENBQUE7SUFFakQsSUFBSSxDQUFDO1FBQ0gsTUFBTSxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUM7WUFDdkMsSUFBQSw4QkFBcUIsRUFBQyxHQUFHLEVBQUUsS0FBSyxFQUFFLGNBQWMsQ0FBQztZQUNqRCxJQUFBLDhCQUFxQixFQUFDLEdBQUcsRUFBRSxLQUFLLEVBQUUsY0FBYyxDQUFDO1NBQ2xELENBQUMsQ0FBQTtRQUVGLE1BQU0sUUFBUSxHQUEwQixJQUFBLDZCQUFvQixFQUMxRCxLQUFLLENBQUMsUUFBUSxFQUNkLEtBQUssQ0FBQyxRQUFRLENBQ2Y7YUFDRSxJQUFJLENBQUMsQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLEVBQUU7WUFDcEIsSUFBSSxLQUFLLENBQUMsT0FBTyxLQUFLLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDbkMsT0FBTyxLQUFLLENBQUMsT0FBTyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUE7WUFDckMsQ0FBQztZQUNELElBQUksS0FBSyxDQUFDLFVBQVUsS0FBSyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7Z0JBQ3pDLE9BQU8sS0FBSyxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFBO1lBQzNDLENBQUM7WUFDRCxPQUFPLEtBQUssQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQTtRQUM3QyxDQUFDLENBQUM7YUFDRCxLQUFLLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQzthQUNaLEdBQUcsQ0FBQyxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQztZQUNiLFVBQVUsRUFBRSxHQUFHLENBQUMsVUFBVTtZQUMxQixhQUFhLEVBQUUsR0FBRyxDQUFDLGFBQWE7WUFDaEMsVUFBVSxFQUFFLEdBQUcsQ0FBQyxVQUFVO1lBQzFCLE9BQU8sRUFBRSxHQUFHLENBQUMsT0FBTztZQUNwQixXQUFXLEVBQUUsR0FBRyxDQUFDLFdBQVc7WUFDNUIsV0FBVyxFQUFFLEdBQUcsQ0FBQyxXQUFXO1NBQzdCLENBQUMsQ0FBQyxDQUFBO1FBRUwsTUFBTSxJQUFJLEdBQTBCO1lBQ2xDLE1BQU07WUFDTixRQUFRO1lBQ1IscUJBQXFCLEVBQUUsS0FBSyxDQUFDLFNBQVM7U0FDdkMsQ0FBQTtRQUVELEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUE7SUFDaEIsQ0FBQztJQUFDLE9BQU8sR0FBRyxFQUFFLENBQUM7UUFDYixPQUFPLENBQUMsS0FBSyxDQUFDLHVDQUF1QyxFQUFFLEdBQUcsQ0FBQyxDQUFBO1FBQzNELEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDO1lBQ25CLEtBQUssRUFBRSxxQ0FBcUM7WUFDNUMsT0FBTyxFQUFFLEdBQUcsWUFBWSxLQUFLLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7U0FDMUQsQ0FBQyxDQUFBO0lBQ0osQ0FBQztBQUNILENBQUMifQ==
|