medusa-contact-us 0.0.3 → 0.0.11
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 +176 -1
- package/.medusa/server/src/admin/index.mjs +176 -1
- package/.medusa/server/src/api/admin/contact-email-subscriptions/route.js +29 -0
- package/.medusa/server/src/api/admin/contact-email-subscriptions/validators.js +12 -0
- package/.medusa/server/src/api/store/contact-email-subscriptions/route.js +19 -0
- package/.medusa/server/src/api/store/contact-email-subscriptions/validators.js +15 -0
- package/.medusa/server/src/constants.js +3 -2
- package/.medusa/server/src/helpers/__tests__/contact-subscription.test.js +109 -0
- package/.medusa/server/src/helpers/__tests__/submit-contact-request.test.js +33 -1
- package/.medusa/server/src/helpers/base-client.js +36 -0
- package/.medusa/server/src/helpers/contact-subscription.js +45 -0
- package/.medusa/server/src/helpers/index.js +5 -2
- package/.medusa/server/src/helpers/submit-contact-request.js +6 -35
- package/.medusa/server/src/index.js +7 -2
- package/.medusa/server/src/modules/contact-requests/migrations/Migration20241124090000.js +7 -2
- package/.medusa/server/src/modules/contact-requests/models/contact-request-comment.js +1 -2
- package/.medusa/server/src/modules/contact-requests/models/contact-request.js +1 -3
- package/.medusa/server/src/modules/contact-subscriptions/index.js +15 -0
- package/.medusa/server/src/modules/contact-subscriptions/migrations/Migration20241126103000.js +38 -0
- package/.medusa/server/src/modules/contact-subscriptions/models/contact-email-subscription.js +13 -0
- package/.medusa/server/src/modules/contact-subscriptions/service.js +57 -0
- package/README.md +107 -16
- package/package.json +1 -1
|
@@ -1,10 +1,175 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const jsxRuntime = require("react/jsx-runtime");
|
|
3
3
|
const react = require("react");
|
|
4
|
-
const reactRouterDom = require("react-router-dom");
|
|
5
4
|
const adminSdk = require("@medusajs/admin-sdk");
|
|
6
5
|
const ui = require("@medusajs/ui");
|
|
7
6
|
const icons = require("@medusajs/icons");
|
|
7
|
+
const reactRouterDom = require("react-router-dom");
|
|
8
|
+
const useDebounce$1 = (value, delay) => {
|
|
9
|
+
const [debouncedValue, setDebouncedValue] = react.useState(value);
|
|
10
|
+
react.useEffect(() => {
|
|
11
|
+
const handler = setTimeout(() => setDebouncedValue(value), delay);
|
|
12
|
+
return () => clearTimeout(handler);
|
|
13
|
+
}, [value, delay]);
|
|
14
|
+
return debouncedValue;
|
|
15
|
+
};
|
|
16
|
+
const badgeClass = (status) => {
|
|
17
|
+
if (status === "subscribed") {
|
|
18
|
+
return "bg-ui-tag-green-bg text-ui-tag-green-text";
|
|
19
|
+
}
|
|
20
|
+
return "bg-ui-tag-red-bg text-ui-tag-red-text";
|
|
21
|
+
};
|
|
22
|
+
const statusFilters = [
|
|
23
|
+
{ value: "all", label: "All" },
|
|
24
|
+
{ value: "subscribed", label: "Subscribed" },
|
|
25
|
+
{ value: "unsubscribed", label: "Unsubscribed" }
|
|
26
|
+
];
|
|
27
|
+
const ContactEmailSubscriptionsPage = () => {
|
|
28
|
+
const [items, setItems] = react.useState([]);
|
|
29
|
+
const [statusFilter, setStatusFilter] = react.useState("all");
|
|
30
|
+
const [query, setQuery] = react.useState("");
|
|
31
|
+
const debouncedQuery = useDebounce$1(query, 300);
|
|
32
|
+
const [isLoading, setIsLoading] = react.useState(true);
|
|
33
|
+
const [isFetchingMore, setIsFetchingMore] = react.useState(false);
|
|
34
|
+
const [error, setError] = react.useState(null);
|
|
35
|
+
const [offset, setOffset] = react.useState(0);
|
|
36
|
+
const [count, setCount] = react.useState(0);
|
|
37
|
+
const limit = 50;
|
|
38
|
+
const loadSubscriptions = react.useCallback(
|
|
39
|
+
async (nextOffset, replace = false) => {
|
|
40
|
+
var _a;
|
|
41
|
+
try {
|
|
42
|
+
if (replace) {
|
|
43
|
+
setIsLoading(true);
|
|
44
|
+
} else {
|
|
45
|
+
setIsFetchingMore(true);
|
|
46
|
+
}
|
|
47
|
+
setError(null);
|
|
48
|
+
const params = new URLSearchParams();
|
|
49
|
+
params.set("limit", String(limit));
|
|
50
|
+
params.set("offset", String(nextOffset));
|
|
51
|
+
if (statusFilter !== "all") {
|
|
52
|
+
params.set("status", statusFilter);
|
|
53
|
+
}
|
|
54
|
+
if (debouncedQuery.trim()) {
|
|
55
|
+
params.set("q", debouncedQuery.trim());
|
|
56
|
+
}
|
|
57
|
+
const response = await fetch(
|
|
58
|
+
`/admin/contact-email-subscriptions?${params.toString()}`,
|
|
59
|
+
{ credentials: "include" }
|
|
60
|
+
);
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
const message = await response.text();
|
|
63
|
+
throw new Error(message || "Unable to load subscriptions");
|
|
64
|
+
}
|
|
65
|
+
const payload = await response.json();
|
|
66
|
+
setCount(payload.count ?? 0);
|
|
67
|
+
setOffset(nextOffset + (((_a = payload.subscriptions) == null ? void 0 : _a.length) ?? 0));
|
|
68
|
+
setItems(
|
|
69
|
+
(prev) => replace ? payload.subscriptions ?? [] : [...prev, ...payload.subscriptions ?? []]
|
|
70
|
+
);
|
|
71
|
+
} catch (loadError) {
|
|
72
|
+
const message = loadError instanceof Error ? loadError.message : "Unable to load subscriptions";
|
|
73
|
+
setError(message);
|
|
74
|
+
} finally {
|
|
75
|
+
setIsLoading(false);
|
|
76
|
+
setIsFetchingMore(false);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
[statusFilter, debouncedQuery]
|
|
80
|
+
);
|
|
81
|
+
react.useEffect(() => {
|
|
82
|
+
void loadSubscriptions(0, true);
|
|
83
|
+
}, [statusFilter, debouncedQuery, loadSubscriptions]);
|
|
84
|
+
const hasMore = react.useMemo(() => offset < count, [offset, count]);
|
|
85
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "mx-auto flex w-full max-w-5xl flex-col gap-6 p-6", children: [
|
|
86
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
|
|
87
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
88
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Contact email list" }),
|
|
89
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Track opt-ins and unsubscribes collected from the storefront helper." })
|
|
90
|
+
] }),
|
|
91
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "primary", onClick: () => loadSubscriptions(0, true), children: "Refresh" })
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
|
|
94
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
95
|
+
ui.Input,
|
|
96
|
+
{
|
|
97
|
+
placeholder: "Search email",
|
|
98
|
+
value: query,
|
|
99
|
+
onChange: (event) => setQuery(event.target.value),
|
|
100
|
+
className: "md:max-w-sm"
|
|
101
|
+
}
|
|
102
|
+
),
|
|
103
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
104
|
+
"select",
|
|
105
|
+
{
|
|
106
|
+
value: statusFilter,
|
|
107
|
+
onChange: (event) => setStatusFilter(event.target.value),
|
|
108
|
+
className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive md:max-w-xs",
|
|
109
|
+
children: statusFilters.map((option) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: option.value, children: option.label }, option.value))
|
|
110
|
+
}
|
|
111
|
+
)
|
|
112
|
+
] }),
|
|
113
|
+
error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-ui-border-strong p-6 text-center", children: [
|
|
114
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { weight: "plus", className: "text-ui-fg-error", children: error }),
|
|
115
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
116
|
+
ui.Button,
|
|
117
|
+
{
|
|
118
|
+
variant: "secondary",
|
|
119
|
+
onClick: () => loadSubscriptions(0, true),
|
|
120
|
+
children: "Try again"
|
|
121
|
+
}
|
|
122
|
+
) })
|
|
123
|
+
] }) : null,
|
|
124
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Loading email list..." }) }) : items.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-dashed border-ui-border-strong p-10 text-center", children: [
|
|
125
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", className: "text-xl", children: "No entries yet" }),
|
|
126
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "mt-2 text-ui-fg-subtle", children: "Submissions created through the storefront helper will appear here with their latest consent state." })
|
|
127
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-hidden rounded-xl border border-ui-border-base", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
|
|
128
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { className: "bg-ui-bg-subtle", children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
129
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Email" }),
|
|
130
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Status" }),
|
|
131
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Source" }),
|
|
132
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Last updated" })
|
|
133
|
+
] }) }),
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: items.map((subscription) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "hover:bg-ui-bg-subtle/60", children: [
|
|
135
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 font-medium text-ui-fg-base", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
136
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: subscription.email }),
|
|
137
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: subscription.id })
|
|
138
|
+
] }) }),
|
|
139
|
+
/* @__PURE__ */ jsxRuntime.jsxs("td", { className: "px-4 py-4", children: [
|
|
140
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
141
|
+
ui.Badge,
|
|
142
|
+
{
|
|
143
|
+
size: "2xsmall",
|
|
144
|
+
className: `uppercase ${badgeClass(subscription.status)}`,
|
|
145
|
+
children: subscription.status
|
|
146
|
+
}
|
|
147
|
+
),
|
|
148
|
+
subscription.unsubscribed_at ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "mt-1 block text-ui-fg-subtle", children: [
|
|
149
|
+
"Unsubscribed on",
|
|
150
|
+
" ",
|
|
151
|
+
new Date(subscription.unsubscribed_at).toLocaleString()
|
|
152
|
+
] }) : null
|
|
153
|
+
] }),
|
|
154
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: subscription.source ?? "storefront" }),
|
|
155
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: new Date(subscription.updated_at).toLocaleString() })
|
|
156
|
+
] }, subscription.id)) })
|
|
157
|
+
] }) }),
|
|
158
|
+
hasMore ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
159
|
+
ui.Button,
|
|
160
|
+
{
|
|
161
|
+
variant: "secondary",
|
|
162
|
+
isLoading: isFetchingMore,
|
|
163
|
+
onClick: () => loadSubscriptions(offset, false),
|
|
164
|
+
children: "Load more"
|
|
165
|
+
}
|
|
166
|
+
) }) : null
|
|
167
|
+
] }) });
|
|
168
|
+
};
|
|
169
|
+
const config$1 = adminSdk.defineRouteConfig({
|
|
170
|
+
label: "Contact email list",
|
|
171
|
+
icon: icons.Envelope
|
|
172
|
+
});
|
|
8
173
|
const useDebounce = (value, delay) => {
|
|
9
174
|
const [debouncedValue, setDebouncedValue] = react.useState(value);
|
|
10
175
|
react.useEffect(() => {
|
|
@@ -487,6 +652,10 @@ const i18nTranslations0 = {
|
|
|
487
652
|
const widgetModule = { widgets: [] };
|
|
488
653
|
const routeModule = {
|
|
489
654
|
routes: [
|
|
655
|
+
{
|
|
656
|
+
Component: ContactEmailSubscriptionsPage,
|
|
657
|
+
path: "/contact-email-subscriptions"
|
|
658
|
+
},
|
|
490
659
|
{
|
|
491
660
|
Component: ContactRequestsPage,
|
|
492
661
|
path: "/contact-requests"
|
|
@@ -504,6 +673,12 @@ const menuItemModule = {
|
|
|
504
673
|
icon: config.icon,
|
|
505
674
|
path: "/contact-requests",
|
|
506
675
|
nested: void 0
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
label: config$1.label,
|
|
679
|
+
icon: config$1.icon,
|
|
680
|
+
path: "/contact-email-subscriptions",
|
|
681
|
+
nested: void 0
|
|
507
682
|
}
|
|
508
683
|
]
|
|
509
684
|
};
|
|
@@ -1,9 +1,174 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useCallback, useEffect, useMemo } from "react";
|
|
3
|
-
import { Link, useParams, useNavigate } from "react-router-dom";
|
|
4
3
|
import { defineRouteConfig } from "@medusajs/admin-sdk";
|
|
5
4
|
import { Container, Heading, Text, Button, Input, Badge, toast, Textarea } from "@medusajs/ui";
|
|
6
5
|
import { Envelope, ArrowUturnLeft } from "@medusajs/icons";
|
|
6
|
+
import { Link, useParams, useNavigate } from "react-router-dom";
|
|
7
|
+
const useDebounce$1 = (value, delay) => {
|
|
8
|
+
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const handler = setTimeout(() => setDebouncedValue(value), delay);
|
|
11
|
+
return () => clearTimeout(handler);
|
|
12
|
+
}, [value, delay]);
|
|
13
|
+
return debouncedValue;
|
|
14
|
+
};
|
|
15
|
+
const badgeClass = (status) => {
|
|
16
|
+
if (status === "subscribed") {
|
|
17
|
+
return "bg-ui-tag-green-bg text-ui-tag-green-text";
|
|
18
|
+
}
|
|
19
|
+
return "bg-ui-tag-red-bg text-ui-tag-red-text";
|
|
20
|
+
};
|
|
21
|
+
const statusFilters = [
|
|
22
|
+
{ value: "all", label: "All" },
|
|
23
|
+
{ value: "subscribed", label: "Subscribed" },
|
|
24
|
+
{ value: "unsubscribed", label: "Unsubscribed" }
|
|
25
|
+
];
|
|
26
|
+
const ContactEmailSubscriptionsPage = () => {
|
|
27
|
+
const [items, setItems] = useState([]);
|
|
28
|
+
const [statusFilter, setStatusFilter] = useState("all");
|
|
29
|
+
const [query, setQuery] = useState("");
|
|
30
|
+
const debouncedQuery = useDebounce$1(query, 300);
|
|
31
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
32
|
+
const [isFetchingMore, setIsFetchingMore] = useState(false);
|
|
33
|
+
const [error, setError] = useState(null);
|
|
34
|
+
const [offset, setOffset] = useState(0);
|
|
35
|
+
const [count, setCount] = useState(0);
|
|
36
|
+
const limit = 50;
|
|
37
|
+
const loadSubscriptions = useCallback(
|
|
38
|
+
async (nextOffset, replace = false) => {
|
|
39
|
+
var _a;
|
|
40
|
+
try {
|
|
41
|
+
if (replace) {
|
|
42
|
+
setIsLoading(true);
|
|
43
|
+
} else {
|
|
44
|
+
setIsFetchingMore(true);
|
|
45
|
+
}
|
|
46
|
+
setError(null);
|
|
47
|
+
const params = new URLSearchParams();
|
|
48
|
+
params.set("limit", String(limit));
|
|
49
|
+
params.set("offset", String(nextOffset));
|
|
50
|
+
if (statusFilter !== "all") {
|
|
51
|
+
params.set("status", statusFilter);
|
|
52
|
+
}
|
|
53
|
+
if (debouncedQuery.trim()) {
|
|
54
|
+
params.set("q", debouncedQuery.trim());
|
|
55
|
+
}
|
|
56
|
+
const response = await fetch(
|
|
57
|
+
`/admin/contact-email-subscriptions?${params.toString()}`,
|
|
58
|
+
{ credentials: "include" }
|
|
59
|
+
);
|
|
60
|
+
if (!response.ok) {
|
|
61
|
+
const message = await response.text();
|
|
62
|
+
throw new Error(message || "Unable to load subscriptions");
|
|
63
|
+
}
|
|
64
|
+
const payload = await response.json();
|
|
65
|
+
setCount(payload.count ?? 0);
|
|
66
|
+
setOffset(nextOffset + (((_a = payload.subscriptions) == null ? void 0 : _a.length) ?? 0));
|
|
67
|
+
setItems(
|
|
68
|
+
(prev) => replace ? payload.subscriptions ?? [] : [...prev, ...payload.subscriptions ?? []]
|
|
69
|
+
);
|
|
70
|
+
} catch (loadError) {
|
|
71
|
+
const message = loadError instanceof Error ? loadError.message : "Unable to load subscriptions";
|
|
72
|
+
setError(message);
|
|
73
|
+
} finally {
|
|
74
|
+
setIsLoading(false);
|
|
75
|
+
setIsFetchingMore(false);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
[statusFilter, debouncedQuery]
|
|
79
|
+
);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
void loadSubscriptions(0, true);
|
|
82
|
+
}, [statusFilter, debouncedQuery, loadSubscriptions]);
|
|
83
|
+
const hasMore = useMemo(() => offset < count, [offset, count]);
|
|
84
|
+
return /* @__PURE__ */ jsx("div", { className: "w-full p-6", children: /* @__PURE__ */ jsxs(Container, { className: "mx-auto flex w-full max-w-5xl flex-col gap-6 p-6", children: [
|
|
85
|
+
/* @__PURE__ */ jsxs("header", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
|
|
86
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
87
|
+
/* @__PURE__ */ jsx(Heading, { level: "h1", children: "Contact email list" }),
|
|
88
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Track opt-ins and unsubscribes collected from the storefront helper." })
|
|
89
|
+
] }),
|
|
90
|
+
/* @__PURE__ */ jsx(Button, { variant: "primary", onClick: () => loadSubscriptions(0, true), children: "Refresh" })
|
|
91
|
+
] }),
|
|
92
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:flex-row md:items-center md:justify-between", children: [
|
|
93
|
+
/* @__PURE__ */ jsx(
|
|
94
|
+
Input,
|
|
95
|
+
{
|
|
96
|
+
placeholder: "Search email",
|
|
97
|
+
value: query,
|
|
98
|
+
onChange: (event) => setQuery(event.target.value),
|
|
99
|
+
className: "md:max-w-sm"
|
|
100
|
+
}
|
|
101
|
+
),
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
"select",
|
|
104
|
+
{
|
|
105
|
+
value: statusFilter,
|
|
106
|
+
onChange: (event) => setStatusFilter(event.target.value),
|
|
107
|
+
className: "h-9 rounded-md border border-ui-border-base bg-transparent px-3 text-sm text-ui-fg-base outline-none transition focus:ring-2 focus:ring-ui-fg-interactive md:max-w-xs",
|
|
108
|
+
children: statusFilters.map((option) => /* @__PURE__ */ jsx("option", { value: option.value, children: option.label }, option.value))
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
] }),
|
|
112
|
+
error ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-ui-border-strong p-6 text-center", children: [
|
|
113
|
+
/* @__PURE__ */ jsx(Text, { weight: "plus", className: "text-ui-fg-error", children: error }),
|
|
114
|
+
/* @__PURE__ */ jsx("div", { className: "mt-4 flex justify-center", children: /* @__PURE__ */ jsx(
|
|
115
|
+
Button,
|
|
116
|
+
{
|
|
117
|
+
variant: "secondary",
|
|
118
|
+
onClick: () => loadSubscriptions(0, true),
|
|
119
|
+
children: "Try again"
|
|
120
|
+
}
|
|
121
|
+
) })
|
|
122
|
+
] }) : null,
|
|
123
|
+
isLoading ? /* @__PURE__ */ jsx("div", { className: "flex justify-center py-16", children: /* @__PURE__ */ jsx(Text, { children: "Loading email list..." }) }) : items.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "rounded-lg border border-dashed border-ui-border-strong p-10 text-center", children: [
|
|
124
|
+
/* @__PURE__ */ jsx(Heading, { level: "h3", className: "text-xl", children: "No entries yet" }),
|
|
125
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "mt-2 text-ui-fg-subtle", children: "Submissions created through the storefront helper will appear here with their latest consent state." })
|
|
126
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "overflow-hidden rounded-xl border border-ui-border-base", children: /* @__PURE__ */ jsxs("table", { className: "min-w-full divide-y divide-ui-border-base", children: [
|
|
127
|
+
/* @__PURE__ */ jsx("thead", { className: "bg-ui-bg-subtle", children: /* @__PURE__ */ jsxs("tr", { children: [
|
|
128
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Email" }),
|
|
129
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Status" }),
|
|
130
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Source" }),
|
|
131
|
+
/* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-ui-fg-muted", children: "Last updated" })
|
|
132
|
+
] }) }),
|
|
133
|
+
/* @__PURE__ */ jsx("tbody", { className: "divide-y divide-ui-border-subtle", children: items.map((subscription) => /* @__PURE__ */ jsxs("tr", { className: "hover:bg-ui-bg-subtle/60", children: [
|
|
134
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-4 font-medium text-ui-fg-base", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
135
|
+
/* @__PURE__ */ jsx("span", { children: subscription.email }),
|
|
136
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: subscription.id })
|
|
137
|
+
] }) }),
|
|
138
|
+
/* @__PURE__ */ jsxs("td", { className: "px-4 py-4", children: [
|
|
139
|
+
/* @__PURE__ */ jsx(
|
|
140
|
+
Badge,
|
|
141
|
+
{
|
|
142
|
+
size: "2xsmall",
|
|
143
|
+
className: `uppercase ${badgeClass(subscription.status)}`,
|
|
144
|
+
children: subscription.status
|
|
145
|
+
}
|
|
146
|
+
),
|
|
147
|
+
subscription.unsubscribed_at ? /* @__PURE__ */ jsxs(Text, { size: "small", className: "mt-1 block text-ui-fg-subtle", children: [
|
|
148
|
+
"Unsubscribed on",
|
|
149
|
+
" ",
|
|
150
|
+
new Date(subscription.unsubscribed_at).toLocaleString()
|
|
151
|
+
] }) : null
|
|
152
|
+
] }),
|
|
153
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: subscription.source ?? "storefront" }),
|
|
154
|
+
/* @__PURE__ */ jsx("td", { className: "px-4 py-4 text-ui-fg-subtle", children: new Date(subscription.updated_at).toLocaleString() })
|
|
155
|
+
] }, subscription.id)) })
|
|
156
|
+
] }) }),
|
|
157
|
+
hasMore ? /* @__PURE__ */ jsx("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx(
|
|
158
|
+
Button,
|
|
159
|
+
{
|
|
160
|
+
variant: "secondary",
|
|
161
|
+
isLoading: isFetchingMore,
|
|
162
|
+
onClick: () => loadSubscriptions(offset, false),
|
|
163
|
+
children: "Load more"
|
|
164
|
+
}
|
|
165
|
+
) }) : null
|
|
166
|
+
] }) });
|
|
167
|
+
};
|
|
168
|
+
const config$1 = defineRouteConfig({
|
|
169
|
+
label: "Contact email list",
|
|
170
|
+
icon: Envelope
|
|
171
|
+
});
|
|
7
172
|
const useDebounce = (value, delay) => {
|
|
8
173
|
const [debouncedValue, setDebouncedValue] = useState(value);
|
|
9
174
|
useEffect(() => {
|
|
@@ -486,6 +651,10 @@ const i18nTranslations0 = {
|
|
|
486
651
|
const widgetModule = { widgets: [] };
|
|
487
652
|
const routeModule = {
|
|
488
653
|
routes: [
|
|
654
|
+
{
|
|
655
|
+
Component: ContactEmailSubscriptionsPage,
|
|
656
|
+
path: "/contact-email-subscriptions"
|
|
657
|
+
},
|
|
489
658
|
{
|
|
490
659
|
Component: ContactRequestsPage,
|
|
491
660
|
path: "/contact-requests"
|
|
@@ -503,6 +672,12 @@ const menuItemModule = {
|
|
|
503
672
|
icon: config.icon,
|
|
504
673
|
path: "/contact-requests",
|
|
505
674
|
nested: void 0
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
label: config$1.label,
|
|
678
|
+
icon: config$1.icon,
|
|
679
|
+
path: "/contact-email-subscriptions",
|
|
680
|
+
nested: void 0
|
|
506
681
|
}
|
|
507
682
|
]
|
|
508
683
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = void 0;
|
|
4
|
+
const constants_1 = require("../../../constants");
|
|
5
|
+
const validators_1 = require("./validators");
|
|
6
|
+
const GET = async (req, res) => {
|
|
7
|
+
const query = validators_1.AdminListContactEmailSubscriptionsSchema.parse(req.query ?? {});
|
|
8
|
+
const service = req.scope.resolve(constants_1.CONTACT_SUBSCRIPTION_MODULE);
|
|
9
|
+
const selector = {};
|
|
10
|
+
if (query.status) {
|
|
11
|
+
selector.status = query.status;
|
|
12
|
+
}
|
|
13
|
+
if (query.q) {
|
|
14
|
+
selector.email = query.q.trim().toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
const [subscriptions, count] = await service.listWithFilters(selector, {
|
|
17
|
+
take: query.limit,
|
|
18
|
+
skip: query.offset,
|
|
19
|
+
order: { created_at: "DESC" },
|
|
20
|
+
});
|
|
21
|
+
res.status(200).json({
|
|
22
|
+
subscriptions,
|
|
23
|
+
count,
|
|
24
|
+
offset: query.offset,
|
|
25
|
+
limit: query.limit,
|
|
26
|
+
});
|
|
27
|
+
};
|
|
28
|
+
exports.GET = GET;
|
|
29
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2NvbnRhY3QtZW1haWwtc3Vic2NyaXB0aW9ucy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSxrREFBZ0U7QUFFaEUsNkNBQXVFO0FBRWhFLE1BQU0sR0FBRyxHQUFHLEtBQUssRUFBRSxHQUFrQixFQUFFLEdBQW1CLEVBQUUsRUFBRTtJQUNuRSxNQUFNLEtBQUssR0FBRyxxREFBd0MsQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLEtBQUssSUFBSSxFQUFFLENBQUMsQ0FBQTtJQUM3RSxNQUFNLE9BQU8sR0FBRyxHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FDL0IsdUNBQTJCLENBQzVCLENBQUE7SUFFRCxNQUFNLFFBQVEsR0FBNEIsRUFBRSxDQUFBO0lBRTVDLElBQUksS0FBSyxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ2pCLFFBQVEsQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQTtJQUNoQyxDQUFDO0lBQ0QsSUFBSSxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDWixRQUFRLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsV0FBVyxFQUFFLENBQUE7SUFDL0MsQ0FBQztJQUVELE1BQU0sQ0FBQyxhQUFhLEVBQUUsS0FBSyxDQUFDLEdBQUcsTUFBTSxPQUFPLENBQUMsZUFBZSxDQUFDLFFBQVEsRUFBRTtRQUNyRSxJQUFJLEVBQUUsS0FBSyxDQUFDLEtBQUs7UUFDakIsSUFBSSxFQUFFLEtBQUssQ0FBQyxNQUFNO1FBQ2xCLEtBQUssRUFBRSxFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUU7S0FDOUIsQ0FBQyxDQUFBO0lBRUYsR0FBRyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUM7UUFDbkIsYUFBYTtRQUNiLEtBQUs7UUFDTCxNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07UUFDcEIsS0FBSyxFQUFFLEtBQUssQ0FBQyxLQUFLO0tBQ25CLENBQUMsQ0FBQTtBQUNKLENBQUMsQ0FBQTtBQTNCWSxRQUFBLEdBQUcsT0EyQmYifQ==
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AdminListContactEmailSubscriptionsSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const validators_1 = require("../../store/contact-email-subscriptions/validators");
|
|
6
|
+
exports.AdminListContactEmailSubscriptionsSchema = zod_1.z.object({
|
|
7
|
+
status: validators_1.ContactSubscriptionStatusEnum.optional(),
|
|
8
|
+
q: zod_1.z.string().optional(),
|
|
9
|
+
limit: zod_1.z.coerce.number().min(1).max(100).default(50),
|
|
10
|
+
offset: zod_1.z.coerce.number().min(0).default(0),
|
|
11
|
+
});
|
|
12
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvYWRtaW4vY29udGFjdC1lbWFpbC1zdWJzY3JpcHRpb25zL3ZhbGlkYXRvcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkJBQXVCO0FBQ3ZCLG1GQUFrRztBQUVyRixRQUFBLHdDQUF3QyxHQUFHLE9BQUMsQ0FBQyxNQUFNLENBQUM7SUFDL0QsTUFBTSxFQUFFLDBDQUE2QixDQUFDLFFBQVEsRUFBRTtJQUNoRCxDQUFDLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUN4QixLQUFLLEVBQUUsT0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7SUFDcEQsTUFBTSxFQUFFLE9BQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUM7Q0FDNUMsQ0FBQyxDQUFBIn0=
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.POST = exports.AUTHENTICATE = void 0;
|
|
4
|
+
const constants_1 = require("../../../constants");
|
|
5
|
+
const validators_1 = require("./validators");
|
|
6
|
+
exports.AUTHENTICATE = false;
|
|
7
|
+
const POST = async (req, res) => {
|
|
8
|
+
const body = validators_1.StoreUpsertContactSubscriptionSchema.parse(req.body ?? {});
|
|
9
|
+
const service = req.scope.resolve(constants_1.CONTACT_SUBSCRIPTION_MODULE);
|
|
10
|
+
const subscription = await service.upsertSubscription({
|
|
11
|
+
email: body.email,
|
|
12
|
+
status: body.status,
|
|
13
|
+
metadata: body.metadata,
|
|
14
|
+
source: body.source ?? "storefront",
|
|
15
|
+
});
|
|
16
|
+
res.status(200).json({ subscription });
|
|
17
|
+
};
|
|
18
|
+
exports.POST = POST;
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL3N0b3JlL2NvbnRhY3QtZW1haWwtc3Vic2NyaXB0aW9ucy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSxrREFBZ0U7QUFFaEUsNkNBQW1FO0FBRXRELFFBQUEsWUFBWSxHQUFHLEtBQUssQ0FBQTtBQUUxQixNQUFNLElBQUksR0FBRyxLQUFLLEVBQUUsR0FBa0IsRUFBRSxHQUFtQixFQUFFLEVBQUU7SUFDcEUsTUFBTSxJQUFJLEdBQUcsaURBQW9DLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxJQUFJLElBQUksRUFBRSxDQUFDLENBQUE7SUFFdkUsTUFBTSxPQUFPLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQy9CLHVDQUEyQixDQUM1QixDQUFBO0lBRUQsTUFBTSxZQUFZLEdBQUcsTUFBTSxPQUFPLENBQUMsa0JBQWtCLENBQUM7UUFDcEQsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLO1FBQ2pCLE1BQU0sRUFBRSxJQUFJLENBQUMsTUFBTTtRQUNuQixRQUFRLEVBQUUsSUFBSSxDQUFDLFFBQVE7UUFDdkIsTUFBTSxFQUFFLElBQUksQ0FBQyxNQUFNLElBQUksWUFBWTtLQUNwQyxDQUFDLENBQUE7SUFFRixHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLFlBQVksRUFBRSxDQUFDLENBQUE7QUFDeEMsQ0FBQyxDQUFBO0FBZlksUUFBQSxJQUFJLFFBZWhCIn0=
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StoreUpsertContactSubscriptionSchema = exports.ContactSubscriptionStatusEnum = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
exports.ContactSubscriptionStatusEnum = zod_1.z.enum([
|
|
6
|
+
"subscribed",
|
|
7
|
+
"unsubscribed",
|
|
8
|
+
]);
|
|
9
|
+
exports.StoreUpsertContactSubscriptionSchema = zod_1.z.object({
|
|
10
|
+
email: zod_1.z.string().email(),
|
|
11
|
+
status: exports.ContactSubscriptionStatusEnum.optional(),
|
|
12
|
+
metadata: zod_1.z.record(zod_1.z.any()).optional(),
|
|
13
|
+
source: zod_1.z.string().optional(),
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdG9ycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9hcGkvc3RvcmUvY29udGFjdC1lbWFpbC1zdWJzY3JpcHRpb25zL3ZhbGlkYXRvcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQUEsNkJBQXVCO0FBRVYsUUFBQSw2QkFBNkIsR0FBRyxPQUFDLENBQUMsSUFBSSxDQUFDO0lBQ2xELFlBQVk7SUFDWixjQUFjO0NBQ2YsQ0FBQyxDQUFBO0FBRVcsUUFBQSxvQ0FBb0MsR0FBRyxPQUFDLENBQUMsTUFBTSxDQUFDO0lBQzNELEtBQUssRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsS0FBSyxFQUFFO0lBQ3pCLE1BQU0sRUFBRSxxQ0FBNkIsQ0FBQyxRQUFRLEVBQUU7SUFDaEQsUUFBUSxFQUFFLE9BQUMsQ0FBQyxNQUFNLENBQUMsT0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDLENBQUMsUUFBUSxFQUFFO0lBQ3RDLE1BQU0sRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0NBQzlCLENBQUMsQ0FBQSJ9
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CONTACT_REQUEST_MODULE = void 0;
|
|
3
|
+
exports.CONTACT_SUBSCRIPTION_MODULE = exports.CONTACT_REQUEST_MODULE = void 0;
|
|
4
4
|
exports.CONTACT_REQUEST_MODULE = "contact_requests";
|
|
5
|
-
|
|
5
|
+
exports.CONTACT_SUBSCRIPTION_MODULE = "contact_email_subscriptions";
|
|
6
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc3RhbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2NvbnN0YW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBYSxRQUFBLHNCQUFzQixHQUFHLGtCQUFrQixDQUFBO0FBQzNDLFFBQUEsMkJBQTJCLEdBQUcsNkJBQTZCLENBQUEifQ==
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const vitest_1 = require("vitest");
|
|
4
|
+
const contact_subscription_1 = require("../contact-subscription");
|
|
5
|
+
const buildPayload = () => ({
|
|
6
|
+
email: "sub@example.com",
|
|
7
|
+
status: "subscribed",
|
|
8
|
+
metadata: { channel: "footer" },
|
|
9
|
+
});
|
|
10
|
+
(0, vitest_1.describe)("upsertContactSubscription helper", () => {
|
|
11
|
+
const fetchCases = [
|
|
12
|
+
{
|
|
13
|
+
name: "uses provided fetch implementation when no client exists",
|
|
14
|
+
setup: () => {
|
|
15
|
+
const mockFetch = vitest_1.vi.fn().mockResolvedValue({
|
|
16
|
+
ok: true,
|
|
17
|
+
json: () => Promise.resolve({
|
|
18
|
+
subscription: { id: "sub_1" },
|
|
19
|
+
}),
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
fetchImpl: mockFetch,
|
|
23
|
+
expectedUrl: "https://demo.store/store/contact-email-subscriptions",
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "throws when response is not ok",
|
|
29
|
+
setup: () => {
|
|
30
|
+
const mockFetch = vitest_1.vi.fn().mockResolvedValue({
|
|
31
|
+
ok: false,
|
|
32
|
+
status: 400,
|
|
33
|
+
text: () => Promise.resolve("invalid"),
|
|
34
|
+
});
|
|
35
|
+
return {
|
|
36
|
+
fetchImpl: mockFetch,
|
|
37
|
+
expectedError: /invalid/i,
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
fetchCases.forEach(({ name, setup }) => {
|
|
43
|
+
(0, vitest_1.it)(name, async () => {
|
|
44
|
+
const config = setup();
|
|
45
|
+
const { fetchImpl } = config;
|
|
46
|
+
if ("expectedError" in config) {
|
|
47
|
+
await (0, vitest_1.expect)((0, contact_subscription_1.upsertContactSubscription)(buildPayload(), {
|
|
48
|
+
fetchImpl,
|
|
49
|
+
baseUrl: "https://demo.store",
|
|
50
|
+
})).rejects.toMatchObject({
|
|
51
|
+
message: vitest_1.expect.stringMatching(config.expectedError),
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const result = await (0, contact_subscription_1.upsertContactSubscription)(buildPayload(), {
|
|
56
|
+
fetchImpl,
|
|
57
|
+
baseUrl: "https://demo.store",
|
|
58
|
+
});
|
|
59
|
+
(0, vitest_1.expect)(result).toEqual({ subscription: { id: "sub_1" } });
|
|
60
|
+
(0, vitest_1.expect)(fetchImpl).toHaveBeenCalledWith(config.expectedUrl, vitest_1.expect.anything());
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
(0, vitest_1.it)("uses medusa client when provided", async () => {
|
|
64
|
+
const mockClient = {
|
|
65
|
+
request: vitest_1.vi.fn().mockResolvedValue({
|
|
66
|
+
subscription: { id: "sub_2" },
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
const result = await (0, contact_subscription_1.upsertContactSubscription)(buildPayload(), {
|
|
70
|
+
client: mockClient,
|
|
71
|
+
});
|
|
72
|
+
(0, vitest_1.expect)(result.subscription.id).toBe("sub_2");
|
|
73
|
+
(0, vitest_1.expect)(mockClient.request).toHaveBeenCalledWith("/store/contact-email-subscriptions", vitest_1.expect.objectContaining({
|
|
74
|
+
method: "POST",
|
|
75
|
+
}));
|
|
76
|
+
});
|
|
77
|
+
(0, vitest_1.it)("attaches publishable API key to headers", async () => {
|
|
78
|
+
const mockFetch = vitest_1.vi.fn().mockResolvedValue({
|
|
79
|
+
ok: true,
|
|
80
|
+
json: () => Promise.resolve({
|
|
81
|
+
subscription: { id: "sub_3" },
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
await (0, contact_subscription_1.upsertContactSubscription)(buildPayload(), {
|
|
85
|
+
fetchImpl: mockFetch,
|
|
86
|
+
baseUrl: "https://demo.store",
|
|
87
|
+
publishableApiKey: "pk_subscribe",
|
|
88
|
+
});
|
|
89
|
+
const [, init] = mockFetch.mock.calls[0];
|
|
90
|
+
(0, vitest_1.expect)(init?.headers).toMatchObject({
|
|
91
|
+
"x-publishable-api-key": "pk_subscribe",
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
(0, vitest_1.it)("allows helper factory to preconfigure options", async () => {
|
|
95
|
+
const mockFetch = vitest_1.vi.fn().mockResolvedValue({
|
|
96
|
+
ok: true,
|
|
97
|
+
json: () => Promise.resolve({
|
|
98
|
+
subscription: { id: "sub_4" },
|
|
99
|
+
}),
|
|
100
|
+
});
|
|
101
|
+
const updateSubscription = (0, contact_subscription_1.createUpsertContactSubscription)({
|
|
102
|
+
fetchImpl: mockFetch,
|
|
103
|
+
baseUrl: "https://demo.store",
|
|
104
|
+
});
|
|
105
|
+
const result = await updateSubscription(buildPayload());
|
|
106
|
+
(0, vitest_1.expect)(result.subscription.id).toBe("sub_4");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29udGFjdC1zdWJzY3JpcHRpb24udGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9oZWxwZXJzL19fdGVzdHNfXy9jb250YWN0LXN1YnNjcmlwdGlvbi50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsbUNBQWlEO0FBQ2pELGtFQUdnQztBQUVoQyxNQUFNLFlBQVksR0FBRyxHQUFHLEVBQUUsQ0FBQyxDQUFDO0lBQzFCLEtBQUssRUFBRSxpQkFBaUI7SUFDeEIsTUFBTSxFQUFFLFlBQXFCO0lBQzdCLFFBQVEsRUFBRSxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUU7Q0FDaEMsQ0FBQyxDQUFBO0FBRUYsSUFBQSxpQkFBUSxFQUFDLGtDQUFrQyxFQUFFLEdBQUcsRUFBRTtJQUNoRCxNQUFNLFVBQVUsR0FBRztRQUNqQjtZQUNFLElBQUksRUFBRSwwREFBMEQ7WUFDaEUsS0FBSyxFQUFFLEdBQUcsRUFBRTtnQkFDVixNQUFNLFNBQVMsR0FBRyxXQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsaUJBQWlCLENBQUM7b0JBQzFDLEVBQUUsRUFBRSxJQUFJO29CQUNSLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FDVCxPQUFPLENBQUMsT0FBTyxDQUFDO3dCQUNkLFlBQVksRUFBRSxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUU7cUJBQzlCLENBQUM7aUJBQ0wsQ0FBQyxDQUFBO2dCQUNGLE9BQU87b0JBQ0wsU0FBUyxFQUFFLFNBQVM7b0JBQ3BCLFdBQVcsRUFBRSxzREFBc0Q7aUJBQ3BFLENBQUE7WUFDSCxDQUFDO1NBQ0Y7UUFDRDtZQUNFLElBQUksRUFBRSxnQ0FBZ0M7WUFDdEMsS0FBSyxFQUFFLEdBQUcsRUFBRTtnQkFDVixNQUFNLFNBQVMsR0FBRyxXQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsaUJBQWlCLENBQUM7b0JBQzFDLEVBQUUsRUFBRSxLQUFLO29CQUNULE1BQU0sRUFBRSxHQUFHO29CQUNYLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQztpQkFDdkMsQ0FBQyxDQUFBO2dCQUNGLE9BQU87b0JBQ0wsU0FBUyxFQUFFLFNBQVM7b0JBQ3BCLGFBQWEsRUFBRSxVQUFVO2lCQUMxQixDQUFBO1lBQ0gsQ0FBQztTQUNGO0tBQ0YsQ0FBQTtJQUVELFVBQVUsQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFO1FBQ3JDLElBQUEsV0FBRSxFQUFDLElBQUksRUFBRSxLQUFLLElBQUksRUFBRTtZQUNsQixNQUFNLE1BQU0sR0FBRyxLQUFLLEVBQUUsQ0FBQTtZQUN0QixNQUFNLEVBQUUsU0FBUyxFQUFFLEdBQUcsTUFBTSxDQUFBO1lBRTVCLElBQUksZUFBZSxJQUFJLE1BQU0sRUFBRSxDQUFDO2dCQUM5QixNQUFNLElBQUEsZUFBTSxFQUNWLElBQUEsZ0RBQXlCLEVBQUMsWUFBWSxFQUFFLEVBQUU7b0JBQ3hDLFNBQVM7b0JBQ1QsT0FBTyxFQUFFLG9CQUFvQjtpQkFDOUIsQ0FBQyxDQUNILENBQUMsT0FBTyxDQUFDLGFBQWEsQ0FBQztvQkFDdEIsT0FBTyxFQUFFLGVBQU0sQ0FBQyxjQUFjLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQztpQkFDckQsQ0FBQyxDQUFBO2dCQUNGLE9BQU07WUFDUixDQUFDO1lBRUQsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFBLGdEQUF5QixFQUFDLFlBQVksRUFBRSxFQUFFO2dCQUM3RCxTQUFTO2dCQUNULE9BQU8sRUFBRSxvQkFBb0I7YUFDOUIsQ0FBQyxDQUFBO1lBRUYsSUFBQSxlQUFNLEVBQUMsTUFBTSxDQUFDLENBQUMsT0FBTyxDQUFDLEVBQUUsWUFBWSxFQUFFLEVBQUUsRUFBRSxFQUFFLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQTtZQUN6RCxJQUFBLGVBQU0sRUFBQyxTQUFTLENBQUMsQ0FBQyxvQkFBb0IsQ0FDcEMsTUFBTSxDQUFDLFdBQVcsRUFDbEIsZUFBTSxDQUFDLFFBQVEsRUFBRSxDQUNsQixDQUFBO1FBQ0gsQ0FBQyxDQUFDLENBQUE7SUFDSixDQUFDLENBQUMsQ0FBQTtJQUVGLElBQUEsV0FBRSxFQUFDLGtDQUFrQyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ2hELE1BQU0sVUFBVSxHQUFHO1lBQ2pCLE9BQU8sRUFBRSxXQUFFLENBQUMsRUFBRSxFQUFFLENBQUMsaUJBQWlCLENBQUM7Z0JBQ2pDLFlBQVksRUFBRSxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUU7YUFDOUIsQ0FBQztTQUNILENBQUE7UUFFRCxNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUEsZ0RBQXlCLEVBQUMsWUFBWSxFQUFFLEVBQUU7WUFDN0QsTUFBTSxFQUFFLFVBQVU7U0FDbkIsQ0FBQyxDQUFBO1FBRUYsSUFBQSxlQUFNLEVBQUMsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUMsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLENBQUE7UUFDNUMsSUFBQSxlQUFNLEVBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQyxDQUFDLG9CQUFvQixDQUM3QyxvQ0FBb0MsRUFDcEMsZUFBTSxDQUFDLGdCQUFnQixDQUFDO1lBQ3RCLE1BQU0sRUFBRSxNQUFNO1NBQ2YsQ0FBQyxDQUNILENBQUE7SUFDSCxDQUFDLENBQUMsQ0FBQTtJQUVGLElBQUEsV0FBRSxFQUFDLHlDQUF5QyxFQUFFLEtBQUssSUFBSSxFQUFFO1FBQ3ZELE1BQU0sU0FBUyxHQUFHLFdBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxpQkFBaUIsQ0FBQztZQUMxQyxFQUFFLEVBQUUsSUFBSTtZQUNSLElBQUksRUFBRSxHQUFHLEVBQUUsQ0FDVCxPQUFPLENBQUMsT0FBTyxDQUFDO2dCQUNkLFlBQVksRUFBRSxFQUFFLEVBQUUsRUFBRSxPQUFPLEVBQUU7YUFDOUIsQ0FBQztTQUNMLENBQUMsQ0FBQTtRQUVGLE1BQU0sSUFBQSxnREFBeUIsRUFBQyxZQUFZLEVBQUUsRUFBRTtZQUM5QyxTQUFTLEVBQUUsU0FBUztZQUNwQixPQUFPLEVBQUUsb0JBQW9CO1lBQzdCLGlCQUFpQixFQUFFLGNBQWM7U0FDbEMsQ0FBQyxDQUFBO1FBRUYsTUFBTSxDQUFDLEVBQUUsSUFBSSxDQUFDLEdBQUcsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUE7UUFDeEMsSUFBQSxlQUFNLEVBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDLGFBQWEsQ0FBQztZQUNsQyx1QkFBdUIsRUFBRSxjQUFjO1NBQ3hDLENBQUMsQ0FBQTtJQUNKLENBQUMsQ0FBQyxDQUFBO0lBRUYsSUFBQSxXQUFFLEVBQUMsK0NBQStDLEVBQUUsS0FBSyxJQUFJLEVBQUU7UUFDN0QsTUFBTSxTQUFTLEdBQUcsV0FBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLGlCQUFpQixDQUFDO1lBQzFDLEVBQUUsRUFBRSxJQUFJO1lBQ1IsSUFBSSxFQUFFLEdBQUcsRUFBRSxDQUNULE9BQU8sQ0FBQyxPQUFPLENBQUM7Z0JBQ2QsWUFBWSxFQUFFLEVBQUUsRUFBRSxFQUFFLE9BQU8sRUFBRTthQUM5QixDQUFDO1NBQ0wsQ0FBQyxDQUFBO1FBRUYsTUFBTSxrQkFBa0IsR0FBRyxJQUFBLHNEQUErQixFQUFDO1lBQ3pELFNBQVMsRUFBRSxTQUFTO1lBQ3BCLE9BQU8sRUFBRSxvQkFBb0I7U0FDOUIsQ0FBQyxDQUFBO1FBRUYsTUFBTSxNQUFNLEdBQUcsTUFBTSxrQkFBa0IsQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUFBO1FBQ3ZELElBQUEsZUFBTSxFQUFDLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFBO0lBQzlDLENBQUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUFDLENBQUEifQ==
|