@weareseeed/medusa-athos-plugin 0.0.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.
- package/.medusa/server/src/admin/index.js +259 -0
- package/.medusa/server/src/admin/index.mjs +258 -0
- package/.medusa/server/src/api/admin/athos/config/route.d.ts +4 -0
- package/.medusa/server/src/api/admin/athos/config/route.js +21 -0
- package/.medusa/server/src/api/admin/athos/middlewares.d.ts +11 -0
- package/.medusa/server/src/api/admin/athos/middlewares.js +20 -0
- package/.medusa/server/src/api/admin/plugin/route.d.ts +2 -0
- package/.medusa/server/src/api/admin/plugin/route.js +7 -0
- package/.medusa/server/src/api/athos/feed/route.d.ts +2 -0
- package/.medusa/server/src/api/athos/feed/route.js +249 -0
- package/.medusa/server/src/api/athos/middlewares.d.ts +2 -0
- package/.medusa/server/src/api/athos/middlewares.js +26 -0
- package/.medusa/server/src/api/middlewares.d.ts +2 -0
- package/.medusa/server/src/api/middlewares.js +9 -0
- package/.medusa/server/src/api/store/plugin/route.d.ts +2 -0
- package/.medusa/server/src/api/store/plugin/route.js +7 -0
- package/.medusa/server/src/modules/athosConfig/index.d.ts +21 -0
- package/.medusa/server/src/modules/athosConfig/index.js +13 -0
- package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000000.d.ts +5 -0
- package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000000.js +26 -0
- package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000001.d.ts +5 -0
- package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000001.js +14 -0
- package/.medusa/server/src/modules/athosConfig/models/athos-config.d.ts +9 -0
- package/.medusa/server/src/modules/athosConfig/models/athos-config.js +13 -0
- package/.medusa/server/src/modules/athosConfig/service.d.ts +17 -0
- package/.medusa/server/src/modules/athosConfig/service.js +32 -0
- package/.medusa/server/src/modules/athosConfig/types.d.ts +15 -0
- package/.medusa/server/src/modules/athosConfig/types.js +3 -0
- package/.medusa/server/src/workflows/steps/upsert-athos-config-step.d.ts +9 -0
- package/.medusa/server/src/workflows/steps/upsert-athos-config-step.js +31 -0
- package/.medusa/server/src/workflows/upsert-athos-config.d.ts +9 -0
- package/.medusa/server/src/workflows/upsert-athos-config.js +10 -0
- package/README.md +198 -0
- package/package.json +73 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
3
|
+
const adminSdk = require("@medusajs/admin-sdk");
|
|
4
|
+
const icons = require("@medusajs/icons");
|
|
5
|
+
const ui = require("@medusajs/ui");
|
|
6
|
+
const reactQuery = require("@tanstack/react-query");
|
|
7
|
+
const react = require("react");
|
|
8
|
+
const Medusa = require("@medusajs/js-sdk");
|
|
9
|
+
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
10
|
+
const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
|
|
11
|
+
const sdk = new Medusa__default.default({
|
|
12
|
+
baseUrl: "/",
|
|
13
|
+
debug: false,
|
|
14
|
+
auth: {
|
|
15
|
+
type: "session"
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
const AthosPage = () => {
|
|
19
|
+
const queryClient = reactQuery.useQueryClient();
|
|
20
|
+
const backendUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
21
|
+
const { data: configData, isLoading: configLoading } = reactQuery.useQuery({
|
|
22
|
+
queryKey: ["athos-config"],
|
|
23
|
+
queryFn: () => sdk.client.fetch("/admin/athos/config")
|
|
24
|
+
});
|
|
25
|
+
const { data: regionsData } = reactQuery.useQuery({
|
|
26
|
+
queryKey: ["athos-regions"],
|
|
27
|
+
queryFn: () => sdk.admin.region.list({ limit: 100 })
|
|
28
|
+
});
|
|
29
|
+
const { data: salesChannelsData } = reactQuery.useQuery({
|
|
30
|
+
queryKey: ["athos-sales-channels"],
|
|
31
|
+
queryFn: () => sdk.admin.salesChannel.list({ limit: 100 })
|
|
32
|
+
});
|
|
33
|
+
const config2 = configData == null ? void 0 : configData.config;
|
|
34
|
+
const regions = (regionsData == null ? void 0 : regionsData.regions) ?? [];
|
|
35
|
+
const salesChannels = (salesChannelsData == null ? void 0 : salesChannelsData.sales_channels) ?? [];
|
|
36
|
+
const [initialized, setInitialized] = react.useState(false);
|
|
37
|
+
const [form, setForm] = react.useState({
|
|
38
|
+
storefront_url: "",
|
|
39
|
+
feed_token: "",
|
|
40
|
+
region_id: null,
|
|
41
|
+
sales_channel_ids: [],
|
|
42
|
+
swatch_option_titles: []
|
|
43
|
+
});
|
|
44
|
+
react.useEffect(() => {
|
|
45
|
+
if (config2 && !initialized) {
|
|
46
|
+
setForm({
|
|
47
|
+
storefront_url: config2.storefront_url ?? "",
|
|
48
|
+
feed_token: config2.feed_token ?? "",
|
|
49
|
+
region_id: config2.region_id ?? null,
|
|
50
|
+
sales_channel_ids: config2.sales_channel_ids ?? [],
|
|
51
|
+
swatch_option_titles: config2.swatch_option_titles ?? []
|
|
52
|
+
});
|
|
53
|
+
setInitialized(true);
|
|
54
|
+
}
|
|
55
|
+
}, [config2, initialized]);
|
|
56
|
+
const saveMutation = reactQuery.useMutation({
|
|
57
|
+
mutationFn: (data) => sdk.client.fetch("/admin/athos/config", {
|
|
58
|
+
method: "POST",
|
|
59
|
+
body: data
|
|
60
|
+
}),
|
|
61
|
+
onSuccess: () => {
|
|
62
|
+
queryClient.invalidateQueries({ queryKey: ["athos-config"] });
|
|
63
|
+
ui.toast.success("Settings saved");
|
|
64
|
+
},
|
|
65
|
+
onError: (error) => {
|
|
66
|
+
ui.toast.error((error == null ? void 0 : error.message) ?? "Failed to save settings");
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const handleGenerateToken = () => {
|
|
70
|
+
setForm((f) => ({ ...f, feed_token: crypto.randomUUID() }));
|
|
71
|
+
};
|
|
72
|
+
const handleSalesChannelToggle = (id) => {
|
|
73
|
+
setForm((f) => ({
|
|
74
|
+
...f,
|
|
75
|
+
sales_channel_ids: f.sales_channel_ids.includes(id) ? f.sales_channel_ids.filter((scId) => scId !== id) : [...f.sales_channel_ids, id]
|
|
76
|
+
}));
|
|
77
|
+
};
|
|
78
|
+
const feedUrl = form.feed_token ? `${backendUrl}/athos/feed?token=${form.feed_token}` : "";
|
|
79
|
+
const handleCopyFeedUrl = () => {
|
|
80
|
+
if (!feedUrl) return;
|
|
81
|
+
navigator.clipboard.writeText(feedUrl).then(() => ui.toast.success("Feed URL copied to clipboard")).catch(() => ui.toast.error("Failed to copy URL"));
|
|
82
|
+
};
|
|
83
|
+
if (configLoading) {
|
|
84
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center p-12", children: "Spinner ..." });
|
|
85
|
+
}
|
|
86
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4 p-4", children: [
|
|
87
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
88
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Athos Feed Settings" }) }),
|
|
89
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4 px-6 pb-6", children: [
|
|
90
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
91
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Storefront URL" }),
|
|
92
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
93
|
+
ui.Input,
|
|
94
|
+
{
|
|
95
|
+
placeholder: "https://mystore.com",
|
|
96
|
+
value: form.storefront_url,
|
|
97
|
+
onChange: (e) => setForm((f) => ({ ...f, storefront_url: e.target.value }))
|
|
98
|
+
}
|
|
99
|
+
),
|
|
100
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Base URL used to build product links in the feed" })
|
|
101
|
+
] }),
|
|
102
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
103
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Feed Token" }),
|
|
104
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-x-2", children: [
|
|
105
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
106
|
+
ui.Input,
|
|
107
|
+
{
|
|
108
|
+
placeholder: "Secret token",
|
|
109
|
+
value: form.feed_token,
|
|
110
|
+
onChange: (e) => setForm((f) => ({ ...f, feed_token: e.target.value }))
|
|
111
|
+
}
|
|
112
|
+
),
|
|
113
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
114
|
+
ui.Button,
|
|
115
|
+
{
|
|
116
|
+
size: "small",
|
|
117
|
+
variant: "secondary",
|
|
118
|
+
onClick: handleGenerateToken,
|
|
119
|
+
children: "Generate"
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
] }),
|
|
123
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Secret used to authenticate Athos feed requests" })
|
|
124
|
+
] }),
|
|
125
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
126
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Region" }),
|
|
127
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
128
|
+
ui.Select,
|
|
129
|
+
{
|
|
130
|
+
value: form.region_id ?? "",
|
|
131
|
+
onValueChange: (val) => setForm((f) => ({ ...f, region_id: val || null })),
|
|
132
|
+
children: [
|
|
133
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "All regions" }) }),
|
|
134
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
|
|
135
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: "-", children: "All regions" }),
|
|
136
|
+
regions.map((region) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Item, { value: region.id, children: [
|
|
137
|
+
region.name,
|
|
138
|
+
" (",
|
|
139
|
+
region.currency_code.toUpperCase(),
|
|
140
|
+
")"
|
|
141
|
+
] }, region.id))
|
|
142
|
+
] })
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
),
|
|
146
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Filter prices to a specific region's currency. Leave empty to include all currencies." })
|
|
147
|
+
] }),
|
|
148
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
149
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Sales Channels" }),
|
|
150
|
+
salesChannels.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "No sales channels found" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
151
|
+
salesChannels.map((sc) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
152
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
153
|
+
ui.Checkbox,
|
|
154
|
+
{
|
|
155
|
+
id: `sc-${sc.id}`,
|
|
156
|
+
checked: form.sales_channel_ids.includes(sc.id),
|
|
157
|
+
onCheckedChange: () => handleSalesChannelToggle(sc.id)
|
|
158
|
+
}
|
|
159
|
+
),
|
|
160
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { htmlFor: `sc-${sc.id}`, className: "cursor-pointer", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: sc.name }) })
|
|
161
|
+
] }, sc.id)),
|
|
162
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Filter products by sales channel. Leave all unchecked to include all channels." })
|
|
163
|
+
] })
|
|
164
|
+
] }),
|
|
165
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
166
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Swatch Option Titles" }),
|
|
167
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
168
|
+
ui.Input,
|
|
169
|
+
{
|
|
170
|
+
placeholder: "Color, Colour, Material",
|
|
171
|
+
value: form.swatch_option_titles.join(", "),
|
|
172
|
+
onChange: (e) => setForm((f) => ({
|
|
173
|
+
...f,
|
|
174
|
+
swatch_option_titles: e.target.value.split(",").map((s) => s.trim()).filter(Boolean)
|
|
175
|
+
}))
|
|
176
|
+
}
|
|
177
|
+
),
|
|
178
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Comma-separated option names to render as visual swatches in Athos (e.g. Color, Material). Defaults to Color and Colour if empty." })
|
|
179
|
+
] }),
|
|
180
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
181
|
+
ui.Button,
|
|
182
|
+
{
|
|
183
|
+
size: "small",
|
|
184
|
+
onClick: () => saveMutation.mutate(form),
|
|
185
|
+
isLoading: saveMutation.isPending,
|
|
186
|
+
disabled: saveMutation.isPending,
|
|
187
|
+
children: "Save"
|
|
188
|
+
}
|
|
189
|
+
) })
|
|
190
|
+
] })
|
|
191
|
+
] }),
|
|
192
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { children: [
|
|
193
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Feed URL" }) }),
|
|
194
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-y-3 px-6 pb-6", children: feedUrl ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
195
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "bg-ui-bg-subtle rounded-md px-3 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
196
|
+
ui.Text,
|
|
197
|
+
{
|
|
198
|
+
size: "small",
|
|
199
|
+
className: "break-all font-mono text-ui-fg-subtle",
|
|
200
|
+
children: feedUrl
|
|
201
|
+
}
|
|
202
|
+
) }),
|
|
203
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
204
|
+
ui.Button,
|
|
205
|
+
{
|
|
206
|
+
size: "small",
|
|
207
|
+
variant: "secondary",
|
|
208
|
+
onClick: handleCopyFeedUrl,
|
|
209
|
+
children: "Copy URL"
|
|
210
|
+
}
|
|
211
|
+
) })
|
|
212
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Save a feed token above to generate the feed URL." }) })
|
|
213
|
+
] })
|
|
214
|
+
] });
|
|
215
|
+
};
|
|
216
|
+
const handle = {
|
|
217
|
+
breadcrumb: () => "Athos"
|
|
218
|
+
};
|
|
219
|
+
const config = adminSdk.defineRouteConfig({
|
|
220
|
+
label: "Athos Commerce",
|
|
221
|
+
icon: icons.FunnelPlus
|
|
222
|
+
});
|
|
223
|
+
const i18nTranslations0 = {};
|
|
224
|
+
const widgetModule = { widgets: [] };
|
|
225
|
+
const routeModule = {
|
|
226
|
+
routes: [
|
|
227
|
+
{
|
|
228
|
+
Component: AthosPage,
|
|
229
|
+
path: "/athos",
|
|
230
|
+
handle
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
};
|
|
234
|
+
const menuItemModule = {
|
|
235
|
+
menuItems: [
|
|
236
|
+
{
|
|
237
|
+
label: config.label,
|
|
238
|
+
icon: config.icon,
|
|
239
|
+
path: "/athos",
|
|
240
|
+
nested: void 0,
|
|
241
|
+
rank: void 0,
|
|
242
|
+
translationNs: void 0
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
};
|
|
246
|
+
const formModule = { customFields: {} };
|
|
247
|
+
const displayModule = {
|
|
248
|
+
displays: {}
|
|
249
|
+
};
|
|
250
|
+
const i18nModule = { resources: i18nTranslations0 };
|
|
251
|
+
const plugin = {
|
|
252
|
+
widgetModule,
|
|
253
|
+
routeModule,
|
|
254
|
+
menuItemModule,
|
|
255
|
+
formModule,
|
|
256
|
+
displayModule,
|
|
257
|
+
i18nModule
|
|
258
|
+
};
|
|
259
|
+
module.exports = plugin;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { defineRouteConfig } from "@medusajs/admin-sdk";
|
|
3
|
+
import { FunnelPlus } from "@medusajs/icons";
|
|
4
|
+
import { toast, Container, Heading, Label, Input, Text, Button, Select, Checkbox } from "@medusajs/ui";
|
|
5
|
+
import { useQueryClient, useQuery, useMutation } from "@tanstack/react-query";
|
|
6
|
+
import { useState, useEffect } from "react";
|
|
7
|
+
import Medusa from "@medusajs/js-sdk";
|
|
8
|
+
const sdk = new Medusa({
|
|
9
|
+
baseUrl: "/",
|
|
10
|
+
debug: false,
|
|
11
|
+
auth: {
|
|
12
|
+
type: "session"
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
const AthosPage = () => {
|
|
16
|
+
const queryClient = useQueryClient();
|
|
17
|
+
const backendUrl = typeof window !== "undefined" ? window.location.origin : "";
|
|
18
|
+
const { data: configData, isLoading: configLoading } = useQuery({
|
|
19
|
+
queryKey: ["athos-config"],
|
|
20
|
+
queryFn: () => sdk.client.fetch("/admin/athos/config")
|
|
21
|
+
});
|
|
22
|
+
const { data: regionsData } = useQuery({
|
|
23
|
+
queryKey: ["athos-regions"],
|
|
24
|
+
queryFn: () => sdk.admin.region.list({ limit: 100 })
|
|
25
|
+
});
|
|
26
|
+
const { data: salesChannelsData } = useQuery({
|
|
27
|
+
queryKey: ["athos-sales-channels"],
|
|
28
|
+
queryFn: () => sdk.admin.salesChannel.list({ limit: 100 })
|
|
29
|
+
});
|
|
30
|
+
const config2 = configData == null ? void 0 : configData.config;
|
|
31
|
+
const regions = (regionsData == null ? void 0 : regionsData.regions) ?? [];
|
|
32
|
+
const salesChannels = (salesChannelsData == null ? void 0 : salesChannelsData.sales_channels) ?? [];
|
|
33
|
+
const [initialized, setInitialized] = useState(false);
|
|
34
|
+
const [form, setForm] = useState({
|
|
35
|
+
storefront_url: "",
|
|
36
|
+
feed_token: "",
|
|
37
|
+
region_id: null,
|
|
38
|
+
sales_channel_ids: [],
|
|
39
|
+
swatch_option_titles: []
|
|
40
|
+
});
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (config2 && !initialized) {
|
|
43
|
+
setForm({
|
|
44
|
+
storefront_url: config2.storefront_url ?? "",
|
|
45
|
+
feed_token: config2.feed_token ?? "",
|
|
46
|
+
region_id: config2.region_id ?? null,
|
|
47
|
+
sales_channel_ids: config2.sales_channel_ids ?? [],
|
|
48
|
+
swatch_option_titles: config2.swatch_option_titles ?? []
|
|
49
|
+
});
|
|
50
|
+
setInitialized(true);
|
|
51
|
+
}
|
|
52
|
+
}, [config2, initialized]);
|
|
53
|
+
const saveMutation = useMutation({
|
|
54
|
+
mutationFn: (data) => sdk.client.fetch("/admin/athos/config", {
|
|
55
|
+
method: "POST",
|
|
56
|
+
body: data
|
|
57
|
+
}),
|
|
58
|
+
onSuccess: () => {
|
|
59
|
+
queryClient.invalidateQueries({ queryKey: ["athos-config"] });
|
|
60
|
+
toast.success("Settings saved");
|
|
61
|
+
},
|
|
62
|
+
onError: (error) => {
|
|
63
|
+
toast.error((error == null ? void 0 : error.message) ?? "Failed to save settings");
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
const handleGenerateToken = () => {
|
|
67
|
+
setForm((f) => ({ ...f, feed_token: crypto.randomUUID() }));
|
|
68
|
+
};
|
|
69
|
+
const handleSalesChannelToggle = (id) => {
|
|
70
|
+
setForm((f) => ({
|
|
71
|
+
...f,
|
|
72
|
+
sales_channel_ids: f.sales_channel_ids.includes(id) ? f.sales_channel_ids.filter((scId) => scId !== id) : [...f.sales_channel_ids, id]
|
|
73
|
+
}));
|
|
74
|
+
};
|
|
75
|
+
const feedUrl = form.feed_token ? `${backendUrl}/athos/feed?token=${form.feed_token}` : "";
|
|
76
|
+
const handleCopyFeedUrl = () => {
|
|
77
|
+
if (!feedUrl) return;
|
|
78
|
+
navigator.clipboard.writeText(feedUrl).then(() => toast.success("Feed URL copied to clipboard")).catch(() => toast.error("Failed to copy URL"));
|
|
79
|
+
};
|
|
80
|
+
if (configLoading) {
|
|
81
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center p-12", children: "Spinner ..." });
|
|
82
|
+
}
|
|
83
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4 p-4", children: [
|
|
84
|
+
/* @__PURE__ */ jsxs(Container, { children: [
|
|
85
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center justify-between px-6 py-4", children: /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Athos Feed Settings" }) }),
|
|
86
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-4 px-6 pb-6", children: [
|
|
87
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
88
|
+
/* @__PURE__ */ jsx(Label, { children: "Storefront URL" }),
|
|
89
|
+
/* @__PURE__ */ jsx(
|
|
90
|
+
Input,
|
|
91
|
+
{
|
|
92
|
+
placeholder: "https://mystore.com",
|
|
93
|
+
value: form.storefront_url,
|
|
94
|
+
onChange: (e) => setForm((f) => ({ ...f, storefront_url: e.target.value }))
|
|
95
|
+
}
|
|
96
|
+
),
|
|
97
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Base URL used to build product links in the feed" })
|
|
98
|
+
] }),
|
|
99
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
100
|
+
/* @__PURE__ */ jsx(Label, { children: "Feed Token" }),
|
|
101
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-x-2", children: [
|
|
102
|
+
/* @__PURE__ */ jsx(
|
|
103
|
+
Input,
|
|
104
|
+
{
|
|
105
|
+
placeholder: "Secret token",
|
|
106
|
+
value: form.feed_token,
|
|
107
|
+
onChange: (e) => setForm((f) => ({ ...f, feed_token: e.target.value }))
|
|
108
|
+
}
|
|
109
|
+
),
|
|
110
|
+
/* @__PURE__ */ jsx(
|
|
111
|
+
Button,
|
|
112
|
+
{
|
|
113
|
+
size: "small",
|
|
114
|
+
variant: "secondary",
|
|
115
|
+
onClick: handleGenerateToken,
|
|
116
|
+
children: "Generate"
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
] }),
|
|
120
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Secret used to authenticate Athos feed requests" })
|
|
121
|
+
] }),
|
|
122
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
123
|
+
/* @__PURE__ */ jsx(Label, { children: "Region" }),
|
|
124
|
+
/* @__PURE__ */ jsxs(
|
|
125
|
+
Select,
|
|
126
|
+
{
|
|
127
|
+
value: form.region_id ?? "",
|
|
128
|
+
onValueChange: (val) => setForm((f) => ({ ...f, region_id: val || null })),
|
|
129
|
+
children: [
|
|
130
|
+
/* @__PURE__ */ jsx(Select.Trigger, { children: /* @__PURE__ */ jsx(Select.Value, { placeholder: "All regions" }) }),
|
|
131
|
+
/* @__PURE__ */ jsxs(Select.Content, { children: [
|
|
132
|
+
/* @__PURE__ */ jsx(Select.Item, { value: "-", children: "All regions" }),
|
|
133
|
+
regions.map((region) => /* @__PURE__ */ jsxs(Select.Item, { value: region.id, children: [
|
|
134
|
+
region.name,
|
|
135
|
+
" (",
|
|
136
|
+
region.currency_code.toUpperCase(),
|
|
137
|
+
")"
|
|
138
|
+
] }, region.id))
|
|
139
|
+
] })
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
),
|
|
143
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Filter prices to a specific region's currency. Leave empty to include all currencies." })
|
|
144
|
+
] }),
|
|
145
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
146
|
+
/* @__PURE__ */ jsx(Label, { children: "Sales Channels" }),
|
|
147
|
+
salesChannels.length === 0 ? /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "No sales channels found" }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
148
|
+
salesChannels.map((sc) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-2", children: [
|
|
149
|
+
/* @__PURE__ */ jsx(
|
|
150
|
+
Checkbox,
|
|
151
|
+
{
|
|
152
|
+
id: `sc-${sc.id}`,
|
|
153
|
+
checked: form.sales_channel_ids.includes(sc.id),
|
|
154
|
+
onCheckedChange: () => handleSalesChannelToggle(sc.id)
|
|
155
|
+
}
|
|
156
|
+
),
|
|
157
|
+
/* @__PURE__ */ jsx("label", { htmlFor: `sc-${sc.id}`, className: "cursor-pointer", children: /* @__PURE__ */ jsx(Text, { size: "small", children: sc.name }) })
|
|
158
|
+
] }, sc.id)),
|
|
159
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Filter products by sales channel. Leave all unchecked to include all channels." })
|
|
160
|
+
] })
|
|
161
|
+
] }),
|
|
162
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-y-2", children: [
|
|
163
|
+
/* @__PURE__ */ jsx(Label, { children: "Swatch Option Titles" }),
|
|
164
|
+
/* @__PURE__ */ jsx(
|
|
165
|
+
Input,
|
|
166
|
+
{
|
|
167
|
+
placeholder: "Color, Colour, Material",
|
|
168
|
+
value: form.swatch_option_titles.join(", "),
|
|
169
|
+
onChange: (e) => setForm((f) => ({
|
|
170
|
+
...f,
|
|
171
|
+
swatch_option_titles: e.target.value.split(",").map((s) => s.trim()).filter(Boolean)
|
|
172
|
+
}))
|
|
173
|
+
}
|
|
174
|
+
),
|
|
175
|
+
/* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Comma-separated option names to render as visual swatches in Athos (e.g. Color, Material). Defaults to Color and Colour if empty." })
|
|
176
|
+
] }),
|
|
177
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
178
|
+
Button,
|
|
179
|
+
{
|
|
180
|
+
size: "small",
|
|
181
|
+
onClick: () => saveMutation.mutate(form),
|
|
182
|
+
isLoading: saveMutation.isPending,
|
|
183
|
+
disabled: saveMutation.isPending,
|
|
184
|
+
children: "Save"
|
|
185
|
+
}
|
|
186
|
+
) })
|
|
187
|
+
] })
|
|
188
|
+
] }),
|
|
189
|
+
/* @__PURE__ */ jsxs(Container, { children: [
|
|
190
|
+
/* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Feed URL" }) }),
|
|
191
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-y-3 px-6 pb-6", children: feedUrl ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
192
|
+
/* @__PURE__ */ jsx("div", { className: "bg-ui-bg-subtle rounded-md px-3 py-2", children: /* @__PURE__ */ jsx(
|
|
193
|
+
Text,
|
|
194
|
+
{
|
|
195
|
+
size: "small",
|
|
196
|
+
className: "break-all font-mono text-ui-fg-subtle",
|
|
197
|
+
children: feedUrl
|
|
198
|
+
}
|
|
199
|
+
) }),
|
|
200
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
|
|
201
|
+
Button,
|
|
202
|
+
{
|
|
203
|
+
size: "small",
|
|
204
|
+
variant: "secondary",
|
|
205
|
+
onClick: handleCopyFeedUrl,
|
|
206
|
+
children: "Copy URL"
|
|
207
|
+
}
|
|
208
|
+
) })
|
|
209
|
+
] }) : /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Save a feed token above to generate the feed URL." }) })
|
|
210
|
+
] })
|
|
211
|
+
] });
|
|
212
|
+
};
|
|
213
|
+
const handle = {
|
|
214
|
+
breadcrumb: () => "Athos"
|
|
215
|
+
};
|
|
216
|
+
const config = defineRouteConfig({
|
|
217
|
+
label: "Athos Commerce",
|
|
218
|
+
icon: FunnelPlus
|
|
219
|
+
});
|
|
220
|
+
const i18nTranslations0 = {};
|
|
221
|
+
const widgetModule = { widgets: [] };
|
|
222
|
+
const routeModule = {
|
|
223
|
+
routes: [
|
|
224
|
+
{
|
|
225
|
+
Component: AthosPage,
|
|
226
|
+
path: "/athos",
|
|
227
|
+
handle
|
|
228
|
+
}
|
|
229
|
+
]
|
|
230
|
+
};
|
|
231
|
+
const menuItemModule = {
|
|
232
|
+
menuItems: [
|
|
233
|
+
{
|
|
234
|
+
label: config.label,
|
|
235
|
+
icon: config.icon,
|
|
236
|
+
path: "/athos",
|
|
237
|
+
nested: void 0,
|
|
238
|
+
rank: void 0,
|
|
239
|
+
translationNs: void 0
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
};
|
|
243
|
+
const formModule = { customFields: {} };
|
|
244
|
+
const displayModule = {
|
|
245
|
+
displays: {}
|
|
246
|
+
};
|
|
247
|
+
const i18nModule = { resources: i18nTranslations0 };
|
|
248
|
+
const plugin = {
|
|
249
|
+
widgetModule,
|
|
250
|
+
routeModule,
|
|
251
|
+
menuItemModule,
|
|
252
|
+
formModule,
|
|
253
|
+
displayModule,
|
|
254
|
+
i18nModule
|
|
255
|
+
};
|
|
256
|
+
export {
|
|
257
|
+
plugin as default
|
|
258
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { AuthenticatedMedusaRequest, MedusaResponse } from "@medusajs/framework/http";
|
|
2
|
+
import { UpsertAthosConfigSchema } from "../middlewares";
|
|
3
|
+
export declare function GET(req: AuthenticatedMedusaRequest, res: MedusaResponse): Promise<MedusaResponse>;
|
|
4
|
+
export declare function POST(req: AuthenticatedMedusaRequest<UpsertAthosConfigSchema>, res: MedusaResponse): Promise<MedusaResponse>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.GET = GET;
|
|
7
|
+
exports.POST = POST;
|
|
8
|
+
const athosConfig_1 = require("../../../../modules/athosConfig");
|
|
9
|
+
const upsert_athos_config_1 = __importDefault(require("../../../../workflows/upsert-athos-config"));
|
|
10
|
+
async function GET(req, res) {
|
|
11
|
+
const athosConfigService = req.scope.resolve(athosConfig_1.ATHOS_CONFIG_MODULE);
|
|
12
|
+
const [configs] = await athosConfigService.listAndCountAthosConfigs();
|
|
13
|
+
return res.json({ config: configs[0] ?? null });
|
|
14
|
+
}
|
|
15
|
+
async function POST(req, res) {
|
|
16
|
+
const { result } = await (0, upsert_athos_config_1.default)(req.scope).run({
|
|
17
|
+
input: req.validatedBody,
|
|
18
|
+
});
|
|
19
|
+
return res.json({ config: result });
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2F0aG9zL2NvbmZpZy9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQU1BLGtCQVVDO0FBRUQsb0JBU0M7QUExQkQsaUVBQXFFO0FBRXJFLG9HQUFpRjtBQUcxRSxLQUFLLFVBQVUsR0FBRyxDQUN2QixHQUErQixFQUMvQixHQUFtQjtJQUVuQixNQUFNLGtCQUFrQixHQUFHLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUMxQyxpQ0FBbUIsQ0FDUSxDQUFBO0lBQzdCLE1BQU0sQ0FBQyxPQUFPLENBQUMsR0FBRyxNQUFNLGtCQUFrQixDQUFDLHdCQUF3QixFQUFFLENBQUE7SUFFckUsT0FBTyxHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxJQUFJLEVBQUUsQ0FBQyxDQUFBO0FBQ2pELENBQUM7QUFFTSxLQUFLLFVBQVUsSUFBSSxDQUN4QixHQUF3RCxFQUN4RCxHQUFtQjtJQUVuQixNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxJQUFBLDZCQUF5QixFQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7UUFDaEUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxhQUFhO0tBQ3pCLENBQUMsQ0FBQTtJQUVGLE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFBO0FBQ3JDLENBQUMifQ==
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MiddlewareRoute } from "@medusajs/framework/http";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export declare const UpsertAthosConfigSchema: z.ZodObject<{
|
|
4
|
+
storefront_url: z.ZodString;
|
|
5
|
+
feed_token: z.ZodString;
|
|
6
|
+
region_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
7
|
+
sales_channel_ids: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
8
|
+
swatch_option_titles: z.ZodOptional<z.ZodNullable<z.ZodArray<z.ZodString>>>;
|
|
9
|
+
}, z.core.$strip>;
|
|
10
|
+
export type UpsertAthosConfigSchema = z.infer<typeof UpsertAthosConfigSchema>;
|
|
11
|
+
export declare const adminAthosMiddlewares: MiddlewareRoute[];
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.adminAthosMiddlewares = exports.UpsertAthosConfigSchema = void 0;
|
|
4
|
+
const http_1 = require("@medusajs/framework/http");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
exports.UpsertAthosConfigSchema = zod_1.z.object({
|
|
7
|
+
storefront_url: zod_1.z.string().min(1),
|
|
8
|
+
feed_token: zod_1.z.string().min(1),
|
|
9
|
+
region_id: zod_1.z.string().nullable().optional(),
|
|
10
|
+
sales_channel_ids: zod_1.z.array(zod_1.z.string()).nullable().optional(),
|
|
11
|
+
swatch_option_titles: zod_1.z.array(zod_1.z.string()).nullable().optional(),
|
|
12
|
+
});
|
|
13
|
+
exports.adminAthosMiddlewares = [
|
|
14
|
+
{
|
|
15
|
+
matcher: "/admin/athos/config",
|
|
16
|
+
method: "POST",
|
|
17
|
+
middlewares: [(0, http_1.validateAndTransformBody)(exports.UpsertAthosConfigSchema)],
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2F0aG9zL21pZGRsZXdhcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1EQUFvRjtBQUNwRiw2QkFBdUI7QUFFVixRQUFBLHVCQUF1QixHQUFHLE9BQUMsQ0FBQyxNQUFNLENBQUM7SUFDOUMsY0FBYyxFQUFFLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDO0lBQ2pDLFVBQVUsRUFBRSxPQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztJQUM3QixTQUFTLEVBQUUsT0FBQyxDQUFDLE1BQU0sRUFBRSxDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUMzQyxpQkFBaUIsRUFBRSxPQUFDLENBQUMsS0FBSyxDQUFDLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUM1RCxvQkFBb0IsRUFBRSxPQUFDLENBQUMsS0FBSyxDQUFDLE9BQUMsQ0FBQyxNQUFNLEVBQUUsQ0FBQyxDQUFDLFFBQVEsRUFBRSxDQUFDLFFBQVEsRUFBRTtDQUNoRSxDQUFDLENBQUE7QUFJVyxRQUFBLHFCQUFxQixHQUFzQjtJQUN0RDtRQUNFLE9BQU8sRUFBRSxxQkFBcUI7UUFDOUIsTUFBTSxFQUFFLE1BQU07UUFDZCxXQUFXLEVBQUUsQ0FBQyxJQUFBLCtCQUF3QixFQUFDLCtCQUF1QixDQUFDLENBQUM7S0FDakU7Q0FDRixDQUFBIn0=
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GET = GET;
|
|
4
|
+
async function GET(req, res) {
|
|
5
|
+
res.sendStatus(200);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL3BsdWdpbi9yb3V0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLGtCQU1DO0FBTk0sS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBa0IsRUFDbEIsR0FBbUI7SUFHbkIsR0FBRyxDQUFDLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztBQUN0QixDQUFDIn0=
|