@alphabite/medusa-wishlist 0.6.1 → 0.7.1

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.
@@ -28,102 +28,220 @@ const ProductWidget = ({ data: product }) => {
28
28
  adminSdk.defineWidgetConfig({
29
29
  zone: "product.details.before"
30
30
  });
31
- const QUERY_KEY$1 = ["wishlist", "settings"];
32
- const WishlistSettingsPage$1 = () => {
33
- const queryClient = reactQuery.useQueryClient();
34
- const { data, isLoading } = reactQuery.useQuery({
35
- queryKey: QUERY_KEY$1,
36
- queryFn: () => sdk.client.fetch("/admin/wishlists/settings", {
37
- method: "GET"
38
- })
31
+ const RANGE_PRESETS = {
32
+ "7": 7,
33
+ "30": 30,
34
+ "90": 90
35
+ };
36
+ const KpiCard = ({
37
+ label,
38
+ value,
39
+ delta
40
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 rounded-lg border p-4", children: [
41
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: label }),
42
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-baseline gap-2", children: [
43
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xlarge", weight: "plus", children: value }),
44
+ delta !== void 0 && /* @__PURE__ */ jsxRuntime.jsxs(
45
+ ui.Badge,
46
+ {
47
+ size: "2xsmall",
48
+ color: delta > 0 ? "green" : delta < 0 ? "red" : "grey",
49
+ children: [
50
+ delta > 0 ? "+" : "",
51
+ delta,
52
+ "%"
53
+ ]
54
+ }
55
+ )
56
+ ] })
57
+ ] });
58
+ const WishlistAnalyticsTab = () => {
59
+ var _a, _b;
60
+ const [rangeDays, setRangeDays] = react.useState("30");
61
+ const [channelId, setChannelId] = react.useState("all");
62
+ const { data: channelsRes } = reactQuery.useQuery({
63
+ queryKey: ["wishlist", "analytics", "channels"],
64
+ queryFn: () => sdk.admin.salesChannel.list({ limit: 100 })
39
65
  });
40
- const [allowGuest, setAllowGuest] = react.useState(false);
41
- const [allowMultiple, setAllowMultiple] = react.useState(false);
42
- react.useEffect(() => {
43
- if (data) {
44
- setAllowGuest(data.allow_guest_wishlist);
45
- setAllowMultiple(data.allow_multiple_wishlists);
46
- }
47
- }, [data]);
48
- const isDirty = data !== void 0 && (allowGuest !== data.allow_guest_wishlist || allowMultiple !== data.allow_multiple_wishlists);
49
- const update = reactQuery.useMutation({
50
- mutationFn: (patch) => sdk.client.fetch("/admin/wishlists/settings", {
51
- method: "PUT",
52
- body: patch
53
- }),
54
- onSuccess: (next) => {
55
- queryClient.setQueryData(QUERY_KEY$1, next);
56
- ui.toast.success("Wishlist settings updated");
57
- },
58
- onError: (err) => {
59
- const msg = err instanceof Error ? err.message : "Failed to update";
60
- ui.toast.error(msg);
66
+ const range = react.useMemo(() => {
67
+ const to = /* @__PURE__ */ new Date();
68
+ const from = new Date(
69
+ to.getTime() - RANGE_PRESETS[rangeDays] * 24 * 60 * 60 * 1e3
70
+ );
71
+ return { from: from.toISOString(), to: to.toISOString() };
72
+ }, [rangeDays]);
73
+ const { data, isLoading, isError } = reactQuery.useQuery({
74
+ queryKey: ["wishlist", "analytics", range, channelId],
75
+ queryFn: () => {
76
+ const params = new URLSearchParams({ from: range.from, to: range.to });
77
+ if (channelId !== "all") params.set("sales_channel_id", channelId);
78
+ return sdk.client.fetch(
79
+ `/admin/wishlists/analytics?${params.toString()}`,
80
+ { method: "GET" }
81
+ );
61
82
  }
62
83
  });
63
- const onSave = () => {
64
- if (!data) return;
65
- const patch = {};
66
- if (allowGuest !== data.allow_guest_wishlist) {
67
- patch.allow_guest_wishlist = allowGuest;
68
- }
69
- if (allowMultiple !== data.allow_multiple_wishlists) {
70
- patch.allow_multiple_wishlists = allowMultiple;
71
- }
72
- update.mutate(patch);
73
- };
74
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
75
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
76
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Wishlists" }),
77
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Control who can use wishlists in your storefront." })
78
- ] }) }),
79
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-6 px-6 py-6", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Loading…" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
80
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
81
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
82
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "allow-guest", className: "font-medium", children: "Allow guest wishlists" }),
83
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Anonymous visitors can save items to a wishlist." })
84
- ] }),
84
+ const maxTrend = react.useMemo(
85
+ () => Math.max(
86
+ 1,
87
+ ...(data == null ? void 0 : data.trend.map((t) => Math.max(t.wishlists, t.items))) ?? [1]
88
+ ),
89
+ [data]
90
+ );
91
+ if (isError) {
92
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-error", children: "Failed to load analytics." }) });
93
+ }
94
+ if (isLoading || !data) {
95
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Loading analytics…" }) });
96
+ }
97
+ const hasData = data.kpis.total_wishlists.value > 0 || data.trend.length > 0 || data.top_products.length > 0;
98
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6 px-6 py-6", children: [
99
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center", children: [
100
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full sm:w-48", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: rangeDays, onValueChange: setRangeDays, children: [
101
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, {}) }),
102
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
103
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "7", children: "Last 7 days" }),
104
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "30", children: "Last 30 days" }),
105
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "90", children: "Last 90 days" })
106
+ ] })
107
+ ] }) }),
108
+ (((_a = channelsRes == null ? void 0 : channelsRes.sales_channels) == null ? void 0 : _a.length) ?? 0) > 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full sm:w-64", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Select, { value: channelId, onValueChange: setChannelId, children: [
109
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "All sales channels" }) }),
110
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
111
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "all", children: "All sales channels" }),
112
+ (_b = channelsRes == null ? void 0 : channelsRes.sales_channels) == null ? void 0 : _b.map((c) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: c.id, children: c.name }, c.id))
113
+ ] })
114
+ ] }) })
115
+ ] }),
116
+ !hasData ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "No wishlist activity in this period yet." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
117
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: [
85
118
  /* @__PURE__ */ jsxRuntime.jsx(
86
- ui.Switch,
119
+ KpiCard,
87
120
  {
88
- id: "allow-guest",
89
- checked: allowGuest,
90
- onCheckedChange: setAllowGuest
121
+ label: "Total wishlists",
122
+ value: String(data.kpis.total_wishlists.value),
123
+ delta: data.kpis.total_wishlists.delta_pct
91
124
  }
92
- )
93
- ] }),
94
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
95
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
96
- /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "allow-multiple", className: "font-medium", children: "Allow multiple wishlists per customer" }),
97
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Signed-in customers can create more than one wishlist." })
98
- ] }),
125
+ ),
99
126
  /* @__PURE__ */ jsxRuntime.jsx(
100
- ui.Switch,
127
+ KpiCard,
101
128
  {
102
- id: "allow-multiple",
103
- checked: allowMultiple,
104
- onCheckedChange: setAllowMultiple
129
+ label: "Saved items",
130
+ value: String(data.kpis.total_items.value),
131
+ delta: data.kpis.total_items.delta_pct
132
+ }
133
+ ),
134
+ /* @__PURE__ */ jsxRuntime.jsx(
135
+ KpiCard,
136
+ {
137
+ label: "Avg items / wishlist",
138
+ value: String(data.kpis.avg_items_per_wishlist.value),
139
+ delta: data.kpis.avg_items_per_wishlist.delta_pct
140
+ }
141
+ ),
142
+ /* @__PURE__ */ jsxRuntime.jsx(
143
+ KpiCard,
144
+ {
145
+ label: "Active vs empty",
146
+ value: `${data.kpis.active_wishlists} / ${data.kpis.empty_wishlists}`
147
+ }
148
+ ),
149
+ /* @__PURE__ */ jsxRuntime.jsx(
150
+ KpiCard,
151
+ {
152
+ label: "Guest vs registered",
153
+ value: `${data.kpis.guest_wishlists} / ${data.kpis.registered_wishlists}`
154
+ }
155
+ ),
156
+ /* @__PURE__ */ jsxRuntime.jsx(
157
+ KpiCard,
158
+ {
159
+ label: "Unique customers",
160
+ value: String(data.kpis.unique_customers.value),
161
+ delta: data.kpis.unique_customers.delta_pct
105
162
  }
106
163
  )
