@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.
Files changed (34) hide show
  1. package/.medusa/server/src/admin/index.js +259 -0
  2. package/.medusa/server/src/admin/index.mjs +258 -0
  3. package/.medusa/server/src/api/admin/athos/config/route.d.ts +4 -0
  4. package/.medusa/server/src/api/admin/athos/config/route.js +21 -0
  5. package/.medusa/server/src/api/admin/athos/middlewares.d.ts +11 -0
  6. package/.medusa/server/src/api/admin/athos/middlewares.js +20 -0
  7. package/.medusa/server/src/api/admin/plugin/route.d.ts +2 -0
  8. package/.medusa/server/src/api/admin/plugin/route.js +7 -0
  9. package/.medusa/server/src/api/athos/feed/route.d.ts +2 -0
  10. package/.medusa/server/src/api/athos/feed/route.js +249 -0
  11. package/.medusa/server/src/api/athos/middlewares.d.ts +2 -0
  12. package/.medusa/server/src/api/athos/middlewares.js +26 -0
  13. package/.medusa/server/src/api/middlewares.d.ts +2 -0
  14. package/.medusa/server/src/api/middlewares.js +9 -0
  15. package/.medusa/server/src/api/store/plugin/route.d.ts +2 -0
  16. package/.medusa/server/src/api/store/plugin/route.js +7 -0
  17. package/.medusa/server/src/modules/athosConfig/index.d.ts +21 -0
  18. package/.medusa/server/src/modules/athosConfig/index.js +13 -0
  19. package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000000.d.ts +5 -0
  20. package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000000.js +26 -0
  21. package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000001.d.ts +5 -0
  22. package/.medusa/server/src/modules/athosConfig/migrations/Migration20260518000001.js +14 -0
  23. package/.medusa/server/src/modules/athosConfig/models/athos-config.d.ts +9 -0
  24. package/.medusa/server/src/modules/athosConfig/models/athos-config.js +13 -0
  25. package/.medusa/server/src/modules/athosConfig/service.d.ts +17 -0
  26. package/.medusa/server/src/modules/athosConfig/service.js +32 -0
  27. package/.medusa/server/src/modules/athosConfig/types.d.ts +15 -0
  28. package/.medusa/server/src/modules/athosConfig/types.js +3 -0
  29. package/.medusa/server/src/workflows/steps/upsert-athos-config-step.d.ts +9 -0
  30. package/.medusa/server/src/workflows/steps/upsert-athos-config-step.js +31 -0
  31. package/.medusa/server/src/workflows/upsert-athos-config.d.ts +9 -0
  32. package/.medusa/server/src/workflows/upsert-athos-config.js +10 -0
  33. package/README.md +198 -0
  34. 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,2 @@
1
+ import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
+ export declare function GET(req: MedusaRequest, res: MedusaResponse): Promise<void>;
@@ -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=
@@ -0,0 +1,2 @@
1
+ import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http";
2
+ export declare function GET(req: MedusaRequest, res: MedusaResponse): Promise<void>;