medusa-product-helper 0.0.5 → 0.0.9
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 +127 -0
- package/.medusa/server/src/admin/index.mjs +127 -0
- package/.medusa/server/src/api/admin/wishlist/stats/route.js +13 -1
- package/.medusa/server/src/api/store/wishlist/[product_id]/route.js +8 -3
- package/.medusa/server/src/api/store/wishlist/route.js +15 -5
- package/.medusa/server/src/shared/wishlist/helper.js +95 -37
- package/.medusa/server/src/shared/wishlist/index.js +8 -2
- package/README.md +75 -37
- package/package.json +1 -1
|
@@ -988,6 +988,129 @@ const ValueField = ({
|
|
|
988
988
|
adminSdk.defineWidgetConfig({
|
|
989
989
|
zone: "product.details.after"
|
|
990
990
|
});
|
|
991
|
+
const fetchJson = async (path) => {
|
|
992
|
+
const response = await fetch(path, {
|
|
993
|
+
credentials: "include"
|
|
994
|
+
});
|
|
995
|
+
const payload = await response.json().catch(() => null);
|
|
996
|
+
if (!response.ok) {
|
|
997
|
+
throw new Error(
|
|
998
|
+
(payload == null ? void 0 : payload.message) ?? "Unable to load wishlist statistics from the server"
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
return payload;
|
|
1002
|
+
};
|
|
1003
|
+
const ProductWishlistStatsWidget = ({ data }) => {
|
|
1004
|
+
const productId = data == null ? void 0 : data.id;
|
|
1005
|
+
const {
|
|
1006
|
+
data: productStats,
|
|
1007
|
+
isPending: isProductStatsPending,
|
|
1008
|
+
isError: isProductStatsError,
|
|
1009
|
+
error: productStatsError
|
|
1010
|
+
} = reactQuery.useQuery({
|
|
1011
|
+
queryKey: ["wishlist", "product", productId],
|
|
1012
|
+
enabled: Boolean(productId),
|
|
1013
|
+
queryFn: () => fetchJson(
|
|
1014
|
+
`/admin/wishlist/stats?product_id=${productId}`
|
|
1015
|
+
),
|
|
1016
|
+
refetchInterval: 6e4
|
|
1017
|
+
});
|
|
1018
|
+
const {
|
|
1019
|
+
data: allStats,
|
|
1020
|
+
isPending: isAllStatsPending,
|
|
1021
|
+
isError: isAllStatsError,
|
|
1022
|
+
error: allStatsError
|
|
1023
|
+
} = reactQuery.useQuery({
|
|
1024
|
+
queryKey: ["wishlist", "stats"],
|
|
1025
|
+
queryFn: () => fetchJson("/admin/wishlist/stats"),
|
|
1026
|
+
staleTime: 6e4
|
|
1027
|
+
});
|
|
1028
|
+
const topFive = react.useMemo(() => {
|
|
1029
|
+
var _a;
|
|
1030
|
+
if (!((_a = allStats == null ? void 0 : allStats.stats) == null ? void 0 : _a.length)) {
|
|
1031
|
+
return [];
|
|
1032
|
+
}
|
|
1033
|
+
return [...allStats.stats].sort((a, b) => b.wishlist_count - a.wishlist_count).slice(0, 5);
|
|
1034
|
+
}, [allStats]);
|
|
1035
|
+
const productWishlistCount = (productStats == null ? void 0 : productStats.wishlist_count) ?? 0;
|
|
1036
|
+
const productIsTopWishlisted = topFive.some(
|
|
1037
|
+
(stat) => stat.product_id === productId
|
|
1038
|
+
);
|
|
1039
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex flex-col gap-y-4", children: [
|
|
1040
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-col gap-y-1", children: [
|
|
1041
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Wishlist performance" }),
|
|
1042
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Track how often this product appears in customer wishlists and see the current top performers." })
|
|
1043
|
+
] }),
|
|
1044
|
+
!productId ? /* @__PURE__ */ jsxRuntime.jsx(ui.InlineTip, { variant: "info", label: "Product not loaded yet", children: "Open a product detail record to view wishlist insights." }) : isProductStatsPending ? /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[96px] w-full rounded-lg" }) : isProductStatsError ? /* @__PURE__ */ jsxRuntime.jsx(ui.InlineTip, { variant: "error", label: "Unable to load product stats", children: productStatsError instanceof Error ? productStatsError.message : "Unknown error" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2 rounded-lg border border-ui-border-base bg-ui-bg-base p-4", children: [
|
|
1045
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
1046
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "txt-compact-xsmall-plus uppercase tracking-wide text-ui-fg-muted", children: "This product" }),
|
|
1047
|
+
productIsTopWishlisted && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", rounded: "full", color: "green", children: "Top 5" })
|
|
1048
|
+
] }),
|
|
1049
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
1050
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", className: "text-[32px] leading-none", children: productWishlistCount }),
|
|
1051
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: productWishlistCount === 1 ? "customer has saved this product." : "customers have saved this product." })
|
|
1052
|
+
] })
|
|
1053
|
+
] }),
|
|
1054
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 rounded-lg border border-ui-border-base bg-ui-bg-subtle p-4", children: [
|
|
1055
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
1056
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1057
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "txt-compact-xsmall-plus uppercase tracking-wide text-ui-fg-muted", children: "Storewide insights" }),
|
|
1058
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Top wishlisted products" })
|
|
1059
|
+
] }),
|
|
1060
|
+
productId && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "grey", children: productIsTopWishlisted ? "In top 5" : "Not in top 5" })
|
|
1061
|
+
] }),
|
|
1062
|
+
isAllStatsPending ? /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-[160px] w-full rounded-lg" }) : isAllStatsError ? /* @__PURE__ */ jsxRuntime.jsx(ui.InlineTip, { variant: "error", label: "Unable to load storewide stats", children: allStatsError instanceof Error ? allStatsError.message : "Unknown error" }) : !topFive.length ? /* @__PURE__ */ jsxRuntime.jsx(ui.InlineTip, { variant: "info", label: "No wishlist activity yet", children: "Customers have not saved any products to their wishlists yet. Once they do, the most popular products will show up here." }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-base", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
|
|
1063
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-ui-bg-field", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
1064
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1065
|
+
"th",
|
|
1066
|
+
{
|
|
1067
|
+
scope: "col",
|
|
1068
|
+
className: "px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
|
|
1069
|
+
children: "#"
|
|
1070
|
+
}
|
|
1071
|
+
),
|
|
1072
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1073
|
+
"th",
|
|
1074
|
+
{
|
|
1075
|
+
scope: "col",
|
|
1076
|
+
className: "px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
|
|
1077
|
+
children: "Product ID"
|
|
1078
|
+
}
|
|
1079
|
+
),
|
|
1080
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1081
|
+
"th",
|
|
1082
|
+
{
|
|
1083
|
+
scope: "col",
|
|
1084
|
+
className: "px-4 py-2 text-right text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
|
|
1085
|
+
children: "Wishlists"
|
|
1086
|
+
}
|
|
1087
|
+
)
|
|
1088
|
+
] }) }),
|
|
1089
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: topFive.map((stat, index) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1090
|
+
"tr",
|
|
1091
|
+
{
|
|
1092
|
+
className: stat.product_id === productId ? "bg-ui-bg-subtle" : "bg-ui-bg-base",
|
|
1093
|
+
children: [
|
|
1094
|
+
/* @__PURE__ */ jsxRuntime.jsxs("td", { className: "px-4 py-3 text-sm font-medium text-ui-fg-subtle", children: [
|
|
1095
|
+
"#",
|
|
1096
|
+
index + 1
|
|
1097
|
+
] }),
|
|
1098
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
1099
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "font-mono text-sm", children: stat.product_id }),
|
|
1100
|
+
stat.product_id === productId && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "green", children: "Current" })
|
|
1101
|
+
] }) }),
|
|
1102
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-3 text-right text-sm font-semibold", children: stat.wishlist_count.toLocaleString() })
|
|
1103
|
+
]
|
|
1104
|
+
},
|
|
1105
|
+
stat.product_id
|
|
1106
|
+
)) })
|
|
1107
|
+
] }) })
|
|
1108
|
+
] })
|
|
1109
|
+
] });
|
|
1110
|
+
};
|
|
1111
|
+
adminSdk.defineWidgetConfig({
|
|
1112
|
+
zone: "product.details.side.after"
|
|
1113
|
+
});
|
|
991
1114
|
const i18nTranslations0 = {};
|
|
992
1115
|
const widgetModule = { widgets: [
|
|
993
1116
|
{
|
|
@@ -1005,6 +1128,10 @@ const widgetModule = { widgets: [
|
|
|
1005
1128
|
{
|
|
1006
1129
|
Component: ProductMetadataTableWidget,
|
|
1007
1130
|
zone: ["product.details.after"]
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
Component: ProductWishlistStatsWidget,
|
|
1134
|
+
zone: ["product.details.side.after"]
|
|
1008
1135
|
}
|
|
1009
1136
|
] };
|
|
1010
1137
|
const routeModule = {
|
|
@@ -987,6 +987,129 @@ const ValueField = ({
|
|
|
987
987
|
defineWidgetConfig({
|
|
988
988
|
zone: "product.details.after"
|
|
989
989
|
});
|
|
990
|
+
const fetchJson = async (path) => {
|
|
991
|
+
const response = await fetch(path, {
|
|
992
|
+
credentials: "include"
|
|
993
|
+
});
|
|
994
|
+
const payload = await response.json().catch(() => null);
|
|
995
|
+
if (!response.ok) {
|
|
996
|
+
throw new Error(
|
|
997
|
+
(payload == null ? void 0 : payload.message) ?? "Unable to load wishlist statistics from the server"
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
return payload;
|
|
1001
|
+
};
|
|
1002
|
+
const ProductWishlistStatsWidget = ({ data }) => {
|
|
1003
|
+
const productId = data == null ? void 0 : data.id;
|
|
1004
|
+
const {
|
|
1005
|
+
data: productStats,
|
|
1006
|
+
isPending: isProductStatsPending,
|
|
1007
|
+
isError: isProductStatsError,
|
|
1008
|
+
error: productStatsError
|
|
1009
|
+
} = useQuery({
|
|
1010
|
+
queryKey: ["wishlist", "product", productId],
|
|
1011
|
+
enabled: Boolean(productId),
|
|
1012
|
+
queryFn: () => fetchJson(
|
|
1013
|
+
`/admin/wishlist/stats?product_id=${productId}`
|
|
1014
|
+
),
|
|
1015
|
+
refetchInterval: 6e4
|
|
1016
|
+
});
|
|
1017
|
+
const {
|
|
1018
|
+
data: allStats,
|
|
1019
|
+
isPending: isAllStatsPending,
|
|
1020
|
+
isError: isAllStatsError,
|
|
1021
|
+
error: allStatsError
|
|
1022
|
+
} = useQuery({
|
|
1023
|
+
queryKey: ["wishlist", "stats"],
|
|
1024
|
+
queryFn: () => fetchJson("/admin/wishlist/stats"),
|
|
1025
|
+
staleTime: 6e4
|
|
1026
|
+
});
|
|
1027
|
+
const topFive = useMemo(() => {
|
|
1028
|
+
var _a;
|
|
1029
|
+
if (!((_a = allStats == null ? void 0 : allStats.stats) == null ? void 0 : _a.length)) {
|
|
1030
|
+
return [];
|
|
1031
|
+
}
|
|
1032
|
+
return [...allStats.stats].sort((a, b) => b.wishlist_count - a.wishlist_count).slice(0, 5);
|
|
1033
|
+
}, [allStats]);
|
|
1034
|
+
const productWishlistCount = (productStats == null ? void 0 : productStats.wishlist_count) ?? 0;
|
|
1035
|
+
const productIsTopWishlisted = topFive.some(
|
|
1036
|
+
(stat) => stat.product_id === productId
|
|
1037
|
+
);
|
|
1038
|
+
return /* @__PURE__ */ jsxs(Container, { className: "flex flex-col gap-y-4", children: [
|
|
1039
|
+
/* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-y-1", children: [
|
|
1040
|
+
/* @__PURE__ */ jsx(Heading, { level: "h2", children: "Wishlist performance" }),
|
|
1041
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Track how often this product appears in customer wishlists and see the current top performers." })
|
|
1042
|
+
] }),
|
|
1043
|
+
!productId ? /* @__PURE__ */ jsx(InlineTip, { variant: "info", label: "Product not loaded yet", children: "Open a product detail record to view wishlist insights." }) : isProductStatsPending ? /* @__PURE__ */ jsx(Skeleton, { className: "h-[96px] w-full rounded-lg" }) : isProductStatsError ? /* @__PURE__ */ jsx(InlineTip, { variant: "error", label: "Unable to load product stats", children: productStatsError instanceof Error ? productStatsError.message : "Unknown error" }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2 rounded-lg border border-ui-border-base bg-ui-bg-base p-4", children: [
|
|
1044
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
1045
|
+
/* @__PURE__ */ jsx(Text, { className: "txt-compact-xsmall-plus uppercase tracking-wide text-ui-fg-muted", children: "This product" }),
|
|
1046
|
+
productIsTopWishlisted && /* @__PURE__ */ jsx(Badge, { size: "2xsmall", rounded: "full", color: "green", children: "Top 5" })
|
|
1047
|
+
] }),
|
|
1048
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-1", children: [
|
|
1049
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", className: "text-[32px] leading-none", children: productWishlistCount }),
|
|
1050
|
+
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: productWishlistCount === 1 ? "customer has saved this product." : "customers have saved this product." })
|
|
1051
|
+
] })
|
|
1052
|
+
] }),
|
|
1053
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-3 rounded-lg border border-ui-border-base bg-ui-bg-subtle p-4", children: [
|
|
1054
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-x-4", children: [
|
|
1055
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1056
|
+
/* @__PURE__ */ jsx(Text, { className: "txt-compact-xsmall-plus uppercase tracking-wide text-ui-fg-muted", children: "Storewide insights" }),
|
|
1057
|
+
/* @__PURE__ */ jsx(Heading, { level: "h3", children: "Top wishlisted products" })
|
|
1058
|
+
] }),
|
|
1059
|
+
productId && /* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: "grey", children: productIsTopWishlisted ? "In top 5" : "Not in top 5" })
|
|
1060
|
+
] }),
|
|
1061
|
+
isAllStatsPending ? /* @__PURE__ */ jsx(Skeleton, { className: "h-[160px] w-full rounded-lg" }) : isAllStatsError ? /* @__PURE__ */ jsx(InlineTip, { variant: "error", label: "Unable to load storewide stats", children: allStatsError instanceof Error ? allStatsError.message : "Unknown error" }) : !topFive.length ? /* @__PURE__ */ jsx(InlineTip, { variant: "info", label: "No wishlist activity yet", children: "Customers have not saved any products to their wishlists yet. Once they do, the most popular products will show up here." }) : /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-lg border border-ui-border-base bg-ui-bg-base", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
|
|
1062
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-ui-bg-field", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
1063
|
+
/* @__PURE__ */ jsx(
|
|
1064
|
+
"th",
|
|
1065
|
+
{
|
|
1066
|
+
scope: "col",
|
|
1067
|
+
className: "px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
|
|
1068
|
+
children: "#"
|
|
1069
|
+
}
|
|
1070
|
+
),
|
|
1071
|
+
/* @__PURE__ */ jsx(
|
|
1072
|
+
"th",
|
|
1073
|
+
{
|
|
1074
|
+
scope: "col",
|
|
1075
|
+
className: "px-4 py-2 text-left text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
|
|
1076
|
+
children: "Product ID"
|
|
1077
|
+
}
|
|
1078
|
+
),
|
|
1079
|
+
/* @__PURE__ */ jsx(
|
|
1080
|
+
"th",
|
|
1081
|
+
{
|
|
1082
|
+
scope: "col",
|
|
1083
|
+
className: "px-4 py-2 text-right text-[11px] font-semibold uppercase tracking-wide text-ui-fg-muted",
|
|
1084
|
+
children: "Wishlists"
|
|
1085
|
+
}
|
|
1086
|
+
)
|
|
1087
|
+
] }) }),
|
|
1088
|
+
/* @__PURE__ */ jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: topFive.map((stat, index) => /* @__PURE__ */ jsxs(
|
|
1089
|
+
"tr",
|
|
1090
|
+
{
|
|
1091
|
+
className: stat.product_id === productId ? "bg-ui-bg-subtle" : "bg-ui-bg-base",
|
|
1092
|
+
children: [
|
|
1093
|
+
/* @__PURE__ */ jsxs("td", { className: "px-4 py-3 text-sm font-medium text-ui-fg-subtle", children: [
|
|
1094
|
+
"#",
|
|
1095
|
+
index + 1
|
|
1096
|
+
] }),
|
|
1097
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
1098
|
+
/* @__PURE__ */ jsx(Text, { className: "font-mono text-sm", children: stat.product_id }),
|
|
1099
|
+
stat.product_id === productId && /* @__PURE__ */ jsx(Badge, { size: "2xsmall", color: "green", children: "Current" })
|
|
1100
|
+
] }) }),
|
|
1101
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right text-sm font-semibold", children: stat.wishlist_count.toLocaleString() })
|
|
1102
|
+
]
|
|
1103
|
+
},
|
|
1104
|
+
stat.product_id
|
|
1105
|
+
)) })
|
|
1106
|
+
] }) })
|
|
1107
|
+
] })
|
|
1108
|
+
] });
|
|
1109
|
+
};
|
|
1110
|
+
defineWidgetConfig({
|
|
1111
|
+
zone: "product.details.side.after"
|
|
1112
|
+
});
|
|
990
1113
|
const i18nTranslations0 = {};
|
|
991
1114
|
const widgetModule = { widgets: [
|
|
992
1115
|
{
|
|
@@ -1004,6 +1127,10 @@ const widgetModule = { widgets: [
|
|
|
1004
1127
|
{
|
|
1005
1128
|
Component: ProductMetadataTableWidget,
|
|
1006
1129
|
zone: ["product.details.after"]
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
Component: ProductWishlistStatsWidget,
|
|
1133
|
+
zone: ["product.details.side.after"]
|
|
1007
1134
|
}
|
|
1008
1135
|
] };
|
|
1009
1136
|
const routeModule = {
|
|
@@ -4,6 +4,18 @@ exports.GET = void 0;
|
|
|
4
4
|
const wishlist_1 = require("../../../../modules/wishlist");
|
|
5
5
|
const GET = async (req, res) => {
|
|
6
6
|
try {
|
|
7
|
+
const authContext = req.auth_context;
|
|
8
|
+
const actorId = authContext?.actor_id;
|
|
9
|
+
const actorType = authContext?.actor_type;
|
|
10
|
+
if (!actorId) {
|
|
11
|
+
res.status(401).json({ message: "Unauthorized" });
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
const adminActorTypes = ["user", "api-key"];
|
|
15
|
+
if (!actorType || !adminActorTypes.includes(actorType)) {
|
|
16
|
+
res.status(403).json({ message: "Only admin actors can access wishlist stats" });
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
7
19
|
const wishlistService = req.scope.resolve(wishlist_1.WISHLIST_MODULE);
|
|
8
20
|
const productId = req.query.product_id;
|
|
9
21
|
// If product_id is provided, return count for that product only
|
|
@@ -29,4 +41,4 @@ const GET = async (req, res) => {
|
|
|
29
41
|
}
|
|
30
42
|
};
|
|
31
43
|
exports.GET = GET;
|
|
32
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
44
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3dpc2hsaXN0L3N0YXRzL3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLDJEQUE4RDtBQUd2RCxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQ3RCLEdBQWtCLEVBQ2xCLEdBQW1CLEVBQ0osRUFBRTtJQUNqQixJQUFJLENBQUM7UUFDSCxNQUFNLFdBQVcsR0FBSSxHQUFXLENBQUMsWUFBWSxDQUFBO1FBQzdDLE1BQU0sT0FBTyxHQUFHLFdBQVcsRUFBRSxRQUFRLENBQUE7UUFDckMsTUFBTSxTQUFTLEdBQUcsV0FBVyxFQUFFLFVBQVUsQ0FBQTtRQUV6QyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7WUFDYixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxjQUFjLEVBQUUsQ0FBQyxDQUFBO1lBQ2pELE9BQU07UUFDUixDQUFDO1FBRUQsTUFBTSxlQUFlLEdBQUcsQ0FBQyxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUE7UUFDM0MsSUFBSSxDQUFDLFNBQVMsSUFBSSxDQUFDLGVBQWUsQ0FBQyxRQUFRLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQztZQUN2RCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSw2Q0FBNkMsRUFBRSxDQUFDLENBQUE7WUFDaEYsT0FBTTtRQUNSLENBQUM7UUFFRCxNQUFNLGVBQWUsR0FBMEIsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQzlELDBCQUFlLENBQ2hCLENBQUE7UUFFRCxNQUFNLFNBQVMsR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLFVBQWdDLENBQUE7UUFFNUQsZ0VBQWdFO1FBQ2hFLElBQUksU0FBUyxFQUFFLENBQUM7WUFDZCxNQUFNLE1BQU0sR0FBRyxNQUFNLGVBQWUsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUE7WUFDbkUsTUFBTSxLQUFLLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLFVBQVUsS0FBSyxTQUFTLENBQUMsQ0FBQTtZQUU1RCxHQUFHLENBQUMsSUFBSSxDQUFDO2dCQUNQLFVBQVUsRUFBRSxTQUFTO2dCQUNyQixjQUFjLEVBQUUsS0FBSyxFQUFFLGNBQWMsSUFBSSxDQUFDO2FBQzNDLENBQUMsQ0FBQTtZQUNGLE9BQU07UUFDUixDQUFDO1FBRUQsNENBQTRDO1FBQzVDLE1BQU0sTUFBTSxHQUFHLE1BQU0sZUFBZSxDQUFDLGlCQUFpQixFQUFFLENBQUE7UUFFeEQsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFBO0lBQzdCLENBQUM7SUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1FBQ2YsSUFBSSxLQUFLLFlBQVksS0FBSyxFQUFFLENBQUM7WUFDM0IsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUE7WUFDaEQsT0FBTTtRQUNSLENBQUM7UUFDRCxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxDQUFDLENBQUE7SUFDNUQsQ0FBQztBQUNILENBQUMsQ0FBQTtBQWpEWSxRQUFBLEdBQUcsT0FpRGYifQ==
|
|
@@ -9,12 +9,17 @@ const DELETE = async (req, res) => {
|
|
|
9
9
|
res.status(400).json({ message: "product_id is required" });
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
|
-
//
|
|
13
|
-
const
|
|
12
|
+
// Enforce that only authenticated customers can delete wishlist entries
|
|
13
|
+
const authContext = req.auth_context;
|
|
14
|
+
const customerId = authContext?.actor_id;
|
|
14
15
|
if (!customerId) {
|
|
15
16
|
res.status(401).json({ message: "Unauthorized" });
|
|
16
17
|
return;
|
|
17
18
|
}
|
|
19
|
+
if (authContext?.actor_type !== "customer") {
|
|
20
|
+
res.status(403).json({ message: "Only customers can remove wishlist items" });
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
18
23
|
// Execute remove from wishlist workflow
|
|
19
24
|
const { result } = await (0, remove_from_wishlist_1.removeFromWishlistWorkflow)(req.scope).run({
|
|
20
25
|
input: {
|
|
@@ -33,4 +38,4 @@ const DELETE = async (req, res) => {
|
|
|
33
38
|
}
|
|
34
39
|
};
|
|
35
40
|
exports.DELETE = DELETE;
|
|
36
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
41
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3dpc2hsaXN0L1twcm9kdWN0X2lkXS9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSxxRkFBdUY7QUFFaEYsTUFBTSxNQUFNLEdBQUcsS0FBSyxFQUN6QixHQUFrQixFQUNsQixHQUFtQixFQUNKLEVBQUU7SUFDakIsSUFBSSxDQUFDO1FBQ0gsTUFBTSxFQUFFLFVBQVUsRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUE7UUFFakMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ2hCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLHdCQUF3QixFQUFFLENBQUMsQ0FBQTtZQUMzRCxPQUFNO1FBQ1IsQ0FBQztRQUVELHdFQUF3RTtRQUN4RSxNQUFNLFdBQVcsR0FBSSxHQUFXLENBQUMsWUFBWSxDQUFBO1FBQzdDLE1BQU0sVUFBVSxHQUFHLFdBQVcsRUFBRSxRQUFRLENBQUE7UUFFeEMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ2hCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUE7WUFDakQsT0FBTTtRQUNSLENBQUM7UUFFRCxJQUFJLFdBQVcsRUFBRSxVQUFVLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDM0MsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsMENBQTBDLEVBQUUsQ0FBQyxDQUFBO1lBQzdFLE9BQU07UUFDUixDQUFDO1FBRUQsd0NBQXdDO1FBQ3hDLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUEsaURBQTBCLEVBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQztZQUNqRSxLQUFLLEVBQUU7Z0JBQ0wsV0FBVyxFQUFFLFVBQVU7Z0JBQ3ZCLFVBQVUsRUFBRSxVQUFVO2FBQ3ZCO1NBQ0YsQ0FBQyxDQUFBO1FBRUYsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxNQUFNLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQTtJQUN2QyxDQUFDO0lBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztRQUNmLElBQUksS0FBSyxZQUFZLEtBQUssRUFBRSxDQUFDO1lBQzNCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLEtBQUssQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFBO1lBQ2hELE9BQU07UUFDUixDQUFDO1FBQ0QsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsQ0FBQyxDQUFBO0lBQzVELENBQUM7QUFDSCxDQUFDLENBQUE7QUExQ1ksUUFBQSxNQUFNLFVBMENsQiJ9
|
|
@@ -8,12 +8,17 @@ const POST = async (req, res) => {
|
|
|
8
8
|
try {
|
|
9
9
|
// Validate request body
|
|
10
10
|
const validated = validators_1.AddToWishlistSchema.parse(req.body);
|
|
11
|
-
//
|
|
12
|
-
const
|
|
11
|
+
// Enforce that only authenticated customers can mutate wishlists
|
|
12
|
+
const authContext = req.auth_context;
|
|
13
|
+
const customerId = authContext?.actor_id;
|
|
13
14
|
if (!customerId) {
|
|
14
15
|
res.status(401).json({ message: "Unauthorized" });
|
|
15
16
|
return;
|
|
16
17
|
}
|
|
18
|
+
if (authContext?.actor_type !== "customer") {
|
|
19
|
+
res.status(403).json({ message: "Only customers can update wishlists" });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
17
22
|
// Execute add to wishlist workflow
|
|
18
23
|
const { result } = await (0, add_to_wishlist_1.addToWishlistWorkflow)(req.scope).run({
|
|
19
24
|
input: {
|
|
@@ -34,12 +39,17 @@ const POST = async (req, res) => {
|
|
|
34
39
|
exports.POST = POST;
|
|
35
40
|
const GET = async (req, res) => {
|
|
36
41
|
try {
|
|
37
|
-
//
|
|
38
|
-
const
|
|
42
|
+
// Restrict wishlist reads to authenticated customers
|
|
43
|
+
const authContext = req.auth_context;
|
|
44
|
+
const customerId = authContext?.actor_id;
|
|
39
45
|
if (!customerId) {
|
|
40
46
|
res.status(401).json({ message: "Unauthorized" });
|
|
41
47
|
return;
|
|
42
48
|
}
|
|
49
|
+
if (authContext?.actor_type !== "customer") {
|
|
50
|
+
res.status(403).json({ message: "Only customers can read their wishlist" });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
43
53
|
// Parse query parameters
|
|
44
54
|
const includeDetails = req.query.include_details === "true" ||
|
|
45
55
|
req.query.include_details === "1";
|
|
@@ -63,4 +73,4 @@ const GET = async (req, res) => {
|
|
|
63
73
|
}
|
|
64
74
|
};
|
|
65
75
|
exports.GET = GET;
|
|
66
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
76
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL3dpc2hsaXN0L3JvdXRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUNBLHdFQUEwRTtBQUMxRSxrRUFBcUU7QUFDckUsNkNBQWtEO0FBRTNDLE1BQU0sSUFBSSxHQUFHLEtBQUssRUFDdkIsR0FBa0IsRUFDbEIsR0FBbUIsRUFDSixFQUFFO0lBQ2pCLElBQUksQ0FBQztRQUNILHdCQUF3QjtRQUN4QixNQUFNLFNBQVMsR0FBRyxnQ0FBbUIsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxDQUFBO1FBRXJELGlFQUFpRTtRQUNqRSxNQUFNLFdBQVcsR0FBSSxHQUFXLENBQUMsWUFBWSxDQUFBO1FBQzdDLE1BQU0sVUFBVSxHQUFHLFdBQVcsRUFBRSxRQUFRLENBQUE7UUFFeEMsSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1lBQ2hCLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLGNBQWMsRUFBRSxDQUFDLENBQUE7WUFDakQsT0FBTTtRQUNSLENBQUM7UUFFRCxJQUFJLFdBQVcsRUFBRSxVQUFVLEtBQUssVUFBVSxFQUFFLENBQUM7WUFDM0MsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxPQUFPLEVBQUUscUNBQXFDLEVBQUUsQ0FBQyxDQUFBO1lBQ3hFLE9BQU07UUFDUixDQUFDO1FBRUQsbUNBQW1DO1FBQ25DLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUEsdUNBQXFCLEVBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQztZQUM1RCxLQUFLLEVBQUU7Z0JBQ0wsV0FBVyxFQUFFLFVBQVU7Z0JBQ3ZCLFVBQVUsRUFBRSxTQUFTLENBQUMsVUFBVTthQUNqQztTQUNGLENBQUMsQ0FBQTtRQUVGLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxhQUFhLEVBQUUsTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFDLENBQUE7SUFDbkQsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixJQUFJLEtBQUssWUFBWSxLQUFLLEVBQUUsQ0FBQztZQUMzQixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQTtZQUNoRCxPQUFNO1FBQ1IsQ0FBQztRQUNELEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLHVCQUF1QixFQUFFLENBQUMsQ0FBQTtJQUM1RCxDQUFDO0FBQ0gsQ0FBQyxDQUFBO0FBdENZLFFBQUEsSUFBSSxRQXNDaEI7QUFFTSxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQ3RCLEdBQWtCLEVBQ2xCLEdBQW1CLEVBQ0osRUFBRTtJQUNqQixJQUFJLENBQUM7UUFDSCxxREFBcUQ7UUFDckQsTUFBTSxXQUFXLEdBQUksR0FBVyxDQUFDLFlBQVksQ0FBQTtRQUM3QyxNQUFNLFVBQVUsR0FBRyxXQUFXLEVBQUUsUUFBUSxDQUFBO1FBRXhDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztZQUNoQixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxjQUFjLEVBQUUsQ0FBQyxDQUFBO1lBQ2pELE9BQU07UUFDUixDQUFDO1FBRUQsSUFBSSxXQUFXLEVBQUUsVUFBVSxLQUFLLFVBQVUsRUFBRSxDQUFDO1lBQzNDLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLHdDQUF3QyxFQUFFLENBQUMsQ0FBQTtZQUMzRSxPQUFNO1FBQ1IsQ0FBQztRQUVELHlCQUF5QjtRQUN6QixNQUFNLGNBQWMsR0FDbEIsR0FBRyxDQUFDLEtBQUssQ0FBQyxlQUFlLEtBQUssTUFBTTtZQUNwQyxHQUFHLENBQUMsS0FBSyxDQUFDLGVBQWUsS0FBSyxHQUFHLENBQUE7UUFFbkMsZ0NBQWdDO1FBQ2hDLE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUEsa0NBQW1CLEVBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQztZQUMxRCxLQUFLLEVBQUU7Z0JBQ0wsV0FBVyxFQUFFLFVBQVU7Z0JBQ3ZCLE9BQU8sRUFBRTtvQkFDUCxjQUFjLEVBQUUsY0FBYztpQkFDL0I7YUFDRjtTQUNGLENBQUMsQ0FBQTtRQUVGLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxRQUFRLEVBQUUsTUFBTSxDQUFDLFFBQVEsRUFBRSxDQUFDLENBQUE7SUFDekMsQ0FBQztJQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7UUFDZixJQUFJLEtBQUssWUFBWSxLQUFLLEVBQUUsQ0FBQztZQUMzQixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQTtZQUNoRCxPQUFNO1FBQ1IsQ0FBQztRQUNELEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsT0FBTyxFQUFFLHVCQUF1QixFQUFFLENBQUMsQ0FBQTtJQUM1RCxDQUFDO0FBQ0gsQ0FBQyxDQUFBO0FBMUNZLFFBQUEsR0FBRyxPQTBDZiJ9
|
|
@@ -1,40 +1,98 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getWishlist =
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
3
|
+
exports.createWishlistHelpers = exports.createRemoveFromWishlist = exports.createAddToWishlist = exports.createGetWishlist = exports.removeFromWishlist = exports.addToWishlist = exports.getWishlist = void 0;
|
|
4
|
+
const WISHLIST_ENDPOINT = "/store/wishlist";
|
|
5
|
+
const normalizeBaseUrl = (baseUrl) => {
|
|
6
|
+
if (!baseUrl) {
|
|
7
|
+
return "";
|
|
8
|
+
}
|
|
9
|
+
return baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
|
|
10
|
+
};
|
|
11
|
+
const getClientRequest = (client) => {
|
|
12
|
+
if (!client) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
if ("request" in client && typeof client.request === "function") {
|
|
16
|
+
return client.request.bind(client);
|
|
17
|
+
}
|
|
18
|
+
if ("client" in client && typeof client.client?.request === "function") {
|
|
19
|
+
return client.client.request.bind(client.client);
|
|
20
|
+
}
|
|
21
|
+
return undefined;
|
|
22
|
+
};
|
|
23
|
+
const execute = async (path, init, options) => {
|
|
24
|
+
const headers = {
|
|
25
|
+
...(init.headers ?? {}),
|
|
26
|
+
...(options.headers ?? {}),
|
|
27
|
+
};
|
|
28
|
+
const normalizedInit = {
|
|
29
|
+
...init,
|
|
30
|
+
headers,
|
|
29
31
|
};
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
const clientRequest = getClientRequest(options.client);
|
|
33
|
+
if (clientRequest) {
|
|
34
|
+
return (await clientRequest(path, normalizedInit));
|
|
35
|
+
}
|
|
36
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
37
|
+
if (!fetchImpl) {
|
|
38
|
+
throw new Error("No fetch implementation available. Provide `fetchImpl` or a Medusa client.");
|
|
39
|
+
}
|
|
40
|
+
const url = `${normalizeBaseUrl(options.baseUrl)}${path}`;
|
|
41
|
+
const response = await fetchImpl(url || path, normalizedInit);
|
|
42
|
+
if (!response.ok) {
|
|
43
|
+
const body = await response.text();
|
|
44
|
+
throw new Error(body || `Request failed with status ${response.status}`);
|
|
45
|
+
}
|
|
46
|
+
return (await response.json());
|
|
47
|
+
};
|
|
48
|
+
const getWishlist = async (input = {}, options = {}) => {
|
|
49
|
+
const params = new URLSearchParams();
|
|
50
|
+
if (input.includeDetails) {
|
|
51
|
+
params.set("include_details", "true");
|
|
52
|
+
}
|
|
53
|
+
const endpoint = params.toString()
|
|
54
|
+
? `${WISHLIST_ENDPOINT}?${params.toString()}`
|
|
55
|
+
: WISHLIST_ENDPOINT;
|
|
56
|
+
return execute(endpoint, {
|
|
57
|
+
method: "GET",
|
|
58
|
+
}, options);
|
|
59
|
+
};
|
|
60
|
+
exports.getWishlist = getWishlist;
|
|
61
|
+
const addToWishlist = async (input, options = {}) => {
|
|
62
|
+
if (!input.product_id) {
|
|
63
|
+
throw new Error("product_id is required");
|
|
64
|
+
}
|
|
65
|
+
return execute(WISHLIST_ENDPOINT, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: {
|
|
68
|
+
"Content-Type": "application/json",
|
|
69
|
+
},
|
|
70
|
+
body: JSON.stringify({
|
|
71
|
+
product_id: input.product_id,
|
|
72
|
+
}),
|
|
73
|
+
}, options);
|
|
74
|
+
};
|
|
75
|
+
exports.addToWishlist = addToWishlist;
|
|
76
|
+
const removeFromWishlist = async (input, options = {}) => {
|
|
77
|
+
if (!input.product_id) {
|
|
78
|
+
throw new Error("product_id is required");
|
|
79
|
+
}
|
|
80
|
+
const endpoint = `${WISHLIST_ENDPOINT}/${encodeURIComponent(input.product_id)}`;
|
|
81
|
+
return execute(endpoint, {
|
|
82
|
+
method: "DELETE",
|
|
83
|
+
}, options);
|
|
84
|
+
};
|
|
85
|
+
exports.removeFromWishlist = removeFromWishlist;
|
|
86
|
+
const createGetWishlist = (options = {}) => (input) => (0, exports.getWishlist)(input, options);
|
|
87
|
+
exports.createGetWishlist = createGetWishlist;
|
|
88
|
+
const createAddToWishlist = (options = {}) => (input) => (0, exports.addToWishlist)(input, options);
|
|
89
|
+
exports.createAddToWishlist = createAddToWishlist;
|
|
90
|
+
const createRemoveFromWishlist = (options = {}) => (input) => (0, exports.removeFromWishlist)(input, options);
|
|
91
|
+
exports.createRemoveFromWishlist = createRemoveFromWishlist;
|
|
92
|
+
const createWishlistHelpers = (options = {}) => ({
|
|
93
|
+
getWishlist: (0, exports.createGetWishlist)(options),
|
|
94
|
+
addToWishlist: (0, exports.createAddToWishlist)(options),
|
|
95
|
+
removeFromWishlist: (0, exports.createRemoveFromWishlist)(options),
|
|
96
|
+
});
|
|
97
|
+
exports.createWishlistHelpers = createWishlistHelpers;
|
|
98
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3NoYXJlZC93aXNobGlzdC9oZWxwZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsTUFBTSxpQkFBaUIsR0FBRyxpQkFBaUIsQ0FBQTtBQXFFM0MsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLE9BQWdCLEVBQUUsRUFBRTtJQUM1QyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDYixPQUFPLEVBQUUsQ0FBQTtJQUNYLENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQTtBQUMvRCxDQUFDLENBQUE7QUFFRCxNQUFNLGdCQUFnQixHQUFHLENBQUMsTUFBeUIsRUFBRSxFQUFFO0lBQ3JELElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE9BQU8sU0FBUyxDQUFBO0lBQ2xCLENBQUM7SUFFRCxJQUFJLFNBQVMsSUFBSSxNQUFNLElBQUksT0FBTyxNQUFNLENBQUMsT0FBTyxLQUFLLFVBQVUsRUFBRSxDQUFDO1FBQ2hFLE9BQU8sTUFBTSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUE7SUFDcEMsQ0FBQztJQUVELElBQUksUUFBUSxJQUFJLE1BQU0sSUFBSSxPQUFPLE1BQU0sQ0FBQyxNQUFNLEVBQUUsT0FBTyxLQUFLLFVBQVUsRUFBRSxDQUFDO1FBQ3ZFLE9BQU8sTUFBTSxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQTtJQUNsRCxDQUFDO0lBRUQsT0FBTyxTQUFTLENBQUE7QUFDbEIsQ0FBQyxDQUFBO0FBRUQsTUFBTSxPQUFPLEdBQUcsS0FBSyxFQUNuQixJQUFZLEVBQ1os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
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getWishlist = void 0;
|
|
3
|
+
exports.removeFromWishlist = exports.getWishlist = exports.createWishlistHelpers = exports.createRemoveFromWishlist = exports.createGetWishlist = exports.createAddToWishlist = exports.addToWishlist = void 0;
|
|
4
4
|
var helper_1 = require("./helper");
|
|
5
|
+
Object.defineProperty(exports, "addToWishlist", { enumerable: true, get: function () { return helper_1.addToWishlist; } });
|
|
6
|
+
Object.defineProperty(exports, "createAddToWishlist", { enumerable: true, get: function () { return helper_1.createAddToWishlist; } });
|
|
7
|
+
Object.defineProperty(exports, "createGetWishlist", { enumerable: true, get: function () { return helper_1.createGetWishlist; } });
|
|
8
|
+
Object.defineProperty(exports, "createRemoveFromWishlist", { enumerable: true, get: function () { return helper_1.createRemoveFromWishlist; } });
|
|
9
|
+
Object.defineProperty(exports, "createWishlistHelpers", { enumerable: true, get: function () { return helper_1.createWishlistHelpers; } });
|
|
5
10
|
Object.defineProperty(exports, "getWishlist", { enumerable: true, get: function () { return helper_1.getWishlist; } });
|
|
6
|
-
|
|
11
|
+
Object.defineProperty(exports, "removeFromWishlist", { enumerable: true, get: function () { return helper_1.removeFromWishlist; } });
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvc2hhcmVkL3dpc2hsaXN0L2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQVFpQjtBQVBmLHVHQUFBLGFBQWEsT0FBQTtBQUNiLDZHQUFBLG1CQUFtQixPQUFBO0FBQ25CLDJHQUFBLGlCQUFpQixPQUFBO0FBQ2pCLGtIQUFBLHdCQUF3QixPQUFBO0FBQ3hCLCtHQUFBLHFCQUFxQixPQUFBO0FBQ3JCLHFHQUFBLFdBQVcsT0FBQTtBQUNYLDRHQUFBLGtCQUFrQixPQUFBIn0=
|
package/README.md
CHANGED
|
@@ -194,6 +194,18 @@ inputs. No extra configuration is required beyond defining descriptors under
|
|
|
194
194
|
`metadata.products.descriptors` or `metadata.categories.descriptors` in
|
|
195
195
|
`medusa-config.ts`.
|
|
196
196
|
|
|
197
|
+
#### Wishlist Insights Widget
|
|
198
|
+
|
|
199
|
+
The admin extension also injects a **Wishlist performance** card into the product
|
|
200
|
+
details sidebar (`product.details.side.after`). The widget:
|
|
201
|
+
|
|
202
|
+
- Shows how many unique customers saved the current product.
|
|
203
|
+
- Highlights whether the product ranks within the most wishlisted items.
|
|
204
|
+
- Displays a live leaderboard (top 5) powered by `/admin/wishlist/stats`.
|
|
205
|
+
|
|
206
|
+
No additional configuration is required—install the plugin, run the wishlist
|
|
207
|
+
migration, and open any product inside Medusa Admin to see the UI.
|
|
208
|
+
|
|
197
209
|
#### Promotion Window
|
|
198
210
|
|
|
199
211
|
Configure how promotion dates are tracked using product metadata:
|
|
@@ -222,6 +234,12 @@ Configure rating-based filtering:
|
|
|
222
234
|
|
|
223
235
|
The plugin includes a comprehensive wishlist feature that allows customers to save products they're interested in and admins to view wishlist statistics.
|
|
224
236
|
|
|
237
|
+
### Security & Access Control
|
|
238
|
+
|
|
239
|
+
- Store-facing wishlist routes authenticate strictly as customers. Customer IDs are always derived from the session or JWT auth context and never taken from the request payload, so an admin or API client cannot spoof a customer identifier.
|
|
240
|
+
- Admin-facing wishlist statistics are available only to admin actors (`user` sessions or secret API keys). Customer tokens receive a `403` response when attempting to access `/admin/wishlist/stats`.
|
|
241
|
+
- This separation ensures only customers can manage their wishlist entries while only admins can inspect aggregate wishlist state.
|
|
242
|
+
|
|
225
243
|
### Module Registration
|
|
226
244
|
|
|
227
245
|
The wishlist module is automatically registered when you add the plugin to your Medusa configuration. No additional configuration is required for basic usage.
|
|
@@ -410,59 +428,79 @@ curl "https://your-store.com/admin/wishlist/stats?product_id=prod_123" \
|
|
|
410
428
|
-H "Authorization: Bearer ADMIN_TOKEN"
|
|
411
429
|
```
|
|
412
430
|
|
|
413
|
-
### Helper
|
|
431
|
+
### Helper Functions
|
|
414
432
|
|
|
415
|
-
The plugin
|
|
433
|
+
The plugin exposes lightweight helpers that wrap the Store API endpoints. They are ideal for server-side storefronts that need to call the wishlist endpoints while relying on the authenticated customer's session (no customer ID is ever passed in the payload).
|
|
416
434
|
|
|
417
|
-
####
|
|
435
|
+
#### Imports
|
|
418
436
|
|
|
419
437
|
```typescript
|
|
420
|
-
import {
|
|
438
|
+
import {
|
|
439
|
+
addToWishlist,
|
|
440
|
+
getWishlist,
|
|
441
|
+
removeFromWishlist,
|
|
442
|
+
createWishlistHelpers,
|
|
443
|
+
} from "medusa-product-helper/wishlist-helper"
|
|
421
444
|
```
|
|
422
445
|
|
|
423
|
-
#### Usage
|
|
446
|
+
#### Example Usage
|
|
424
447
|
|
|
425
|
-
**Get only product IDs**:
|
|
426
448
|
```typescript
|
|
427
|
-
|
|
449
|
+
// Add a product
|
|
450
|
+
await addToWishlist(
|
|
451
|
+
{ product_id: "prod_123" },
|
|
452
|
+
{
|
|
453
|
+
baseUrl: "https://store.example.com",
|
|
454
|
+
headers: {
|
|
455
|
+
Cookie: "connect.sid=...", // or Authorization header
|
|
456
|
+
},
|
|
457
|
+
}
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
// Read wishlist with details
|
|
461
|
+
const { wishlist } = await getWishlist(
|
|
462
|
+
{ includeDetails: true },
|
|
463
|
+
{
|
|
464
|
+
baseUrl: "https://store.example.com",
|
|
465
|
+
headers: {
|
|
466
|
+
Cookie: "connect.sid=...",
|
|
467
|
+
},
|
|
468
|
+
}
|
|
469
|
+
)
|
|
428
470
|
|
|
429
|
-
//
|
|
430
|
-
|
|
431
|
-
|
|
471
|
+
// Remove a product
|
|
472
|
+
await removeFromWishlist(
|
|
473
|
+
{ product_id: "prod_123" },
|
|
474
|
+
{
|
|
475
|
+
baseUrl: "https://store.example.com",
|
|
476
|
+
headers: {
|
|
477
|
+
Cookie: "connect.sid=...",
|
|
478
|
+
},
|
|
479
|
+
}
|
|
480
|
+
)
|
|
432
481
|
```
|
|
433
482
|
|
|
434
|
-
|
|
435
|
-
```typescript
|
|
436
|
-
import { getWishlist } from "medusa-product-helper/wishlist-helper"
|
|
483
|
+
#### Configuration Options
|
|
437
484
|
|
|
438
|
-
|
|
439
|
-
includeDetails: true
|
|
440
|
-
})
|
|
441
|
-
// Returns: [
|
|
442
|
-
// {
|
|
443
|
-
// product_id: 'prod_123',
|
|
444
|
-
// product: { ... },
|
|
445
|
-
// id: 'wish_123',
|
|
446
|
-
// created_at: Date
|
|
447
|
-
// },
|
|
448
|
-
// ...
|
|
449
|
-
// ]
|
|
450
|
-
```
|
|
485
|
+
All helper calls accept the following options:
|
|
451
486
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
487
|
+
- `client`: Medusa JS/SDK client instance. When provided, network requests are delegated to `client.request`.
|
|
488
|
+
- `baseUrl`: Base URL for the Store API (e.g., `https://store.example.com`). Required when a client is not provided.
|
|
489
|
+
- `fetchImpl`: Custom `fetch` implementation for SSR or React Native environments. Defaults to `globalThis.fetch`.
|
|
490
|
+
- `headers`: Additional headers appended to every request (useful for `Cookie` / `Authorization` headers).
|
|
455
491
|
|
|
456
|
-
|
|
457
|
-
includeDetails: true,
|
|
458
|
-
productIds: ['prod_123', 'prod_456']
|
|
459
|
-
})
|
|
460
|
-
```
|
|
492
|
+
You can also generate pre-configured helpers:
|
|
461
493
|
|
|
462
|
-
|
|
494
|
+
```typescript
|
|
495
|
+
const wishlist = createWishlistHelpers({
|
|
496
|
+
baseUrl: "https://store.example.com",
|
|
497
|
+
headers: { Cookie: "connect.sid=..." },
|
|
498
|
+
})
|
|
463
499
|
|
|
464
|
-
|
|
465
|
-
|
|
500
|
+
await wishlist.addToWishlist({ product_id: "prod_123" })
|
|
501
|
+
const { wishlist: items } = await wishlist.getWishlist({ includeDetails: true })
|
|
502
|
+
await wishlist.removeFromWishlist({ product_id: "prod_123" })
|
|
503
|
+
```
|
|
466
504
|
|
|
467
505
|
### Using Workflows Directly
|
|
468
506
|
|