107
164
  ] }),
108
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
109
- ui.Button,
110
- {
111
- variant: "primary",
112
- onClick: onSave,
113
- disabled: !isDirty || update.isPending,
114
- isLoading: update.isPending,
115
- children: "Save"
116
- }
117
- ) })
118
- ] }) })
165
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 rounded-lg border p-4", children: [
166
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Activity over time" }),
167
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-40 items-end gap-1 overflow-x-auto", children: data.trend.map((t) => /* @__PURE__ */ jsxRuntime.jsx(
168
+ "div",
169
+ {
170
+ className: "flex min-w-[10px] flex-1 flex-col items-center justify-end gap-0.5",
171
+ title: `${t.date}: ${t.wishlists} wishlists, ${t.items} items`,
172
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-32 w-full items-end justify-center gap-0.5", children: [
173
+ /* @__PURE__ */ jsxRuntime.jsx(
174
+ "div",
175
+ {
176
+ className: "w-1/2 rounded-t bg-ui-fg-interactive",
177
+ style: {
178
+ height: `${t.wishlists / maxTrend * 100}%`,
179
+ minHeight: t.wishlists > 0 ? "2px" : void 0
180
+ }
181
+ }
182
+ ),
183
+ /* @__PURE__ */ jsxRuntime.jsx(
184
+ "div",
185
+ {
186
+ className: "w-1/2 rounded-t bg-ui-fg-muted",
187
+ style: {
188
+ height: `${t.items / maxTrend * 100}%`,
189
+ minHeight: t.items > 0 ? "2px" : void 0
190
+ }
191
+ }
192
+ )
193
+ ] })
194
+ },
195
+ t.date
196
+ )) }),
197
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-4", children: [
198
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: "▮ Wishlists" }),
199
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "xsmall", className: "text-ui-fg-muted", children: "▮ Items" })
200
+ ] })
201
+ ] }),
202
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
203
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Most-wishlisted products (top 10)" }),
204
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
205
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
206
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Product" }),
207
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "Wishlists" }),
208
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "Items" })
209
+ ] }) }),
210
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: data.top_products.map((p) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
211
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
212
+ p.thumbnail ? /* @__PURE__ */ jsxRuntime.jsx(
213
+ "img",
214
+ {
215
+ src: p.thumbnail,
216
+ alt: "",
217
+ className: "h-8 w-8 rounded object-cover"
218
+ }
219
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 w-8 rounded bg-ui-bg-subtle" }),
220
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: p.title })
221
+ ] }) }),
222
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: p.wishlist_count }),
223
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: p.item_count })
224
+ ] }, p.product_id)) })
225
+ ] })
226
+ ] }),
227
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
228
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Most-wishlisted variants (top 10)" }),
229
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
230
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
231
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Variant" }),
232
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "text-right", children: "Wishlists" })
233
+ ] }) }),
234
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: data.top_variants.map((v) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
235
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: v.title }) }),
236
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { className: "text-right", children: v.wishlist_count })
237
+ ] }, v.product_variant_id)) })
238
+ ] })
239
+ ] })
240
+ ] })
119
241
  ] });
120
242
  };
121
- const config$1 = adminSdk.defineRouteConfig({
122
- label: "Wishlists",
123
- icon: icons.Heart
124
- });
125
243
  const QUERY_KEY = ["wishlist", "settings"];
126
- const WishlistSettingsPage = () => {
244
+ const WishlistSettingsTab = () => {
127
245
  const queryClient = reactQuery.useQueryClient();
128
246
  const { data, isLoading } = reactQuery.useQuery({
129
247
  queryKey: QUERY_KEY,
@@ -165,12 +283,9 @@ const WishlistSettingsPage = () => {
165
283
  }
166
284
  update.mutate(patch);
167
285
  };
168
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
169
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
170
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Wishlists" }),
171
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Control who can use wishlists in your storefront." })
172
- ] }) }),
173
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-6 px-6 py-6", children: isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Loading…" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
286
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6 px-6 py-6", children: [
287
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Control who can use wishlists in your storefront." }) }),
288
+ isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", children: "Loading…" }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
174
289
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
175
290
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
176
291
  /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "allow-guest", className: "font-medium", children: "Allow guest wishlists" }),
@@ -209,9 +324,22 @@ const WishlistSettingsPage = () => {
209
324
  children: "Save"
210
325
  }
211
326
  ) })
212
- ] }) })
327
+ ] })
213
328
  ] });
214
329
  };
