medusa-analytics 0.0.13 → 0.0.14
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 +594 -9
- package/.medusa/server/src/admin/index.mjs +597 -12
- package/.medusa/server/src/api/admin/analytics/products/shared.js +588 -0
- package/.medusa/server/src/api/admin/analytics/products-filters/route.js +21 -0
- package/.medusa/server/src/api/admin/analytics/products-filters/types.js +3 -0
- package/.medusa/server/src/api/admin/analytics/products-over-time/route.js +24 -0
- package/.medusa/server/src/api/admin/analytics/products-over-time/types.js +3 -0
- package/.medusa/server/src/api/admin/analytics/products-performance/route.js +51 -0
- package/.medusa/server/src/api/admin/analytics/products-performance/types.js +3 -0
- package/.medusa/server/src/api/admin/analytics/products-summary/route.js +36 -0
- package/.medusa/server/src/api/admin/analytics/products-summary/types.js +3 -0
- package/.medusa/server/src/api/admin/analytics/products-top-sellers/route.js +48 -0
- package/.medusa/server/src/api/admin/analytics/products-top-sellers/types.js +3 -0
- package/README.md +45 -0
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect } from "react";
|
|
2
|
+
import { useState, useEffect, useMemo } from "react";
|
|
3
3
|
import { defineRouteConfig } from "@medusajs/admin-sdk";
|
|
4
4
|
import { ChartBar } from "@medusajs/icons";
|
|
5
|
-
import { Text, Container, Heading, Label, Button } from "@medusajs/ui";
|
|
6
|
-
import { ResponsiveContainer, PieChart, Pie, Cell, Tooltip, Legend, LineChart, CartesianGrid, XAxis, YAxis, Line } from "recharts";
|
|
5
|
+
import { Text, Container, Heading, Label, Button, Table } from "@medusajs/ui";
|
|
6
|
+
import { ResponsiveContainer, PieChart, Pie, Cell, Tooltip, Legend, LineChart, CartesianGrid, XAxis, YAxis, Line, BarChart, Bar } from "recharts";
|
|
7
7
|
function OrdersOverTimeTooltip({
|
|
8
8
|
active,
|
|
9
9
|
payload,
|
|
@@ -47,7 +47,7 @@ const ORDER_CHART_STATUSES = [
|
|
|
47
47
|
{ key: "cancelled", label: "Cancelled", color: STATUS_COLORS.cancelled },
|
|
48
48
|
{ key: "delivered", label: "Delivered", color: STATUS_COLORS.delivered }
|
|
49
49
|
];
|
|
50
|
-
function formatCurrency(value) {
|
|
50
|
+
function formatCurrency$1(value) {
|
|
51
51
|
return new Intl.NumberFormat("en-IN", {
|
|
52
52
|
style: "currency",
|
|
53
53
|
currency: "INR",
|
|
@@ -55,7 +55,7 @@ function formatCurrency(value) {
|
|
|
55
55
|
maximumFractionDigits: 0
|
|
56
56
|
}).format(value);
|
|
57
57
|
}
|
|
58
|
-
const SUMMARY_PERIODS = [
|
|
58
|
+
const SUMMARY_PERIODS$1 = [
|
|
59
59
|
{ value: "all", label: "All time" },
|
|
60
60
|
{ value: "0", label: "Today's orders" },
|
|
61
61
|
{ value: "7", label: "Last 7 days" },
|
|
@@ -149,7 +149,7 @@ function OrdersDashboard() {
|
|
|
149
149
|
value: filter,
|
|
150
150
|
onChange: (e) => setFilter(e.target.value),
|
|
151
151
|
className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
152
|
-
children: SUMMARY_PERIODS.map((p) => /* @__PURE__ */ jsx("option", { value: p.value, children: p.label }, p.value))
|
|
152
|
+
children: SUMMARY_PERIODS$1.map((p) => /* @__PURE__ */ jsx("option", { value: p.value, children: p.label }, p.value))
|
|
153
153
|
}
|
|
154
154
|
)
|
|
155
155
|
] }),
|
|
@@ -172,11 +172,11 @@ function OrdersDashboard() {
|
|
|
172
172
|
] }),
|
|
173
173
|
/* @__PURE__ */ jsxs(Container, { className: "p-4", children: [
|
|
174
174
|
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Total revenue" }),
|
|
175
|
-
/* @__PURE__ */ jsx(Heading, { level: "h2", children: formatCurrency(data.totalRevenue) })
|
|
175
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: formatCurrency$1(data.totalRevenue) })
|
|
176
176
|
] }),
|
|
177
177
|
/* @__PURE__ */ jsxs(Container, { className: "p-4", children: [
|
|
178
178
|
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Average order value" }),
|
|
179
|
-
/* @__PURE__ */ jsx(Heading, { level: "h2", children: formatCurrency(data.aov) })
|
|
179
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: formatCurrency$1(data.aov) })
|
|
180
180
|
] }),
|
|
181
181
|
/* @__PURE__ */ jsxs(Container, { className: "p-4", children: [
|
|
182
182
|
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Orders today" }),
|
|
@@ -385,7 +385,7 @@ const COUNT_DAY_PRESETS = [
|
|
|
385
385
|
{ value: "30", label: "Last 30 days" },
|
|
386
386
|
{ value: "90", label: "Last 90 days" }
|
|
387
387
|
];
|
|
388
|
-
const GRAPH_PERIODS = [
|
|
388
|
+
const GRAPH_PERIODS$1 = [
|
|
389
389
|
{ value: "one_week", label: "One week" },
|
|
390
390
|
{ value: "one_month", label: "One month" },
|
|
391
391
|
{ value: "one_year", label: "One year" }
|
|
@@ -526,7 +526,7 @@ function CustomersDashboard() {
|
|
|
526
526
|
value: graphPeriod,
|
|
527
527
|
onChange: (e) => setGraphPeriod(e.target.value),
|
|
528
528
|
className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
529
|
-
children: GRAPH_PERIODS.map((p) => /* @__PURE__ */ jsx("option", { value: p.value, children: p.label }, p.value))
|
|
529
|
+
children: GRAPH_PERIODS$1.map((p) => /* @__PURE__ */ jsx("option", { value: p.value, children: p.label }, p.value))
|
|
530
530
|
}
|
|
531
531
|
)
|
|
532
532
|
] })
|
|
@@ -603,9 +603,593 @@ function CustomersDashboard() {
|
|
|
603
603
|
] })
|
|
604
604
|
] });
|
|
605
605
|
}
|
|
606
|
+
const SUMMARY_PERIODS = [
|
|
607
|
+
{ value: "all", label: "All time" },
|
|
608
|
+
{ value: "0", label: "Today" },
|
|
609
|
+
{ value: "7", label: "Last 7 days" },
|
|
610
|
+
{ value: "30", label: "Last 30 days" },
|
|
611
|
+
{ value: "90", label: "Last 90 days" }
|
|
612
|
+
];
|
|
613
|
+
const GRAPH_PERIODS = [
|
|
614
|
+
{ value: "one_week", label: "One week" },
|
|
615
|
+
{ value: "one_month", label: "One month" },
|
|
616
|
+
{ value: "one_year", label: "One year" }
|
|
617
|
+
];
|
|
618
|
+
const TOP_SELLER_PERIODS = [
|
|
619
|
+
{ value: "week", label: "Week" },
|
|
620
|
+
{ value: "month", label: "Month" },
|
|
621
|
+
{ value: "year", label: "Year" },
|
|
622
|
+
{ value: "all", label: "All time" }
|
|
623
|
+
];
|
|
624
|
+
const SALES_COLOR = "var(--medusa-color-ui-fg-interactive)";
|
|
625
|
+
const VIEWS_COLOR = "#8B5CF6";
|
|
626
|
+
const REVENUE_COLOR = "#F59E0B";
|
|
627
|
+
function formatCurrency(value) {
|
|
628
|
+
return new Intl.NumberFormat("en-IN", {
|
|
629
|
+
style: "currency",
|
|
630
|
+
currency: "INR",
|
|
631
|
+
minimumFractionDigits: 0,
|
|
632
|
+
maximumFractionDigits: 0
|
|
633
|
+
}).format(value);
|
|
634
|
+
}
|
|
635
|
+
function formatRatio(value) {
|
|
636
|
+
if (value === null || Number.isNaN(value)) return "N/A";
|
|
637
|
+
return `${value.toFixed(2)}x`;
|
|
638
|
+
}
|
|
639
|
+
function truncateLabel(value, maxLength = 22) {
|
|
640
|
+
return value.length > maxLength ? `${value.slice(0, maxLength - 1)}…` : value;
|
|
641
|
+
}
|
|
642
|
+
function buildAnalyticsUrl(path, params) {
|
|
643
|
+
const search = new URLSearchParams();
|
|
644
|
+
for (const [key, value] of Object.entries(params)) {
|
|
645
|
+
if (value) {
|
|
646
|
+
search.set(key, value);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
const query = search.toString();
|
|
650
|
+
return query ? `${path}?${query}` : path;
|
|
651
|
+
}
|
|
652
|
+
function TrendTooltip({
|
|
653
|
+
active,
|
|
654
|
+
payload,
|
|
655
|
+
label
|
|
656
|
+
}) {
|
|
657
|
+
var _a, _b, _c, _d;
|
|
658
|
+
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
659
|
+
const row = (_a = payload[0]) == null ? void 0 : _a.payload;
|
|
660
|
+
const displayLabel = (row == null ? void 0 : row.label) ?? (label ? new Date(label).toLocaleDateString() : "");
|
|
661
|
+
const unitsSold = ((_b = payload.find((entry) => entry.name === "Units sold")) == null ? void 0 : _b.value) ?? 0;
|
|
662
|
+
const views = ((_c = payload.find((entry) => entry.name === "Views")) == null ? void 0 : _c.value) ?? 0;
|
|
663
|
+
const revenue = ((_d = payload.find((entry) => entry.name === "Revenue")) == null ? void 0 : _d.value) ?? 0;
|
|
664
|
+
return /* @__PURE__ */ jsxs(
|
|
665
|
+
"div",
|
|
666
|
+
{
|
|
667
|
+
style: {
|
|
668
|
+
backgroundColor: "rgb(255, 255, 255)",
|
|
669
|
+
color: "#111827",
|
|
670
|
+
border: "1px solid rgb(229, 231, 235)",
|
|
671
|
+
borderRadius: "8px",
|
|
672
|
+
padding: "12px 16px",
|
|
673
|
+
boxShadow: "0 4px 14px rgba(0, 0, 0, 0.08)",
|
|
674
|
+
fontSize: "14px",
|
|
675
|
+
lineHeight: 1.5
|
|
676
|
+
},
|
|
677
|
+
children: [
|
|
678
|
+
/* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: "6px" }, children: displayLabel }),
|
|
679
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
680
|
+
"Units sold: ",
|
|
681
|
+
/* @__PURE__ */ jsx("strong", { children: Math.floor(unitsSold).toLocaleString() })
|
|
682
|
+
] }),
|
|
683
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
684
|
+
"Views: ",
|
|
685
|
+
/* @__PURE__ */ jsx("strong", { children: Math.floor(views).toLocaleString() })
|
|
686
|
+
] }),
|
|
687
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
688
|
+
"Revenue: ",
|
|
689
|
+
/* @__PURE__ */ jsx("strong", { children: formatCurrency(Number(revenue)) })
|
|
690
|
+
] })
|
|
691
|
+
]
|
|
692
|
+
}
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
function ProductBarTooltip({
|
|
696
|
+
active,
|
|
697
|
+
payload
|
|
698
|
+
}) {
|
|
699
|
+
var _a;
|
|
700
|
+
if (!active || !(payload == null ? void 0 : payload.length)) return null;
|
|
701
|
+
const row = (_a = payload[0]) == null ? void 0 : _a.payload;
|
|
702
|
+
if (!row) return null;
|
|
703
|
+
const unitsSold = "units_sold" in row ? row.units_sold : 0;
|
|
704
|
+
const totalViews = "total_views" in row ? row.total_views : 0;
|
|
705
|
+
const revenue = "revenue" in row ? row.revenue : 0;
|
|
706
|
+
return /* @__PURE__ */ jsxs(
|
|
707
|
+
"div",
|
|
708
|
+
{
|
|
709
|
+
style: {
|
|
710
|
+
backgroundColor: "rgb(255, 255, 255)",
|
|
711
|
+
color: "#111827",
|
|
712
|
+
border: "1px solid rgb(229, 231, 235)",
|
|
713
|
+
borderRadius: "8px",
|
|
714
|
+
padding: "12px 16px",
|
|
715
|
+
boxShadow: "0 4px 14px rgba(0, 0, 0, 0.08)",
|
|
716
|
+
fontSize: "14px",
|
|
717
|
+
lineHeight: 1.5
|
|
718
|
+
},
|
|
719
|
+
children: [
|
|
720
|
+
/* @__PURE__ */ jsx("div", { style: { fontWeight: 600, marginBottom: "6px" }, children: row.product_title }),
|
|
721
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
722
|
+
"Units sold: ",
|
|
723
|
+
/* @__PURE__ */ jsx("strong", { children: Math.floor(unitsSold).toLocaleString() })
|
|
724
|
+
] }),
|
|
725
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
726
|
+
"Views: ",
|
|
727
|
+
/* @__PURE__ */ jsx("strong", { children: Math.floor(totalViews).toLocaleString() })
|
|
728
|
+
] }),
|
|
729
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
730
|
+
"Revenue: ",
|
|
731
|
+
/* @__PURE__ */ jsx("strong", { children: formatCurrency(Number(revenue)) })
|
|
732
|
+
] })
|
|
733
|
+
]
|
|
734
|
+
}
|
|
735
|
+
);
|
|
736
|
+
}
|
|
737
|
+
function ProductsDashboard() {
|
|
738
|
+
var _a, _b, _c;
|
|
739
|
+
const [summaryDays, setSummaryDays] = useState("all");
|
|
740
|
+
const [graphPeriod, setGraphPeriod] = useState("one_week");
|
|
741
|
+
const [topSellerPeriod, setTopSellerPeriod] = useState("week");
|
|
742
|
+
const [salesChannelId, setSalesChannelId] = useState("all");
|
|
743
|
+
const [salesChannels, setSalesChannels] = useState([]);
|
|
744
|
+
const [filtersLoading, setFiltersLoading] = useState(true);
|
|
745
|
+
const [filtersError, setFiltersError] = useState(null);
|
|
746
|
+
const [summary, setSummary] = useState(null);
|
|
747
|
+
const [summaryLoading, setSummaryLoading] = useState(true);
|
|
748
|
+
const [summaryError, setSummaryError] = useState(null);
|
|
749
|
+
const [overTime, setOverTime] = useState(null);
|
|
750
|
+
const [overTimeLoading, setOverTimeLoading] = useState(true);
|
|
751
|
+
const [overTimeError, setOverTimeError] = useState(null);
|
|
752
|
+
const [topSellers, setTopSellers] = useState(null);
|
|
753
|
+
const [topSellersLoading, setTopSellersLoading] = useState(true);
|
|
754
|
+
const [topSellersError, setTopSellersError] = useState(null);
|
|
755
|
+
const [performance, setPerformance] = useState(null);
|
|
756
|
+
const [performanceLoading, setPerformanceLoading] = useState(true);
|
|
757
|
+
const [performanceError, setPerformanceError] = useState(null);
|
|
758
|
+
useEffect(() => {
|
|
759
|
+
let cancelled = false;
|
|
760
|
+
setFiltersLoading(true);
|
|
761
|
+
setFiltersError(null);
|
|
762
|
+
fetch("/admin/analytics/products-filters").then((res) => {
|
|
763
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
764
|
+
return res.json();
|
|
765
|
+
}).then((body) => {
|
|
766
|
+
if (!cancelled) {
|
|
767
|
+
setSalesChannels(body.salesChannels ?? []);
|
|
768
|
+
}
|
|
769
|
+
}).catch((error) => {
|
|
770
|
+
if (!cancelled) {
|
|
771
|
+
setFiltersError(error instanceof Error ? error.message : String(error));
|
|
772
|
+
}
|
|
773
|
+
}).finally(() => {
|
|
774
|
+
if (!cancelled) setFiltersLoading(false);
|
|
775
|
+
});
|
|
776
|
+
return () => {
|
|
777
|
+
cancelled = true;
|
|
778
|
+
};
|
|
779
|
+
}, []);
|
|
780
|
+
useEffect(() => {
|
|
781
|
+
let cancelled = false;
|
|
782
|
+
setSummaryLoading(true);
|
|
783
|
+
setSummaryError(null);
|
|
784
|
+
fetch(
|
|
785
|
+
buildAnalyticsUrl("/admin/analytics/products-summary", {
|
|
786
|
+
days: summaryDays,
|
|
787
|
+
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null
|
|
788
|
+
})
|
|
789
|
+
).then((res) => {
|
|
790
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
791
|
+
return res.json();
|
|
792
|
+
}).then((body) => {
|
|
793
|
+
if (!cancelled) setSummary(body);
|
|
794
|
+
}).catch((error) => {
|
|
795
|
+
if (!cancelled) {
|
|
796
|
+
setSummaryError(error instanceof Error ? error.message : String(error));
|
|
797
|
+
}
|
|
798
|
+
}).finally(() => {
|
|
799
|
+
if (!cancelled) setSummaryLoading(false);
|
|
800
|
+
});
|
|
801
|
+
return () => {
|
|
802
|
+
cancelled = true;
|
|
803
|
+
};
|
|
804
|
+
}, [salesChannelId, summaryDays]);
|
|
805
|
+
useEffect(() => {
|
|
806
|
+
let cancelled = false;
|
|
807
|
+
setOverTimeLoading(true);
|
|
808
|
+
setOverTimeError(null);
|
|
809
|
+
fetch(
|
|
810
|
+
buildAnalyticsUrl("/admin/analytics/products-over-time", {
|
|
811
|
+
period: graphPeriod,
|
|
812
|
+
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null
|
|
813
|
+
})
|
|
814
|
+
).then((res) => {
|
|
815
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
816
|
+
return res.json();
|
|
817
|
+
}).then((body) => {
|
|
818
|
+
if (!cancelled) setOverTime(body);
|
|
819
|
+
}).catch((error) => {
|
|
820
|
+
if (!cancelled) {
|
|
821
|
+
setOverTimeError(error instanceof Error ? error.message : String(error));
|
|
822
|
+
}
|
|
823
|
+
}).finally(() => {
|
|
824
|
+
if (!cancelled) setOverTimeLoading(false);
|
|
825
|
+
});
|
|
826
|
+
return () => {
|
|
827
|
+
cancelled = true;
|
|
828
|
+
};
|
|
829
|
+
}, [graphPeriod, salesChannelId]);
|
|
830
|
+
useEffect(() => {
|
|
831
|
+
let cancelled = false;
|
|
832
|
+
setTopSellersLoading(true);
|
|
833
|
+
setTopSellersError(null);
|
|
834
|
+
fetch(
|
|
835
|
+
buildAnalyticsUrl("/admin/analytics/products-top-sellers", {
|
|
836
|
+
period: topSellerPeriod,
|
|
837
|
+
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null
|
|
838
|
+
})
|
|
839
|
+
).then((res) => {
|
|
840
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
841
|
+
return res.json();
|
|
842
|
+
}).then((body) => {
|
|
843
|
+
if (!cancelled) setTopSellers(body);
|
|
844
|
+
}).catch((error) => {
|
|
845
|
+
if (!cancelled) {
|
|
846
|
+
setTopSellersError(error instanceof Error ? error.message : String(error));
|
|
847
|
+
}
|
|
848
|
+
}).finally(() => {
|
|
849
|
+
if (!cancelled) setTopSellersLoading(false);
|
|
850
|
+
});
|
|
851
|
+
return () => {
|
|
852
|
+
cancelled = true;
|
|
853
|
+
};
|
|
854
|
+
}, [salesChannelId, topSellerPeriod]);
|
|
855
|
+
useEffect(() => {
|
|
856
|
+
let cancelled = false;
|
|
857
|
+
setPerformanceLoading(true);
|
|
858
|
+
setPerformanceError(null);
|
|
859
|
+
fetch(
|
|
860
|
+
buildAnalyticsUrl("/admin/analytics/products-performance", {
|
|
861
|
+
days: summaryDays,
|
|
862
|
+
sales_channel_id: salesChannelId !== "all" ? salesChannelId : null
|
|
863
|
+
})
|
|
864
|
+
).then((res) => {
|
|
865
|
+
if (!res.ok) throw new Error(res.statusText);
|
|
866
|
+
return res.json();
|
|
867
|
+
}).then((body) => {
|
|
868
|
+
if (!cancelled) setPerformance(body);
|
|
869
|
+
}).catch((error) => {
|
|
870
|
+
if (!cancelled) {
|
|
871
|
+
setPerformanceError(error instanceof Error ? error.message : String(error));
|
|
872
|
+
}
|
|
873
|
+
}).finally(() => {
|
|
874
|
+
if (!cancelled) setPerformanceLoading(false);
|
|
875
|
+
});
|
|
876
|
+
return () => {
|
|
877
|
+
cancelled = true;
|
|
878
|
+
};
|
|
879
|
+
}, [salesChannelId, summaryDays]);
|
|
880
|
+
const topSellerChartData = useMemo(
|
|
881
|
+
() => ((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 7).map((row) => ({
|
|
882
|
+
...row,
|
|
883
|
+
product_title: truncateLabel(row.product_title, 24)
|
|
884
|
+
})).reverse(),
|
|
885
|
+
[topSellers]
|
|
886
|
+
);
|
|
887
|
+
const topViewedChartData = useMemo(
|
|
888
|
+
() => ((performance == null ? void 0 : performance.topViewedProducts) ?? []).slice(0, 7).map((row) => ({
|
|
889
|
+
...row,
|
|
890
|
+
product_title: truncateLabel(row.product_title, 24)
|
|
891
|
+
})).reverse(),
|
|
892
|
+
[performance]
|
|
893
|
+
);
|
|
894
|
+
if (summaryLoading) {
|
|
895
|
+
return /* @__PURE__ */ jsx(
|
|
896
|
+
"div",
|
|
897
|
+
{
|
|
898
|
+
className: "flex items-center justify-center min-h-[320px]",
|
|
899
|
+
role: "status",
|
|
900
|
+
"aria-label": "Loading product analytics",
|
|
901
|
+
children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted", children: "Loading…" })
|
|
902
|
+
}
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
if (summaryError || !summary) {
|
|
906
|
+
return /* @__PURE__ */ jsx(Container, { className: "p-6", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-danger", children: summaryError ?? "Failed to load product analytics" }) });
|
|
907
|
+
}
|
|
908
|
+
const viewsConnected = summary.productViewsConnected || (overTime == null ? void 0 : overTime.productViewsConnected) === true || (topSellers == null ? void 0 : topSellers.productViewsConnected) === true || (performance == null ? void 0 : performance.productViewsConnected) === true;
|
|
909
|
+
const selectedChannelLabel = salesChannelId === "all" ? "All channels" : ((_a = salesChannels.find((channel) => channel.id === salesChannelId)) == null ? void 0 : _a.name) ?? "Selected channel";
|
|
910
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-8", children: [
|
|
911
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [
|
|
912
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
913
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Products" }),
|
|
914
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm mt-1", children: "Track units sold, revenue, best sellers, and product visit behavior." })
|
|
915
|
+
] }),
|
|
916
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
917
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
918
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "products-channel-filter", className: "text-ui-fg-muted text-sm", children: "Channel" }),
|
|
919
|
+
/* @__PURE__ */ jsxs(
|
|
920
|
+
"select",
|
|
921
|
+
{
|
|
922
|
+
id: "products-channel-filter",
|
|
923
|
+
value: salesChannelId,
|
|
924
|
+
onChange: (event) => setSalesChannelId(event.target.value),
|
|
925
|
+
disabled: filtersLoading,
|
|
926
|
+
className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
927
|
+
children: [
|
|
928
|
+
/* @__PURE__ */ jsx("option", { value: "all", children: "All channels" }),
|
|
929
|
+
salesChannels.map((channel) => /* @__PURE__ */ jsx("option", { value: channel.id, children: channel.name }, channel.id))
|
|
930
|
+
]
|
|
931
|
+
}
|
|
932
|
+
)
|
|
933
|
+
] }),
|
|
934
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
935
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "products-summary-period", className: "text-ui-fg-muted text-sm", children: "Period" }),
|
|
936
|
+
/* @__PURE__ */ jsx(
|
|
937
|
+
"select",
|
|
938
|
+
{
|
|
939
|
+
id: "products-summary-period",
|
|
940
|
+
value: summaryDays,
|
|
941
|
+
onChange: (event) => setSummaryDays(event.target.value),
|
|
942
|
+
className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
943
|
+
children: SUMMARY_PERIODS.map((period) => /* @__PURE__ */ jsx("option", { value: period.value, children: period.label }, period.value))
|
|
944
|
+
}
|
|
945
|
+
)
|
|
946
|
+
] }),
|
|
947
|
+
summaryDays !== "all" && /* @__PURE__ */ jsx(
|
|
948
|
+
"button",
|
|
949
|
+
{
|
|
950
|
+
type: "button",
|
|
951
|
+
onClick: () => setSummaryDays("all"),
|
|
952
|
+
className: "text-xs text-ui-fg-muted hover:text-ui-fg-base transition-colors",
|
|
953
|
+
"aria-label": "Show all products analytics",
|
|
954
|
+
children: "Clear filter"
|
|
955
|
+
}
|
|
956
|
+
),
|
|
957
|
+
salesChannelId !== "all" && /* @__PURE__ */ jsx(
|
|
958
|
+
"button",
|
|
959
|
+
{
|
|
960
|
+
type: "button",
|
|
961
|
+
onClick: () => setSalesChannelId("all"),
|
|
962
|
+
className: "text-xs text-ui-fg-muted hover:text-ui-fg-base transition-colors",
|
|
963
|
+
"aria-label": "Show all sales channels",
|
|
964
|
+
children: "Clear channel"
|
|
965
|
+
}
|
|
966
|
+
)
|
|
967
|
+
] })
|
|
968
|
+
] }),
|
|
969
|
+
filtersError && /* @__PURE__ */ jsx(Container, { className: "p-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-danger text-sm", children: filtersError }) }),
|
|
970
|
+
!viewsConnected && /* @__PURE__ */ jsx(Container, { className: "p-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Product views are unavailable until the `medusa-product-helper` tracking module is registered and storefront page views are being recorded." }) }),
|
|
971
|
+
salesChannelId !== "all" && viewsConnected && /* @__PURE__ */ jsx(Container, { className: "p-4", children: /* @__PURE__ */ jsxs(Text, { className: "text-ui-fg-muted text-sm", children: [
|
|
972
|
+
"Showing product analytics for ",
|
|
973
|
+
/* @__PURE__ */ jsx("strong", { children: selectedChannelLabel }),
|
|
974
|
+
". Product views are scoped to products assigned to this sales channel."
|
|
975
|
+
] }) }),
|
|
976
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-4", children: [
|
|
977
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-4", children: [
|
|
978
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Units sold" }),
|
|
979
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: summary.unitsSold.toLocaleString() })
|
|
980
|
+
] }),
|
|
981
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-4", children: [
|
|
982
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Product revenue" }),
|
|
983
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: formatCurrency(summary.productRevenue) })
|
|
984
|
+
] }),
|
|
985
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-4", children: [
|
|
986
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Orders with product sales" }),
|
|
987
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: summary.ordersWithProducts.toLocaleString() })
|
|
988
|
+
] }),
|
|
989
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-4", children: [
|
|
990
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Active products sold" }),
|
|
991
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: summary.activeProductsSold.toLocaleString() })
|
|
992
|
+
] }),
|
|
993
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-4", children: [
|
|
994
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "Product views" }),
|
|
995
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: summary.totalProductViews.toLocaleString() })
|
|
996
|
+
] }),
|
|
997
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-4", children: [
|
|
998
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm", children: "View / order ratio" }),
|
|
999
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: formatRatio(summary.viewToOrderRatio) })
|
|
1000
|
+
] })
|
|
1001
|
+
] }),
|
|
1002
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-6", children: [
|
|
1003
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 mb-4", children: [
|
|
1004
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1005
|
+
/* @__PURE__ */ jsx(Heading, { level: "h3", children: "Sales and views over time" }),
|
|
1006
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm mt-1", children: "Units sold and revenue are always shown. Product views appear when tracking data is available." })
|
|
1007
|
+
] }),
|
|
1008
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1009
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "products-graph-period", className: "text-ui-fg-muted text-sm", children: "Range" }),
|
|
1010
|
+
/* @__PURE__ */ jsx(
|
|
1011
|
+
"select",
|
|
1012
|
+
{
|
|
1013
|
+
id: "products-graph-period",
|
|
1014
|
+
value: graphPeriod,
|
|
1015
|
+
onChange: (event) => setGraphPeriod(event.target.value),
|
|
1016
|
+
className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive",
|
|
1017
|
+
children: GRAPH_PERIODS.map((period) => /* @__PURE__ */ jsx("option", { value: period.value, children: period.label }, period.value))
|
|
1018
|
+
}
|
|
1019
|
+
)
|
|
1020
|
+
] })
|
|
1021
|
+
] }),
|
|
1022
|
+
/* @__PURE__ */ jsx("div", { className: "h-80", children: overTimeLoading ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted", children: "Loading chart…" }) }) : overTimeError || !overTime ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-danger", children: overTimeError ?? "Failed to load product trend" }) }) : /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(LineChart, { data: overTime.series, children: [
|
|
1023
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", vertical: false }),
|
|
1024
|
+
/* @__PURE__ */ jsx(XAxis, { dataKey: "label" }),
|
|
1025
|
+
/* @__PURE__ */ jsx(
|
|
1026
|
+
YAxis,
|
|
1027
|
+
{
|
|
1028
|
+
yAxisId: "counts",
|
|
1029
|
+
allowDecimals: false,
|
|
1030
|
+
tickFormatter: (value) => Math.floor(Number(value)).toLocaleString()
|
|
1031
|
+
}
|
|
1032
|
+
),
|
|
1033
|
+
/* @__PURE__ */ jsx(
|
|
1034
|
+
YAxis,
|
|
1035
|
+
{
|
|
1036
|
+
yAxisId: "revenue",
|
|
1037
|
+
orientation: "right",
|
|
1038
|
+
tickFormatter: (value) => formatCurrency(Number(value))
|
|
1039
|
+
}
|
|
1040
|
+
),
|
|
1041
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(TrendTooltip, {}) }),
|
|
1042
|
+
/* @__PURE__ */ jsx(Legend, {}),
|
|
1043
|
+
/* @__PURE__ */ jsx(
|
|
1044
|
+
Line,
|
|
1045
|
+
{
|
|
1046
|
+
yAxisId: "counts",
|
|
1047
|
+
type: "monotone",
|
|
1048
|
+
dataKey: "units_sold",
|
|
1049
|
+
name: "Units sold",
|
|
1050
|
+
stroke: SALES_COLOR,
|
|
1051
|
+
strokeWidth: 2,
|
|
1052
|
+
dot: false
|
|
1053
|
+
}
|
|
1054
|
+
),
|
|
1055
|
+
/* @__PURE__ */ jsx(
|
|
1056
|
+
Line,
|
|
1057
|
+
{
|
|
1058
|
+
yAxisId: "counts",
|
|
1059
|
+
type: "monotone",
|
|
1060
|
+
dataKey: "views",
|
|
1061
|
+
name: "Views",
|
|
1062
|
+
stroke: VIEWS_COLOR,
|
|
1063
|
+
strokeWidth: 2,
|
|
1064
|
+
dot: false
|
|
1065
|
+
}
|
|
1066
|
+
),
|
|
1067
|
+
/* @__PURE__ */ jsx(
|
|
1068
|
+
Line,
|
|
1069
|
+
{
|
|
1070
|
+
yAxisId: "revenue",
|
|
1071
|
+
type: "monotone",
|
|
1072
|
+
dataKey: "revenue",
|
|
1073
|
+
name: "Revenue",
|
|
1074
|
+
stroke: REVENUE_COLOR,
|
|
1075
|
+
strokeWidth: 2,
|
|
1076
|
+
dot: false
|
|
1077
|
+
}
|
|
1078
|
+
)
|
|
1079
|
+
] }) }) })
|
|
1080
|
+
] }),
|
|
1081
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 xl:grid-cols-2 gap-4", children: [
|
|
1082
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-6", children: [
|
|
1083
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4 mb-4", children: [
|
|
1084
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1085
|
+
/* @__PURE__ */ jsx(Heading, { level: "h3", children: "Best sellers" }),
|
|
1086
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm mt-1", children: "Ranked by units sold." })
|
|
1087
|
+
] }),
|
|
1088
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap items-center gap-2", children: TOP_SELLER_PERIODS.map((period) => /* @__PURE__ */ jsx(
|
|
1089
|
+
Button,
|
|
1090
|
+
{
|
|
1091
|
+
variant: topSellerPeriod === period.value ? "secondary" : "transparent",
|
|
1092
|
+
size: "small",
|
|
1093
|
+
onClick: () => setTopSellerPeriod(period.value),
|
|
1094
|
+
children: period.label
|
|
1095
|
+
},
|
|
1096
|
+
period.value
|
|
1097
|
+
)) })
|
|
1098
|
+
] }),
|
|
1099
|
+
/* @__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: [
|
|
1100
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", horizontal: false }),
|
|
1101
|
+
/* @__PURE__ */ jsx(XAxis, { type: "number", allowDecimals: false }),
|
|
1102
|
+
/* @__PURE__ */ jsx(
|
|
1103
|
+
YAxis,
|
|
1104
|
+
{
|
|
1105
|
+
type: "category",
|
|
1106
|
+
dataKey: "product_title",
|
|
1107
|
+
width: 160,
|
|
1108
|
+
tickLine: false
|
|
1109
|
+
}
|
|
1110
|
+
),
|
|
1111
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ProductBarTooltip, {}) }),
|
|
1112
|
+
/* @__PURE__ */ jsx(Bar, { dataKey: "units_sold", fill: SALES_COLOR, radius: [0, 6, 6, 0] })
|
|
1113
|
+
] }) }) })
|
|
1114
|
+
] }),
|
|
1115
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-6", children: [
|
|
1116
|
+
/* @__PURE__ */ jsx(Heading, { level: "h3", className: "mb-4", children: "Top seller breakdown" }),
|
|
1117
|
+
/* @__PURE__ */ jsxs(Table, { children: [
|
|
1118
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1119
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Product" }),
|
|
1120
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Units" }),
|
|
1121
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Orders" }),
|
|
1122
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Revenue" })
|
|
1123
|
+
] }) }),
|
|
1124
|
+
/* @__PURE__ */ jsxs(Table.Body, { children: [
|
|
1125
|
+
((topSellers == null ? void 0 : topSellers.products) ?? []).slice(0, 8).map((product) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1126
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: product.product_title }),
|
|
1127
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: product.units_sold.toLocaleString() }),
|
|
1128
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: product.order_count.toLocaleString() }),
|
|
1129
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: formatCurrency(product.revenue) })
|
|
1130
|
+
] }, product.product_id)),
|
|
1131
|
+
!topSellersLoading && (((_b = topSellers == null ? void 0 : topSellers.products) == null ? void 0 : _b.length) ?? 0) === 0 && /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1132
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: "No best seller data yet." }),
|
|
1133
|
+
/* @__PURE__ */ jsx(Table.Cell, {}),
|
|
1134
|
+
/* @__PURE__ */ jsx(Table.Cell, {}),
|
|
1135
|
+
/* @__PURE__ */ jsx(Table.Cell, {})
|
|
1136
|
+
] })
|
|
1137
|
+
] })
|
|
1138
|
+
] })
|
|
1139
|
+
] })
|
|
1140
|
+
] }),
|
|
1141
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 xl:grid-cols-2 gap-4", children: [
|
|
1142
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-6", children: [
|
|
1143
|
+
/* @__PURE__ */ jsx(Heading, { level: "h3", className: "mb-1", children: "Most viewed products" }),
|
|
1144
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-muted text-sm mb-4", children: "View activity is sourced from the existing product-helper tracking module." }),
|
|
1145
|
+
/* @__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 product views…" }) }) : 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." }) }) : topViewedChartData.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 views found for this range." }) }) : /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", children: /* @__PURE__ */ jsxs(BarChart, { data: topViewedChartData, layout: "vertical", margin: { left: 8, right: 8 }, children: [
|
|
1146
|
+
/* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", horizontal: false }),
|
|
1147
|
+
/* @__PURE__ */ jsx(XAxis, { type: "number", allowDecimals: false }),
|
|
1148
|
+
/* @__PURE__ */ jsx(
|
|
1149
|
+
YAxis,
|
|
1150
|
+
{
|
|
1151
|
+
type: "category",
|
|
1152
|
+
dataKey: "product_title",
|
|
1153
|
+
width: 160,
|
|
1154
|
+
tickLine: false
|
|
1155
|
+
}
|
|
1156
|
+
),
|
|
1157
|
+
/* @__PURE__ */ jsx(Tooltip, { content: /* @__PURE__ */ jsx(ProductBarTooltip, {}) }),
|
|
1158
|
+
/* @__PURE__ */ jsx(Bar, { dataKey: "total_views", fill: VIEWS_COLOR, radius: [0, 6, 6, 0] })
|
|
1159
|
+
] }) }) })
|
|
1160
|
+
] }),
|
|
1161
|
+
/* @__PURE__ */ jsxs(Container, { className: "p-6", children: [
|
|
1162
|
+
/* @__PURE__ */ jsx(Heading, { level: "h3", className: "mb-4", children: "View-to-sales opportunities" }),
|
|
1163
|
+
/* @__PURE__ */ jsxs(Table, { children: [
|
|
1164
|
+
/* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1165
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Product" }),
|
|
1166
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Views" }),
|
|
1167
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Units" }),
|
|
1168
|
+
/* @__PURE__ */ jsx(Table.HeaderCell, { children: "Views / unit" })
|
|
1169
|
+
] }) }),
|
|
1170
|
+
/* @__PURE__ */ jsxs(Table.Body, { children: [
|
|
1171
|
+
((performance == null ? void 0 : performance.viewOpportunities) ?? []).slice(0, 8).map((product) => /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1172
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: product.product_title }),
|
|
1173
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: product.total_views.toLocaleString() }),
|
|
1174
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: product.units_sold.toLocaleString() }),
|
|
1175
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: formatRatio(product.views_per_unit) })
|
|
1176
|
+
] }, product.product_id)),
|
|
1177
|
+
!performanceLoading && !performanceError && (((_c = performance == null ? void 0 : performance.viewOpportunities) == null ? void 0 : _c.length) ?? 0) === 0 && /* @__PURE__ */ jsxs(Table.Row, { children: [
|
|
1178
|
+
/* @__PURE__ */ jsx(Table.Cell, { children: "No product view opportunities yet." }),
|
|
1179
|
+
/* @__PURE__ */ jsx(Table.Cell, {}),
|
|
1180
|
+
/* @__PURE__ */ jsx(Table.Cell, {}),
|
|
1181
|
+
/* @__PURE__ */ jsx(Table.Cell, {})
|
|
1182
|
+
] })
|
|
1183
|
+
] })
|
|
1184
|
+
] })
|
|
1185
|
+
] })
|
|
1186
|
+
] })
|
|
1187
|
+
] });
|
|
1188
|
+
}
|
|
606
1189
|
const ANALYTICS_MODULES = [
|
|
607
1190
|
{ id: "orders", label: "Orders" },
|
|
608
|
-
{ id: "customers", label: "Customers" }
|
|
1191
|
+
{ id: "customers", label: "Customers" },
|
|
1192
|
+
{ id: "products", label: "Products" }
|
|
609
1193
|
];
|
|
610
1194
|
const AnalyticsPage = () => {
|
|
611
1195
|
const [activeModule, setActiveModule] = useState("orders");
|
|
@@ -622,7 +1206,8 @@ const AnalyticsPage = () => {
|
|
|
622
1206
|
m.id
|
|
623
1207
|
)) }),
|
|
624
1208
|
activeModule === "orders" && /* @__PURE__ */ jsx(OrdersDashboard, {}),
|
|
625
|
-
activeModule === "customers" && /* @__PURE__ */ jsx(CustomersDashboard, {})
|
|
1209
|
+
activeModule === "customers" && /* @__PURE__ */ jsx(CustomersDashboard, {}),
|
|
1210
|
+
activeModule === "products" && /* @__PURE__ */ jsx(ProductsDashboard, {})
|
|
626
1211
|
] }) }) }) });
|
|
627
1212
|
};
|
|
628
1213
|
const config = defineRouteConfig({
|