330
+ const WishlistPage = () => {
331
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-0", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs, { defaultValue: "analytics", children: [
332
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 border-b px-6 py-4", children: [
333
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Wishlists" }),
334
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Tabs.List, { children: [
335
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "analytics", children: "Analytics" }),
336
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Trigger, { value: "settings", children: "Settings" })
337
+ ] })
338
+ ] }),
339
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "analytics", children: /* @__PURE__ */ jsxRuntime.jsx(WishlistAnalyticsTab, {}) }),
340
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Tabs.Content, { value: "settings", children: /* @__PURE__ */ jsxRuntime.jsx(WishlistSettingsTab, {}) })
341
+ ] }) });
342
+ };
215
343
  const config = adminSdk.defineRouteConfig({
216
344
  label: "Wishlists",
217
345
  icon: icons.Heart
@@ -225,29 +353,17 @@ const widgetModule = { widgets: [
225
353
  const routeModule = {
226
354
  routes: [
227
355
  {
228
- Component: WishlistSettingsPage$1,
356
+ Component: WishlistPage,
229
357
  path: "/wishlists"
230
- },
231
- {
232
- Component: WishlistSettingsPage,
233
- path: "/settings/wishlists"
234
358
  }
235
359
  ]
236
360
  };
237
361
  const menuItemModule = {
238
362
  menuItems: [
239
- {
240
- label: config$1.label,
241
- icon: config$1.icon,
242
- path: "/wishlists",
243
- nested: void 0,
244
- rank: void 0,
245
- translationNs: void 0
246
- },
247
363
  {
248
364
  label: config.label,
249
365
  icon: config.icon,
250
- path: "/settings/wishlists",
366
+ path: "/wishlists",
251
367
  nested: void 0,
252
368
  rank: void 0,
253
369
  translationNs: void 0
@@ -1,10 +1,10 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
3
- import { Container, Heading, Text, toast, Label, Switch, Button } from "@medusajs/ui";
3
+ import { Container, Heading, Text, Select, Table, Badge, toast, Label, Switch, Button, Tabs } from "@medusajs/ui";
4
4
  import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query";
5
5
  import Medusa from "@medusajs/js-sdk";
6
6
  import { Heart } from "@medusajs/icons";
7
- import { useState, useEffect } from "react";
7
+ import { useState, useMemo, useEffect } from "react";
8
8
  const sdk = new Medusa({
9
9
  baseUrl: __BACKEND_URL__ || "http://localhost:9000",
10
10
  debug: process.env.NODE_ENV === "development",
@@ -25,102 +25,220 @@ const ProductWidget = ({ data: product }) => {
25
25
  defineWidgetConfig({
26
26
  zone: "product.details.before"
27
27
  });
28
- const QUERY_KEY$1 = ["wishlist", "settings"];
29
- const WishlistSettingsPage$1 = () => {
30
- const queryClient = useQueryClient();
31
- const { data, isLoading } = useQuery({
32
- queryKey: QUERY_KEY$1,
33
- queryFn: () => sdk.client.fetch("/admin/wishlists/settings", {
34
- method: "GET"
35
- })
28
+ const RANGE_PRESETS = {
29
+ "7": 7,
30
+ "30": 30,
31
+ "90": 90
32
+ };
33
+ const KpiCard = ({
34
+ label,
35
+ value,
36
+ delta
37
+ }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1 rounded-lg border p-4", children: [
38
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: label }),
39
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2", children: [
40
+ /* @__PURE__ */ jsx(Text, { size: "xlarge", weight: "plus", children: value }),
41
+ delta !== void 0 && /* @__PURE__ */ jsxs(
42
+ Badge,
43
+ {
44
+ size: "2xsmall",
45
+ color: delta > 0 ? "green" : delta < 0 ? "red" : "grey",
46
+ children: [
47
+ delta > 0 ? "+" : "",
48
+ delta,
49
+ "%"
50
+ ]
51
+ }
52
+ )
53
+ ] })
54
+ ] });
55
+ const WishlistAnalyticsTab = () => {
56
+ var _a, _b;
57
+ const [rangeDays, setRangeDays] = useState("30");
58
+ const [channelId, setChannelId] = useState("all");
59
+ const { data: channelsRes } = useQuery({
60
+ queryKey: ["wishlist", "analytics", "channels"],
61
+ queryFn: () => sdk.admin.salesChannel.list({ limit: 100 })
36
62
  });
37
- const [allowGuest, setAllowGuest] = useState(false);
38
- const [allowMultiple, setAllowMultiple] = useState(false);
39
- useEffect(() => {
40
- if (data) {
41
- setAllowGuest(data.allow_guest_wishlist);
42
- setAllowMultiple(data.allow_multiple_wishlists);
43
- }
44
- }, [data]);
45
- const isDirty = data !== void 0 && (allowGuest !== data.allow_guest_wishlist || allowMultiple !== data.allow_multiple_wishlists);
46
- const update = useMutation({
47
- mutationFn: (patch) => sdk.client.fetch("/admin/wishlists/settings", {
48
- method: "PUT",
49
- body: patch
50
- }),
51
- onSuccess: (next) => {
52
- queryClient.setQueryData(QUERY_KEY$1, next);
53
- toast.success("Wishlist settings updated");
54
- },
55
- onError: (err) => {
56
- const msg = err instanceof Error ? err.message : "Failed to update";
57
- toast.error(msg);
63
+ const range = useMemo(() => {
64
+ const to = /* @__PURE__ */ new Date();
65
+ const from = new Date(
66
+ to.getTime() - RANGE_PRESETS[rangeDays] * 24 * 60 * 60 * 1e3
67
+ );
68
+ return { from: from.toISOString(), to: to.toISOString() };
69
+ }, [rangeDays]);
70
+ const { data, isLoading, isError } = useQuery({
71
+ queryKey: ["wishlist", "analytics", range, channelId],
72
+ queryFn: () => {
73
+ const params = new URLSearchParams({ from: range.from, to: range.to });
74
+ if (channelId !== "all") params.set("sales_channel_id", channelId);
75
+ return sdk.client.fetch(
76
+ `/admin/wishlists/analytics?${params.toString()}`,
77
+ { method: "GET" }
78
+ );
58
79
  }
59
80
  });
60
- const onSave = () => {
61
- if (!data) return;
62
- const patch = {};
63
- if (allowGuest !== data.allow_guest_wishlist) {
64
- patch.allow_guest_wishlist = allowGuest;
65
- }
66
- if (allowMultiple !== data.allow_multiple_wishlists) {
67
- patch.allow_multiple_wishlists = allowMultiple;
68
- }
69
- update.mutate(patch);
70
- };
71
- return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
72
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { children: [
73
- /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Wishlists" }),
74
- /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Control who can use wishlists in your storefront." })
75
- ] }) }),
76
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-6 px-6 py-6", children: isLoading ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Loading…" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
77
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
78
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
79
- /* @__PURE__ */ jsx(Label, { htmlFor: "allow-guest", className: "font-medium", children: "Allow guest wishlists" }),
80
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Anonymous visitors can save items to a wishlist." })
81
- ] }),
81
+ const maxTrend = useMemo(
82
+ () => Math.max(
83
+ 1,
84
+ ...(data == null ? void 0 : data.trend.map((t) => Math.max(t.wishlists, t.items))) ?? [1]
85
+ ),
86
+ [data]
87
+ );
88
+ if (isError) {
89
+ return /* @__PURE__ */ jsx("div", { className: "px-6 py-6", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-error", children: "Failed to load analytics." }) });
90
+ }
91
+ if (isLoading || !data) {
92
+ return /* @__PURE__ */ jsx("div", { className: "px-6 py-6", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Loading analytics…" }) });
93
+ }
94
+ const hasData = data.kpis.total_wishlists.value > 0 || data.trend.length > 0 || data.top_products.length > 0;
95
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 px-6 py-6", children: [
96
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center", children: [
97
+ /* @__PURE__ */ jsx("div", { className: "w-full sm:w-48", children: /* @__PURE__ */ jsxs(Select, { value: rangeDays, onValueChange: setRangeDays, children: [
98
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, {}) }),
99
+ /* @__PURE__ */ jsxs(Select.Content, { children: [
100
+ /* @__PURE__ */ jsx(Select.Item, { value: "7", children: "Last 7 days" }),
101
+ /* @__PURE__ */ jsx(Select.Item, { value: "30", children: "Last 30 days" }),
102
+ /* @__PURE__ */ jsx(Select.Item, { value: "90", children: "Last 90 days" })
103
+ ] })
104
+ ] }) }),
105
+ (((_a = channelsRes == null ? void 0 : channelsRes.sales_channels) == null ? void 0 : _a.length) ?? 0) > 1 && /* @__PURE__ */ jsx("div", { className: "w-full sm:w-64", children: /* @__PURE__ */ jsxs(Select, { value: channelId, onValueChange: setChannelId, children: [
106
+ /* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "All sales channels" }) }),
107
+ /* @__PURE__ */ jsxs(Select.Content, { children: [
108
+ /* @__PURE__ */ jsx(Select.Item, { value: "all", children: "All sales channels" }),
109
+ (_b = channelsRes == null ? void 0 : channelsRes.sales_channels) == null ? void 0 : _b.map((c) => /* @__PURE__ */ jsx(Select.Item, { value: c.id, children: c.name }, c.id))
110
+ ] })
111
+ ] }) })
112
+ ] }),
113
+ !hasData ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "No wishlist activity in this period yet." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
114
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3", children: [
82
115
  /* @__PURE__ */ jsx(
83
- Switch,
116
+ KpiCard,
84
117
  {
85
- id: "allow-guest",
86
- checked: allowGuest,
87
- onCheckedChange: setAllowGuest
118
+ label: "Total wishlists",
119
+ value: String(data.kpis.total_wishlists.value),
120
+ delta: data.kpis.total_wishlists.delta_pct
88
121
  }
89
- )
90
- ] }),
91
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
92
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
93
- /* @__PURE__ */ jsx(Label, { htmlFor: "allow-multiple", className: "font-medium", children: "Allow multiple wishlists per customer" }),
94
- /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Signed-in customers can create more than one wishlist." })
95
- ] }),
122
+ ),
96
123
  /* @__PURE__ */ jsx(
97
- Switch,
124
+ KpiCard,
98
125
  {
99
- id: "allow-multiple",
100
- checked: allowMultiple,
101
- onCheckedChange: setAllowMultiple
126
+ label: "Saved items",
127
+ value: String(data.kpis.total_items.value),
128
+ delta: data.kpis.total_items.delta_pct
129
+ }
130
+ ),
131
+ /* @__PURE__ */ jsx(
132
+ KpiCard,
133
+ {
134
+ label: "Avg items / wishlist",
135
+ value: String(data.kpis.avg_items_per_wishlist.value),
136
+ delta: data.kpis.avg_items_per_wishlist.delta_pct
137
+ }
138
+ ),
139
+ /* @__PURE__ */ jsx(
140
+ KpiCard,
141
+ {
142
+ label: "Active vs empty",
143
+ value: `${data.kpis.active_wishlists} / ${data.kpis.empty_wishlists}`
144
+ }
145
+ ),
146
+ /* @__PURE__ */ jsx(
147
+ KpiCard,
148
+ {
149
+ label: "Guest vs registered",
150
+ value: `${data.kpis.guest_wishlists} / ${data.kpis.registered_wishlists}`
151
+ }
152
+ ),
153
+ /* @__PURE__ */ jsx(
154
+ KpiCard,
155
+ {
156
+ label: "Unique customers",
157
+ value: String(data.kpis.unique_customers.value),
158
+ delta: data.kpis.unique_customers.delta_pct
102
159
  }
103
160
  )
104
161
  ] }),
105
- /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
106
- Button,
107
- {
108
- variant: "primary",
109
- onClick: onSave,
110
- disabled: !isDirty || update.isPending,
111
- isLoading: update.isPending,
112
- children: "Save"
113
- }
114
- ) })
115
- ] }) })
162
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 rounded-lg border p-4", children: [
163
+ /* @__PURE__ */ jsx(Text, { size: "small", weight: "plus", children: "Activity over time" }),
164
+ /* @__PURE__ */ jsx("div", { className: "flex h-40 items-end gap-1 overflow-x-auto", children: data.trend.map((t) => /* @__PURE__ */ jsx(
165
+ "div",
166
+ {
167
+ className: "flex min-w-[10px] flex-1 flex-col items-center justify-end gap-0.5",
168
+ title: `${t.date}: ${t.wishlists} wishlists, ${t.items} items`,
169
+ children: /* @__PURE__ */ jsxs("div", { className: "flex h-32 w-full items-end justify-center gap-0.5", children: [
170
+ /* @__PURE__ */ jsx(
171
+ "div",
172
+ {
173
+ className: "w-1/2 rounded-t bg-ui-fg-interactive",
174
+ style: {
175
+ height: `${t.wishlists / maxTrend * 100}%`,
176
+ minHeight: t.wishlists > 0 ? "2px" : void 0
177
+ }
178
+ }
179
+ ),
180
+ /* @__PURE__ */ jsx(
181
+ "div",
182
+ {
183
+ className: "w-1/2 rounded-t bg-ui-fg-muted",
184
+ style: {
185
+ height: `${t.items / maxTrend * 100}%`,
186
+ minHeight: t.items > 0 ? "2px" : void 0
187
+ }
188
+ }
189
+ )
190
+ ] })
191
+ },
192
+ t.date
193
+ )) }),
194
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
195
+ /* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-subtle", children: "▮ Wishlists" }),
196
+ /* @__PURE__ */ jsx(Text, { size: "xsmall", className: "text-ui-fg-muted", children: "▮ Items" })
197
+ ] })
198
+ ] }),
199
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
200
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Most-wishlisted products (top 10)" }),
201
+ /* @__PURE__ */ jsxs(Table, { children: [
202
+ /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
203
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Product" }),
204
+ /* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "Wishlists" }),
205
+ /* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "Items" })
206
+ ] }) }),
207
+ /* @__PURE__ */ jsx(Table.Body, { children: data.top_products.map((p) => /* @__PURE__ */ jsxs(Table.Row, { children: [
208
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
209
+ p.thumbnail ? /* @__PURE__ */ jsx(
210
+ "img",
211
+ {
212
+ src: p.thumbnail,
213
+ alt: "",
214
+ className: "h-8 w-8 rounded object-cover"
215
+ }
216
+ ) : /* @__PURE__ */ jsx("div", { className: "h-8 w-8 rounded bg-ui-bg-subtle" }),
217
+ /* @__PURE__ */ jsx(Text, { size: "small", children: p.title })
218
+ ] }) }),
219
+ /* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: p.wishlist_count }),
220
+ /* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: p.item_count })
221
+ ] }, p.product_id)) })
222
+ ] })
223
+ ] }),
224
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
225
+ /* @__PURE__ */ jsx(Heading, { level: "h3", children: "Most-wishlisted variants (top 10)" }),
226
+ /* @__PURE__ */ jsxs(Table, { children: [
227
+ /* @__PURE__ */ jsx(Table.Header, { children: /* @__PURE__ */ jsxs(Table.Row, { children: [
228
+ /* @__PURE__ */ jsx(Table.HeaderCell, { children: "Variant" }),
229
+ /* @__PURE__ */ jsx(Table.HeaderCell, { className: "text-right", children: "Wishlists" })
230
+ ] }) }),
231
+ /* @__PURE__ */ jsx(Table.Body, { children: data.top_variants.map((v) => /* @__PURE__ */ jsxs(Table.Row, { children: [
232
+ /* @__PURE__ */ jsx(Table.Cell, { children: /* @__PURE__ */ jsx(Text, { size: "small", children: v.title }) }),
233
+ /* @__PURE__ */ jsx(Table.Cell, { className: "text-right", children: v.wishlist_count })
234
+ ] }, v.product_variant_id)) })
235
+ ] })
236
+ ] })
237
+ ] })
116
238
  ] });
117
239
  };
118
- const config$1 = defineRouteConfig({
119
- label: "Wishlists",
120
- icon: Heart
121
- });
122
240
  const QUERY_KEY = ["wishlist", "settings"];
123
- const WishlistSettingsPage = () => {
241
+ const WishlistSettingsTab = () => {
124
242
  const queryClient = useQueryClient();
125
243
  const { data, isLoading } = useQuery({
126
244
  queryKey: QUERY_KEY,
@@ -162,12 +280,9 @@ const WishlistSettingsPage = () => {
162
280
  }
163
281
  update.mutate(patch);
164
282
  };
165
- return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
166
- /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxs("div", { children: [
167
- /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Wishlists" }),
168
- /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Control who can use wishlists in your storefront." })
169
- ] }) }),
170
- /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-6 px-6 py-6", children: isLoading ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Loading…" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
283
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 px-6 py-6", children: [
284
+ /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Control who can use wishlists in your storefront." }) }),
285
+ isLoading ? /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", children: "Loading…" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
171
286
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
172
287
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
173
288
  /* @__PURE__ */ jsx(Label, { htmlFor: "allow-guest", className: "font-medium", children: "Allow guest wishlists" }),
@@ -206,9 +321,22 @@ const WishlistSettingsPage = () => {
206
321
  children: "Save"
207
322
  }
208
323
  ) })
209
- ] }) })
324
+ ] })
210
325
  ] });
211
326
  };
327
+ const WishlistPage = () => {
328
+ return /* @__PURE__ */ jsx(Container, { className: "p-0", children: /* @__PURE__ */ jsxs(Tabs, { defaultValue: "analytics", children: [
329
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 border-b px-6 py-4", children: [
330
+ /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Wishlists" }),
331
+ /* @__PURE__ */ jsxs(Tabs.List, { children: [
332
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "analytics", children: "Analytics" }),
333
+ /* @__PURE__ */ jsx(Tabs.Trigger, { value: "settings", children: "Settings" })
334
+ ] })
335
+ ] }),
336
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "analytics", children: /* @__PURE__ */ jsx(WishlistAnalyticsTab, {}) }),
337
+ /* @__PURE__ */ jsx(Tabs.Content, { value: "settings", children: /* @__PURE__ */ jsx(WishlistSettingsTab, {}) })
338
+ ] }) });
339
+ };
212
340
  const config = defineRouteConfig({
213
341
  label: "Wishlists",
214
342
  icon: Heart
@@ -222,29 +350,17 @@ const widgetModule = { widgets: [
222
350
  const routeModule = {
223
351
  routes: [
224
352
  {
225
- Component: WishlistSettingsPage$1,
353
+ Component: WishlistPage,
226
354
  path: "/wishlists"
227
- },
228
- {
229
- Component: WishlistSettingsPage,
230
- path: "/settings/wishlists"
231
355
  }
232
356
  ]
233
357
  };
234
358
  const menuItemModule = {
235
359
  menuItems: [
236
- {
237
- label: config$1.label,
238
- icon: config$1.icon,
239
- path: "/wishlists",
240
- nested: void 0,
241
- rank: void 0,
242
- translationNs: void 0
243
- },
244
360
  {
245
361
  label: config.label,
246
362
  icon: config.icon,
247
- path: "/settings/wishlists",
363
+ path: "/wishlists",
248
364
  nested: void 0,
249
365
  rank: void 0,
250
366
  translationNs: void 0
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GET = GET;
4
+ const utils_1 = require("@medusajs/framework/utils");
5
+ const wishlist_1 = require("../../../../modules/wishlist");
6
+ const validators_1 = require("./validators");
7
+ async function GET(req, res) {
8
+ const logger = req.scope.resolve(utils_1.ContainerRegistrationKeys.LOGGER);
9
+ const query = req.scope.resolve(utils_1.ContainerRegistrationKeys.QUERY);
10
+ const service = req.scope.resolve(wishlist_1.WISHLIST_MODULE);
11
+ const parsed = validators_1.WishlistAnalyticsQuerySchema.safeParse(req.query);
12
+ if (!parsed.success) {
13
+ return res.status(400).json({
14
+ error: "Invalid analytics query",
15
+ issues: parsed.error.flatten(),
16
+ });
17
+ }
18
+ try {
19
+ const view = await service.getAnalytics(parsed.data);
20
+ const productIds = view.top_products.map((p) => p.product_id);
21
+ const variantIds = view.top_variants.map((v) => v.product_variant_id);
22
+ const [products, variants] = await Promise.all([
23
+ productIds.length
24
+ ? query.graph({
25
+ entity: "product",
26
+ fields: ["id", "title", "thumbnail"],
27
+ filters: { id: productIds },
28
+ })
29
+ : Promise.resolve({ data: [] }),
30
+ variantIds.length
31
+ ? query.graph({
32
+ entity: "product_variant",
33
+ fields: ["id", "title"],
34
+ filters: { id: variantIds },
35
+ })
36
+ : Promise.resolve({ data: [] }),
37
+ ]);
38
+ const productRows = products.data;
39
+ const variantRows = variants.data;
40
+ const productMap = new Map(productRows.map((p) => [p.id, p]));
41
+ const variantMap = new Map(variantRows.map((v) => [v.id, v]));
42
+ const response = {
43
+ ...view,
44
+ top_products: view.top_products.map((p) => {
45
+ const product = productMap.get(p.product_id);
46
+ return {
47
+ ...p,
48
+ title: product?.title ?? p.product_id,
49
+ thumbnail: product?.thumbnail ?? null,
50
+ };
51
+ }),
52
+ top_variants: view.top_variants.map((v) => ({
53
+ ...v,
54
+ title: variantMap.get(v.product_variant_id)?.title ?? v.product_variant_id,
55
+ })),
56
+ };
57
+ return res.status(200).json(response);
58
+ }
59
+ catch (error) {
60
+ logger.error("[admin/wishlists/analytics] GET error:", error);
61
+ return res.status(500).json({
62
+ error: "Failed to load wishlist analytics",
63
+ details: error instanceof Error ? error.message : "Unknown error",
64
+ });
65
+ }
66
+ }
67
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3dpc2hsaXN0cy9hbmFseXRpY3Mvcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFVQSxrQkFtRUM7QUE1RUQscURBQXNFO0FBQ3RFLDJEQUErRDtBQUUvRCw2Q0FBNEQ7QUFNckQsS0FBSyxVQUFVLEdBQUcsQ0FBQyxHQUFrQixFQUFFLEdBQW1CO0lBQy9ELE1BQU0sTUFBTSxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBQ25FLE1BQU0sS0FBSyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQ2pFLE1BQU0sT0FBTyxHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUF3QiwwQkFBZSxDQUFDLENBQUM7SUFFMUUsTUFBTSxNQUFNLEdBQUcseUNBQTRCLENBQUMsU0FBUyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztJQUNqRSxJQUFJLENBQUMsTUFBTSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ3BCLE9BQU8sR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7WUFDMUIsS0FBSyxFQUFFLHlCQUF5QjtZQUNoQyxNQUFNLEVBQUUsTUFBTSxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUU7U0FDL0IsQ0FBQyxDQUFDO0lBQ0wsQ0FBQztJQUVELElBQUksQ0FBQztRQUNILE1BQU0sSUFBSSxHQUFHLE1BQU0sT0FBTyxDQUFDLFlBQVksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFckQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxV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
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3dpc2hsaXN0cy9hbmFseXRpY3MvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IiJ9
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WishlistAnalyticsQuerySchema = void 0;
4
+ const zod_1 = require("@medusajs/framework/zod");
5
+ exports.WishlistAnalyticsQuerySchema = zod_1.z.object({
6
+ from: zod_1.z.string().datetime().optional(),
7
+ to: zod_1.z.string().datetime().optional(),
8
+ sales_channel_id: zod_1.z.string().optional(),
9
+ });
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvYWRtaW4vd2lzaGxpc3RzL2FuYWx5dGljcy92YWxpZGF0b3JzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLGlEQUE0QztBQUUvQixRQUFBLDRCQUE0QixHQUFHLE9BQUMsQ0FBQyxNQUFNLENBQUM7SUFDbkQsSUFBSSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDdEMsRUFBRSxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLEVBQUU7SUFDcEMsZ0JBQWdCLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRTtDQUN4QyxDQUFDLENBQUMifQ==
@@ -53,6 +53,173 @@ class WishlistModuleService extends (0, utils_1.MedusaService)({
53
53
  .where("wi.product_id = ?", [productId])
54
54
  .execute())?.length || 0);
55
55
  }
56
+ async getAnalytics(params = {}, context = {}) {
57
+ const knex = context.manager.getConnection().getKnex();
58
+ const scopeItemsToChannel = (qb) => {
59
+ if (channelId) {
60
+ qb.join("wishlist as w", "w.id", "wi.wishlist_id").where("w.sales_channel_id", channelId);
61
+ }
62
+ return qb;
63
+ };
64
+ const n = (v) => Number(v ?? 0);
65
+ const DAY_MS = 24 * 60 * 60 * 1000;
66
+ const to = params.to ? new Date(params.to) : new Date();
67
+ const from = params.from
68
+ ? new Date(params.from)
69
+ : new Date(to.getTime() - 30 * DAY_MS);
70
+ const periodMs = to.getTime() - from.getTime();
71
+ const prevFrom = new Date(from.getTime() - periodMs);
72
+ const prevTo = from;
73
+ const channelId = params.sales_channel_id;
74
+ const bucket = periodMs <= 60 * DAY_MS ? "day" : "week";
75
+ const mkDelta = (value, previous) => ({
76
+ value,
77
+ previous,
78
+ delta_pct: previous === 0
79
+ ? value > 0
80
+ ? 100
81
+ : 0
82
+ : Math.round(((value - previous) / previous) * 1000) / 10,
83
+ });
84
+ const countWishlists = async (lo, hi) => {
85
+ const qb = knex("wishlist")
86
+ .where("created_at", ">=", lo)
87
+ .andWhere("created_at", "<", hi);
88
+ if (channelId)
89
+ qb.where("sales_channel_id", channelId);
90
+ const row = await qb.count("* as c");
91
+ return n(row[0]?.c);
92
+ };
93
+ const countItems = async (lo, hi) => {
94
+ const qb = scopeItemsToChannel(knex("wishlist_item as wi")
95
+ .where("wi.created_at", ">=", lo)
96
+ .andWhere("wi.created_at", "<", hi));
97
+ const row = await qb.count("* as c");
98
+ return n(row[0]?.c);
99
+ };
100
+ const countUniqueCustomers = async (lo, hi) => {
101
+ const qb = knex("wishlist")
102
+ .where("created_at", ">=", lo)
103
+ .andWhere("created_at", "<", hi)
104
+ .whereNotNull("customer_id");
105
+ if (channelId)
106
+ qb.where("sales_channel_id", channelId);
107
+ const row = await qb.countDistinct("customer_id as c");
108
+ return n(row[0]?.c);
109
+ };
110
+ const [totalWishlists, prevTotalWishlists, totalItems, prevTotalItems, uniqueCustomers, prevUniqueCustomers,] = await Promise.all([
111
+ countWishlists(from, to),
112
+ countWishlists(prevFrom, prevTo),
113
+ countItems(from, to),
114
+ countItems(prevFrom, prevTo),
115
+ countUniqueCustomers(from, to),
116
+ countUniqueCustomers(prevFrom, prevTo),
117
+ ]);
118
+ const activeQb = knex("wishlist as w")
119
+ .where("w.created_at", ">=", from)
120
+ .andWhere("w.created_at", "<", to)
121
+ .whereExists(function () {
122
+ this.select(knex.raw("1"))
123
+ .from("wishlist_item as wi")
124
+ .where("wi.wishlist_id", knex.ref("w.id"));
125
+ });
126
+ if (channelId)
127
+ activeQb.where("w.sales_channel_id", channelId);
128
+ const activeWishlists = n((await activeQb.count("* as c"))[0]?.c);
129
+ const emptyWishlists = totalWishlists - activeWishlists;
130
+ const guestQb = knex("wishlist")
131
+ .where("created_at", ">=", from)
132
+ .andWhere("created_at", "<", to)
133
+ .whereNull("customer_id");
134
+ if (channelId)
135
+ guestQb.where("sales_channel_id", channelId);
136
+ const guestWishlists = n((await guestQb.count("* as c"))[0]?.c);
137
+ const registeredWishlists = totalWishlists - guestWishlists;
138
+ const wishlistTrendQb = knex("wishlist")
139
+ .where("created_at", ">=", from)
140
+ .andWhere("created_at", "<", to)
141
+ .select(knex.raw("to_char(date_trunc(?, created_at), 'YYYY-MM-DD') as date", [
142
+ bucket,
143
+ ]))
144
+ .count("* as count")
145
+ // Group/order by the SELECT ordinal: repeating date_trunc(?, ...) here
146
+ // would bind a *separate* parameter, which Postgres does not treat as the
147
+ // same expression as the one in SELECT ("must appear in GROUP BY").
148
+ .groupByRaw("1")
149
+ .orderByRaw("1");
150
+ if (channelId)
151
+ wishlistTrendQb.where("sales_channel_id", channelId);
152
+ const itemTrendQb = scopeItemsToChannel(knex("wishlist_item as wi")
153
+ .where("wi.created_at", ">=", from)
154
+ .andWhere("wi.created_at", "<", to)
155
+ .select(knex.raw("to_char(date_trunc(?, wi.created_at), 'YYYY-MM-DD') as date", [bucket]))
156
+ .count("* as count")
157
+ .groupByRaw("1")
158
+ .orderByRaw("1"));
159
+ const [wishlistTrendRows, itemTrendRows] = (await Promise.all([
160
+ wishlistTrendQb,
161
+ itemTrendQb,
162
+ ]));
163
+ const trendMap = new Map();
164
+ for (const r of wishlistTrendRows) {
165
+ trendMap.set(r.date, { wishlists: n(r.count), items: 0 });
166
+ }
167
+ for (const r of itemTrendRows) {
168
+ const e = trendMap.get(r.date) ?? { wishlists: 0, items: 0 };
169
+ e.items = n(r.count);
170
+ trendMap.set(r.date, e);
171
+ }
172
+ const trend = [...trendMap.entries()]
173
+ .sort(([a], [b]) => a.localeCompare(b))
174
+ .map(([date, v]) => ({ date, wishlists: v.wishlists, items: v.items }));
175
+ const topProductsQb = scopeItemsToChannel(knex("wishlist_item as wi")
176
+ .where("wi.created_at", ">=", from)
177
+ .andWhere("wi.created_at", "<", to));
178
+ const topProductsRows = (await topProductsQb
179
+ .select("wi.product_id")
180
+ .countDistinct("wi.wishlist_id as wishlist_count")
181
+ .count("* as item_count")
182
+ .groupBy("wi.product_id")
183
+ .orderBy("wishlist_count", "desc")
184
+ .limit(10));
185
+ const topVariantsQb = scopeItemsToChannel(knex("wishlist_item as wi")
186
+ .where("wi.created_at", ">=", from)
187
+ .andWhere("wi.created_at", "<", to));
188
+ const topVariantsRows = (await topVariantsQb
189
+ .select("wi.product_variant_id", "wi.product_id")
190
+ .countDistinct("wi.wishlist_id as wishlist_count")
191
+ .groupBy("wi.product_variant_id", "wi.product_id")
192
+ .orderBy("wishlist_count", "desc")
193
+ .limit(10));
194
+ const avgCurrent = totalWishlists ? totalItems / totalWishlists : 0;
195
+ const avgPrev = prevTotalWishlists
196
+ ? prevTotalItems / prevTotalWishlists
197
+ : 0;
198
+ return {
199
+ range: { from: from.toISOString(), to: to.toISOString() },
200
+ kpis: {
201
+ total_wishlists: mkDelta(totalWishlists, prevTotalWishlists),
202
+ total_items: mkDelta(totalItems, prevTotalItems),
203
+ avg_items_per_wishlist: mkDelta(Math.round(avgCurrent * 100) / 100, Math.round(avgPrev * 100) / 100),
204
+ active_wishlists: activeWishlists,
205
+ empty_wishlists: emptyWishlists,
206
+ guest_wishlists: guestWishlists,
207
+ registered_wishlists: registeredWishlists,
208
+ unique_customers: mkDelta(uniqueCustomers, prevUniqueCustomers),
209
+ },
210
+ trend,
211
+ top_products: topProductsRows.map((r) => ({
212
+ product_id: r.product_id,
213
+ wishlist_count: n(r.wishlist_count),
214
+ item_count: n(r.item_count),
215
+ })),
216
+ top_variants: topVariantsRows.map((r) => ({
217
+ product_variant_id: r.product_variant_id,
218
+ product_id: r.product_id,
219
+ wishlist_count: n(r.wishlist_count),
220
+ })),
221
+ };
222
+ }
56
223
  async totalItemsCount({ customer_id, wishlist_id, }, context = {}) {
57
224
  const wishlist_items_count = await context.manager?.count(wishlist_item_1.WishlistItem, {
58
225
  wishlist: {
@@ -133,6 +300,13 @@ __decorate([
133
300
  __metadata("design:paramtypes", [String, Object]),
134
301
  __metadata("design:returntype", Promise)
135
302
  ], WishlistModuleService.prototype, "getWishlistCountsOfProduct", null);
303
+ __decorate([
304
+ (0, utils_2.InjectManager)(),
305
+ __param(1, (0, utils_1.MedusaContext)()),
306
+ __metadata("design:type", Function),
307
+ __metadata("design:paramtypes", [Object, Object]),
308
+ __metadata("design:returntype", Promise)
309
+ ], WishlistModuleService.prototype, "getAnalytics", null);
136
310
  __decorate([
137
311
  (0, utils_2.InjectManager)(),
138
312
  __param(1, (0, utils_1.MedusaContext)()),
@@ -140,4 +314,4 @@ __decorate([
140
314
  __metadata("design:paramtypes", [Object, Object]),
141
315
  __metadata("design:returntype", Promise)
142
316
  ], WishlistModuleService.prototype, "totalItemsCount", null);
143
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL3dpc2hsaXN0L3NlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxxREFJbUM7QUFDbkMsZ0RBQTZDO0FBQzdDLDBEQUFzRDtBQUN0RCxrRUFBOEQ7QUFDOUQscURBQTBEO0FBRzFELGdFQUErQjtBQUMvQixpREFBNEM7QUFPNUMsTUFBTSw4QkFBOEIsR0FBRyxlQUFlLENBQUM7QUEyQ3ZELE1BQU0sYUFBYSxHQUFHLE9BQUMsQ0FBQyxNQUFNLENBQUM7SUFDN0IsY0FBYyxFQUFFLE9BQUMsQ0FBQyxLQUFLLENBQUMsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsUUFBUSxFQUFFO0lBQzlDLG1CQUFtQixFQUFFLE9BQUMsQ0FBQyxLQUFLLENBQUMsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsUUFBUSxFQUFFO0lBQ25ELG9CQUFvQixFQUFFLE9BQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO0lBQ2hELHdCQUF3QixFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQy9DLGtCQUFrQixFQUFFLE9BQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO0lBQzlDLGdCQUFnQixFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUM7Q0FDdkQsQ0FBQyxDQUFDO0FBSUgsTUFBcUIscUJBQXNCLFNBQVEsSUFBQSxxQkFBYSxFQUFDO0lBQy9ELFFBQVEsRUFBUixtQkFBUTtJQUNSLFlBQVksRUFBWiw0QkFBWTtJQUNaLGdCQUFnQixFQUFoQixvQ0FBZ0I7Q0FDakIsQ0FBQztJQUdBLE1BQU0sQ0FBQyxlQUFlLENBQ3BCLFFBQTRDO1FBRTVDLE1BQU0s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
317
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9tb2R1bGVzL3dpc2hsaXN0L3NlcnZpY2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxxREFJbUM7QUFDbkMsZ0RBQTZDO0FBQzdDLDBEQUFzRDtBQUN0RCxrRUFBOEQ7QUFDOUQscURBQTBEO0FBSTFELGdFQUErQjtBQUMvQixpREFBNEM7QUFZNUMsTUFBTSw4QkFBOEIsR0FBRyxlQUFlLENBQUM7QUEyQ3ZELE1BQU0sYUFBYSxHQUFHLE9BQUMsQ0FBQyxNQUFNLENBQUM7SUFDN0IsY0FBYyxFQUFFLE9BQUMsQ0FBQyxLQUFLLENBQUMsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsUUFBUSxFQUFFO0lBQzlDLG1CQUFtQixFQUFFLE9BQUMsQ0FBQyxLQUFLLENBQUMsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUMsUUFBUSxFQUFFO0lBQ25ELG9CQUFvQixFQUFFLE9BQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO0lBQ2hELHdCQUF3QixFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDO0lBQy9DLGtCQUFrQixFQUFFLE9BQUMsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO0lBQzlDLGdCQUFnQixFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUM7Q0FDdkQsQ0FBQyxDQUFDO0FBSUgsTUFBcUIscUJBQXNCLFNBQVEsSUFBQSxxQkFBYSxFQUFDO0lBQy9ELFFBQVEsRUFBUixtQkFBUTtJQUNSLFlBQVksRUFBWiw0QkFBWTtJQUNaLGdCQUFnQixFQUFoQixvQ0FBZ0I7Q0FDakIsQ0FBQztJQUdBLE1BQU0sQ0FBQyxlQUFlLENBQ3BCLFFBQTRDO1FBRTVDLE1BQU0s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
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYW5hbHl0aWNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL21vZHVsZXMvd2lzaGxpc3QvdHlwZXMvYW5hbHl0aWNzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiIifQ==
@@ -3,6 +3,7 @@ import { EntityManager } from "@mikro-orm/knex";
3
3
  import { z } from "@medusajs/framework/zod";
4
4
  import { Wishlist as WishlistType } from "../../api/store/wishlists/types";
5
5
  import type { WishlistSettingsView, WishlistSettingsPatch } from "./types/settings";
6
+ import type { WishlistAnalyticsParams, WishlistAnalyticsView } from "./types/analytics";
6
7
  /**
7
8
  * Options for configuring the Alphabite Wishlist Plugin
8
9
  */
@@ -83,6 +84,7 @@ export default class WishlistModuleService extends WishlistModuleService_base {
83
84
  static validateOptions(_options: AlphabiteWishlistPluginOptionsType): void | never;
84
85
  constructor({}: {}, options: AlphabiteWishlistPluginOptionsType);
85
86
  getWishlistCountsOfProduct(productId: string, context?: Context<EntityManager>): Promise<number>;
87
+ getAnalytics(params?: WishlistAnalyticsParams, context?: Context<EntityManager>): Promise<WishlistAnalyticsView>;
86
88
  totalItemsCount({ customer_id, wishlist_id, }: {
87
89
  customer_id?: string;
88
90
  wishlist_id?: string;
@@ -0,0 +1,45 @@
1
+ export type WishlistAnalyticsParams = {
2
+ /** ISO date string; defaults to 30 days before `to`. */
3
+ from?: string;
4
+ /** ISO date string; defaults to now. */
5
+ to?: string;
6
+ /** Restrict all metrics to a single sales channel. */
7
+ sales_channel_id?: string;
8
+ };
9
+ export type Delta = {
10
+ value: number;
11
+ previous: number;
12
+ /** Percent change vs the previous equal-length period, rounded to 1 decimal. */
13
+ delta_pct: number;
14
+ };
15
+ export type WishlistAnalyticsView = {
16
+ range: {
17
+ from: string;
18
+ to: string;
19
+ };
20
+ kpis: {
21
+ total_wishlists: Delta;
22
+ total_items: Delta;
23
+ avg_items_per_wishlist: Delta;
24
+ active_wishlists: number;
25
+ empty_wishlists: number;
26
+ guest_wishlists: number;
27
+ registered_wishlists: number;
28
+ unique_customers: Delta;
29
+ };
30
+ trend: Array<{
31
+ date: string;
32
+ wishlists: number;
33
+ items: number;
34
+ }>;
35
+ top_products: Array<{
36
+ product_id: string;
37
+ wishlist_count: number;
38
+ item_count: number;
39
+ }>;
40
+ top_variants: Array<{
41
+ product_variant_id: string;
42
+ product_id: string;
43
+ wishlist_count: number;
44
+ }>;
45
+ };
package/README.md CHANGED
@@ -47,6 +47,7 @@ npm install @alphabite/medusa-wishlist@0.5.7
47
47
  - [✨ Features](#-features)
48
48
  - [📦 Installation](#-installation)
49
49
  - [🔧 Plugin Options](#-plugin-options)
50
+ - [🛠️ Admin](#-admin)
50
51
  - [📦 API Endpoints](#-api-endpoints)
51
52
  - [🧑‍💻 SDK Usage](#-sdk-usage)
52
53
  - [🧪 Guest Wishlist Flow](#-guest-wishlist-flow)
@@ -63,6 +64,7 @@ npm install @alphabite/medusa-wishlist@0.5.7
63
64
  - ✅ Guest wishlist supported + transfer when registered
64
65
  - ✅ Fully typed Medusa JS SDK integration with our SDK client
65
66
  - ✅ Pagination and filtering built-in
67
+ - ✅ Admin page with **Settings** + **Analytics** tabs
66
68
 
67
69
  ---
68
70
 
@@ -106,6 +108,18 @@ const plugins = [
106
108
 
107
109
  ---
108
110
 
111
+ ## 🛠️ Admin
112
+
113
+ The plugin adds a **Wishlists** page to the admin sidebar (`/app/wishlists`) with
114
+ two tabs: **Settings** (guest / multiple-wishlist toggles) and **Analytics** (a
115
+ read-only dashboard of KPIs, an activity trend, and the most-wishlisted products
116
+ and variants).
117
+
118
+ See [docs/admin-analytics.md](docs/admin-analytics.md) for the analytics endpoint
119
+ and response shape.
120
+
121
+ ---
122
+
109
123
  ## 📦 API Endpoints
110
124
 
111
125
  All endpoints are available under `/store/wishlists`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alphabite/medusa-wishlist",
3
- "version": "0.6.1",
3
+ "version": "0.7.1",
4
4
  "description": "Alphabite's Medusa Wishlist Plugin",
5
5
  "author": "Alphabite (https://alphabite.io)",
6
6
  "license": "MIT",