medusa-plugin-printify 0.1.0

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 (39) hide show
  1. package/.medusa/server/jest.config.js +20 -0
  2. package/.medusa/server/jest.setup.js +4 -0
  3. package/.medusa/server/src/admin/index.js +472 -0
  4. package/.medusa/server/src/admin/index.mjs +473 -0
  5. package/.medusa/server/src/api/admin/plugin/route.js +7 -0
  6. package/.medusa/server/src/api/admin/printify/orders/[id]/submit/route.js +19 -0
  7. package/.medusa/server/src/api/admin/printify/orders/route.js +18 -0
  8. package/.medusa/server/src/api/admin/printify/products/[id]/route.js +113 -0
  9. package/.medusa/server/src/api/admin/printify/products/route.js +35 -0
  10. package/.medusa/server/src/api/admin/printify/shops/route.js +18 -0
  11. package/.medusa/server/src/api/middlewares.js +12 -0
  12. package/.medusa/server/src/api/store/plugin/route.js +7 -0
  13. package/.medusa/server/src/api/webhooks/printify/route.js +102 -0
  14. package/.medusa/server/src/jobs/auto-submit-orders-job.js +31 -0
  15. package/.medusa/server/src/jobs/sync-products-job.js +23 -0
  16. package/.medusa/server/src/lib/webhook-utils.js +13 -0
  17. package/.medusa/server/src/links/printify-order-medusa-order.js +10 -0
  18. package/.medusa/server/src/links/printify-product-medusa-product.js +10 -0
  19. package/.medusa/server/src/modules/printify/api-client.js +67 -0
  20. package/.medusa/server/src/modules/printify/index.js +13 -0
  21. package/.medusa/server/src/modules/printify/migrations/Migration20260305115907.js +21 -0
  22. package/.medusa/server/src/modules/printify/migrations/Migration20260313185144.js +14 -0
  23. package/.medusa/server/src/modules/printify/models/order.js +18 -0
  24. package/.medusa/server/src/modules/printify/models/product.js +22 -0
  25. package/.medusa/server/src/modules/printify/models/shop.js +11 -0
  26. package/.medusa/server/src/modules/printify/service.js +38 -0
  27. package/.medusa/server/src/modules/printify/types.js +3 -0
  28. package/.medusa/server/src/subscribers/order-placed.js +55 -0
  29. package/.medusa/server/src/subscribers/plugin-registered.js +37 -0
  30. package/.medusa/server/src/utils/build-variant-prices.js +17 -0
  31. package/.medusa/server/src/utils/currency-converter.js +46 -0
  32. package/.medusa/server/src/workflows/create-printify-order.js +86 -0
  33. package/.medusa/server/src/workflows/index.js +12 -0
  34. package/.medusa/server/src/workflows/option-mapper.js +64 -0
  35. package/.medusa/server/src/workflows/submit-printify-order.js +29 -0
  36. package/.medusa/server/src/workflows/sync-products.js +313 -0
  37. package/.medusa/server/src/workflows/sync-shops.js +30 -0
  38. package/README.md +253 -0
  39. package/package.json +78 -0
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const config = {
4
+ preset: "ts-jest",
5
+ testEnvironment: "node",
6
+ resetMocks: true,
7
+ setupFilesAfterEnv: ["./jest.setup.ts"],
8
+ moduleNameMapper: {
9
+ "^(\\.{1,2}/.*)\\.js$": "$1",
10
+ "^@/(.*)$": "<rootDir>/src/$1",
11
+ },
12
+ testMatch: [
13
+ "<rootDir>/tests/**/*.test.ts",
14
+ ],
15
+ transform: {
16
+ "^.+\\.tsx?$": ["ts-jest", { tsconfig: "tsconfig.json" }],
17
+ },
18
+ };
19
+ exports.default = config;
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9qZXN0LmNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOztBQUVBLE1BQU0sTUFBTSxHQUFXO0lBQ3JCLE1BQU0sRUFBRSxTQUFTO0lBQ2pCLGVBQWUsRUFBRSxNQUFNO0lBQ3ZCLFVBQVUsRUFBRSxJQUFJO0lBQ2hCLGtCQUFrQixFQUFFLENBQUMsaUJBQWlCLENBQUM7SUFDdkMsZ0JBQWdCLEVBQUU7UUFDaEIsc0JBQXNCLEVBQUUsSUFBSTtRQUM1QixVQUFVLEVBQUUsa0JBQWtCO0tBQy9CO0lBQ0QsU0FBUyxFQUFFO1FBQ1QsOEJBQThCO0tBQy9CO0lBQ0QsU0FBUyxFQUFFO1FBQ1QsYUFBYSxFQUFFLENBQUMsU0FBUyxFQUFFLEVBQUUsUUFBUSxFQUFFLGVBQWUsRUFBRSxDQUFDO0tBQzFEO0NBQ0YsQ0FBQTtBQUVELGtCQUFlLE1BQU0sQ0FBQSJ9
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // Global test setup - intentionally empty for now
4
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiamVzdC5zZXR1cC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL2plc3Quc2V0dXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxrREFBa0QifQ==
@@ -0,0 +1,472 @@
1
+ "use strict";
2
+ const jsxRuntime = require("react/jsx-runtime");
3
+ const adminSdk = require("@medusajs/admin-sdk");
4
+ const ui = require("@medusajs/ui");
5
+ const react = require("react");
6
+ const icons = require("@medusajs/icons");
7
+ const reactRouterDom = require("react-router-dom");
8
+ const PrintifyOrderWidget = ({ data }) => {
9
+ const [printifyOrders, setPrintifyOrders] = react.useState([]);
10
+ const [submitting, setSubmitting] = react.useState(false);
11
+ const loadOrders = async () => {
12
+ const res = await fetch(
13
+ `/admin/printify/orders?medusa_order_id=${data.id}`,
14
+ { credentials: "include" }
15
+ );
16
+ const json = await res.json();
17
+ setPrintifyOrders(json.orders ?? []);
18
+ };
19
+ const submitOrder = async (orderId) => {
20
+ setSubmitting(true);
21
+ try {
22
+ await fetch(`/admin/printify/orders/${orderId}/submit`, {
23
+ method: "POST",
24
+ credentials: "include"
25
+ });
26
+ await loadOrders();
27
+ } finally {
28
+ setSubmitting(false);
29
+ }
30
+ };
31
+ react.useEffect(() => {
32
+ loadOrders();
33
+ }, [data.id]);
34
+ if (printifyOrders.length === 0) return null;
35
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-4", children: [
36
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { className: "mb-3", level: "h2", children: "Printify Fulfillment" }),
37
+ printifyOrders.map((order) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between py-2 border-b last:border-0", children: [
38
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
39
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "font-medium", children: order.printify_id ?? "Not yet submitted" }),
40
+ /* @__PURE__ */ jsxRuntime.jsx(
41
+ ui.Badge,
42
+ {
43
+ color: order.status === "shipped" ? "green" : order.status === "in-production" ? "blue" : order.status === "pending" ? "orange" : "grey",
44
+ size: "2xsmall",
45
+ className: "mt-1",
46
+ children: order.status
47
+ }
48
+ )
49
+ ] }),
50
+ order.status === "pending" && /* @__PURE__ */ jsxRuntime.jsx(
51
+ ui.Button,
52
+ {
53
+ size: "small",
54
+ variant: "secondary",
55
+ onClick: () => submitOrder(order.id),
56
+ isLoading: submitting,
57
+ children: "Submit to Printify"
58
+ }
59
+ ),
60
+ order.total_cost && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-muted", children: [
61
+ "Cost: $",
62
+ (order.total_cost / 100).toFixed(2)
63
+ ] })
64
+ ] }, order.id))
65
+ ] });
66
+ };
67
+ adminSdk.defineWidgetConfig({
68
+ zone: "order.details.after"
69
+ });
70
+ const PrintifyPage = () => {
71
+ const [shops, setShops] = react.useState([]);
72
+ const [syncing, setSyncing] = react.useState(false);
73
+ const loadShops = async () => {
74
+ const res = await fetch("/admin/printify/shops", { credentials: "include" });
75
+ const json = await res.json();
76
+ setShops(json.shops ?? []);
77
+ };
78
+ const syncShops = async () => {
79
+ setSyncing(true);
80
+ try {
81
+ await fetch("/admin/printify/shops", { method: "POST", credentials: "include" });
82
+ await loadShops();
83
+ } finally {
84
+ setSyncing(false);
85
+ }
86
+ };
87
+ react.useEffect(() => {
88
+ loadShops();
89
+ }, []);
90
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-8", children: [
91
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-6", children: [
92
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Printify Integration" }),
93
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: syncShops, isLoading: syncing, size: "small", children: "Sync Shops" })
94
+ ] }),
95
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
96
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
97
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Shop Name" }),
98
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Printify ID" }),
99
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, {})
100
+ ] }) }),
101
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: shops.map((shop) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
102
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
103
+ reactRouterDom.Link,
104
+ {
105
+ to: `/printify/products?shop_id=${shop.printify_id}&shop_name=${encodeURIComponent(shop.title)}`,
106
+ className: "block w-full text-inherit no-underline",
107
+ children: shop.title
108
+ }
109
+ ) }),
110
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
111
+ reactRouterDom.Link,
112
+ {
113
+ to: `/printify/products?shop_id=${shop.printify_id}&shop_name=${encodeURIComponent(shop.title)}`,
114
+ className: "block w-full no-underline",
115
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: "grey", size: "2xsmall", children: shop.printify_id })
116
+ }
117
+ ) }),
118
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
119
+ reactRouterDom.Link,
120
+ {
121
+ to: `/printify/products?shop_id=${shop.printify_id}&shop_name=${encodeURIComponent(shop.title)}`,
122
+ className: "block w-full no-underline text-ui-fg-muted",
123
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowRight, {})
124
+ }
125
+ ) })
126
+ ] }, shop.id)) })
127
+ ] })
128
+ ] });
129
+ };
130
+ const config = adminSdk.defineRouteConfig({
131
+ label: "Printify",
132
+ icon: icons.BuildingStorefront
133
+ });
134
+ const PrintifyProductsPage = () => {
135
+ const [products, setProducts] = react.useState([]);
136
+ const [syncing, setSyncing] = react.useState(false);
137
+ const [search, setSearch] = react.useState("");
138
+ const [searchParams] = reactRouterDom.useSearchParams();
139
+ const shopId = searchParams.get("shop_id");
140
+ const shopName = searchParams.get("shop_name") ?? shopId ?? "All Shops";
141
+ const loadProducts = async () => {
142
+ const params = new URLSearchParams();
143
+ if (shopId) params.set("shop_id", shopId);
144
+ const res = await fetch(`/admin/printify/products?${params}`, { credentials: "include" });
145
+ const json = await res.json();
146
+ setProducts(json.products ?? []);
147
+ };
148
+ const syncProducts = async () => {
149
+ setSyncing(true);
150
+ try {
151
+ await fetch("/admin/printify/products", {
152
+ method: "POST",
153
+ credentials: "include"
154
+ });
155
+ await loadProducts();
156
+ } finally {
157
+ setSyncing(false);
158
+ }
159
+ };
160
+ const toggleVisibility = async (product) => {
161
+ const newValue = !product.is_published;
162
+ setProducts(
163
+ (prev) => prev.map((p) => p.id === product.id ? { ...p, is_published: newValue } : p)
164
+ );
165
+ try {
166
+ const res = await fetch(`/admin/printify/products/${product.id}`, {
167
+ method: "PATCH",
168
+ credentials: "include",
169
+ headers: { "Content-Type": "application/json" },
170
+ body: JSON.stringify({ is_published: newValue })
171
+ });
172
+ if (!res.ok) throw new Error("Request failed");
173
+ } catch {
174
+ setProducts(
175
+ (prev) => prev.map((p) => p.id === product.id ? { ...p, is_published: product.is_published } : p)
176
+ );
177
+ }
178
+ };
179
+ react.useEffect(() => {
180
+ loadProducts();
181
+ }, [shopId]);
182
+ const filtered = products.filter(
183
+ (p) => p.title.toLowerCase().includes(search.toLowerCase())
184
+ );
185
+ const getThumb = (images) => {
186
+ const img = (images == null ? void 0 : images.find((i) => i.is_default)) ?? (images == null ? void 0 : images[0]);
187
+ return (img == null ? void 0 : img.src) ?? null;
188
+ };
189
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-8", children: [
190
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4", children: /* @__PURE__ */ jsxRuntime.jsxs(
191
+ reactRouterDom.Link,
192
+ {
193
+ to: "/printify",
194
+ className: "flex items-center gap-1 text-ui-fg-muted hover:text-ui-fg-base text-sm no-underline",
195
+ children: [
196
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, { className: "w-4 h-4" }),
197
+ "Back to Shops"
198
+ ]
199
+ }
200
+ ) }),
201
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center mb-6", children: [
202
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
203
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: shopName }),
204
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-muted", children: [
205
+ products.length,
206
+ " product",
207
+ products.length !== 1 ? "s" : ""
208
+ ] })
209
+ ] }),
210
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: syncProducts, isLoading: syncing, size: "small", children: "Sync Products" })
211
+ ] }),
212
+ /* @__PURE__ */ jsxRuntime.jsx(
213
+ ui.Input,
214
+ {
215
+ placeholder: "Search products...",
216
+ value: search,
217
+ onChange: (e) => setSearch(e.target.value),
218
+ className: "mb-4 max-w-xs"
219
+ }
220
+ ),
221
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
222
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
223
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { className: "w-12" }),
224
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Title" }),
225
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Variants" }),
226
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Status" }),
227
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Visible on Store" })
228
+ ] }) }),
229
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: filtered.map((product) => {
230
+ var _a;
231
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
232
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/printify/products/${product.id}`, className: "block", children: getThumb(product.images) ? /* @__PURE__ */ jsxRuntime.jsx(
233
+ "img",
234
+ {
235
+ src: getThumb(product.images),
236
+ alt: "",
237
+ className: "w-10 h-10 rounded object-cover"
238
+ }
239
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-10 h-10 rounded bg-ui-bg-subtle" }) }) }),
240
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/printify/products/${product.id}`, className: "block text-inherit no-underline", children: product.title }) }),
241
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsxs(reactRouterDom.Link, { to: `/printify/products/${product.id}`, className: "block text-inherit no-underline", children: [
242
+ ((_a = product.variants) == null ? void 0 : _a.length) ?? 0,
243
+ " variants"
244
+ ] }) }),
245
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/printify/products/${product.id}`, className: "block no-underline", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: product.is_published ? "green" : "grey", size: "2xsmall", children: product.is_published ? "Published" : "Draft" }) }) }),
246
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.jsx(
247
+ ui.Switch,
248
+ {
249
+ checked: product.is_published,
250
+ onCheckedChange: () => toggleVisibility(product)
251
+ }
252
+ ) })
253
+ ] }, product.id);
254
+ }) })
255
+ ] })
256
+ ] });
257
+ };
258
+ const formatCents = (cents) => `$${(cents / 100).toFixed(2)}`;
259
+ const PrintifyProductDetailPage = () => {
260
+ var _a, _b;
261
+ const { id } = reactRouterDom.useParams();
262
+ const [product, setProduct] = react.useState(null);
263
+ const [medusaProductId, setMedusaProductId] = react.useState(null);
264
+ const [loading, setLoading] = react.useState(true);
265
+ const [rawOpen, setRawOpen] = react.useState(false);
266
+ const [isPublished, setIsPublished] = react.useState(false);
267
+ const [toggling, setToggling] = react.useState(false);
268
+ react.useEffect(() => {
269
+ const load = async () => {
270
+ try {
271
+ const res = await fetch(`/admin/printify/products/${id}`, { credentials: "include" });
272
+ const json = await res.json();
273
+ setProduct(json.product);
274
+ setIsPublished(json.product.is_published);
275
+ setMedusaProductId(json.medusa_product_id ?? null);
276
+ } finally {
277
+ setLoading(false);
278
+ }
279
+ };
280
+ load();
281
+ }, [id]);
282
+ const toggleVisibility = async () => {
283
+ setToggling(true);
284
+ const newValue = !isPublished;
285
+ try {
286
+ const res = await fetch(`/admin/printify/products/${id}`, {
287
+ method: "PATCH",
288
+ credentials: "include",
289
+ headers: { "Content-Type": "application/json" },
290
+ body: JSON.stringify({ is_published: newValue })
291
+ });
292
+ if (res.ok) {
293
+ setIsPublished(newValue);
294
+ }
295
+ } finally {
296
+ setToggling(false);
297
+ }
298
+ };
299
+ if (loading) {
300
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-8", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Loading..." }) });
301
+ }
302
+ if (!product) {
303
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-8", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: "Product not found." }) });
304
+ }
305
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 p-8", children: [
306
+ /* @__PURE__ */ jsxRuntime.jsxs(
307
+ reactRouterDom.Link,
308
+ {
309
+ to: "/printify/products",
310
+ className: "flex items-center gap-1 text-ui-fg-muted hover:text-ui-fg-base text-sm w-fit no-underline",
311
+ children: [
312
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, { className: "w-4 h-4" }),
313
+ "Back to Products"
314
+ ]
315
+ }
316
+ ),
317
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-6", children: [
318
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between", children: [
319
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
320
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: product.title }),
321
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-muted mt-1", children: [
322
+ "Printify ID: ",
323
+ product.printify_id
324
+ ] })
325
+ ] }),
326
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
327
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { color: isPublished ? "green" : "grey", size: "small", children: isPublished ? "Published" : "Draft" }),
328
+ /* @__PURE__ */ jsxRuntime.jsx(
329
+ ui.Switch,
330
+ {
331
+ id: "visibility-toggle",
332
+ checked: isPublished,
333
+ onCheckedChange: toggleVisibility,
334
+ disabled: toggling
335
+ }
336
+ ),
337
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { htmlFor: "visibility-toggle", className: "text-ui-fg-muted text-sm cursor-pointer", children: isPublished ? "Visible on storefront" : "Hidden from storefront" })
338
+ ] })
339
+ ] }),
340
+ product.description && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "mt-3", children: product.description })
341
+ ] }),
342
+ ((_a = product.images) == null ? void 0 : _a.length) > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-6", children: [
343
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-4", children: "Images" }),
344
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-2 md:grid-cols-4 gap-4", children: product.images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
345
+ /* @__PURE__ */ jsxRuntime.jsx(
346
+ "img",
347
+ {
348
+ src: img.src,
349
+ alt: `${product.title} - ${img.position}`,
350
+ className: "w-full aspect-square rounded-lg object-cover"
351
+ }
352
+ ),
353
+ /* @__PURE__ */ jsxRuntime.jsx(
354
+ ui.Badge,
355
+ {
356
+ color: "grey",
357
+ size: "2xsmall",
358
+ className: "absolute bottom-2 left-2",
359
+ children: img.position
360
+ }
361
+ )
362
+ ] }, i)) })
363
+ ] }),
364
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-6", children: [
365
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Heading, { level: "h2", className: "mb-4", children: [
366
+ "Variants (",
367
+ ((_b = product.variants) == null ? void 0 : _b.length) ?? 0,
368
+ ")"
369
+ ] }),
370
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Table, { children: [
371
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
372
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Title" }),
373
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "SKU" }),
374
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Cost" }),
375
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Price" }),
376
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.HeaderCell, { children: "Status" })
377
+ ] }) }),
378
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Body, { children: (product.variants ?? []).map((v) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Table.Row, { children: [
379
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: v.title }),
380
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx("code", { className: "text-xs", children: v.sku }) }),
381
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatCents(v.cost) }),
382
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: formatCents(v.price) }),
383
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Table.Cell, { children: /* @__PURE__ */ jsxRuntime.jsx(
384
+ ui.Badge,
385
+ {
386
+ color: v.is_enabled ? "green" : "grey",
387
+ size: "2xsmall",
388
+ children: v.is_enabled ? "Enabled" : "Disabled"
389
+ }
390
+ ) })
391
+ ] }, v.id)) })
392
+ ] })
393
+ ] }),
394
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-6", children: [
395
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", className: "mb-4", children: "Medusa Product" }),
396
+ medusaProductId ? /* @__PURE__ */ jsxRuntime.jsxs(
397
+ reactRouterDom.Link,
398
+ {
399
+ to: `/products/${medusaProductId}`,
400
+ className: "flex items-center gap-2 text-ui-fg-interactive hover:text-ui-fg-interactive-hover text-sm no-underline",
401
+ children: [
402
+ "View linked product",
403
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, { className: "w-4 h-4" })
404
+ ]
405
+ }
406
+ ) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-muted", children: "No linked Medusa product" })
407
+ ] }),
408
+ product.printify_data && /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-6", children: [
409
+ /* @__PURE__ */ jsxRuntime.jsxs(
410
+ "button",
411
+ {
412
+ onClick: () => setRawOpen(!rawOpen),
413
+ className: "flex items-center gap-2 w-full",
414
+ children: [
415
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Raw Printify Data" }),
416
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: rawOpen ? "Hide" : "Show" })
417
+ ]
418
+ }
419
+ ),
420
+ rawOpen && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-4 p-4 bg-ui-bg-subtle rounded-lg text-xs overflow-auto max-h-96", children: JSON.stringify(product.printify_data, null, 2) })
421
+ ] })
422
+ ] });
423
+ };
424
+ const i18nTranslations0 = {};
425
+ const widgetModule = { widgets: [
426
+ {
427
+ Component: PrintifyOrderWidget,
428
+ zone: ["order.details.after"]
429
+ }
430
+ ] };
431
+ const routeModule = {
432
+ routes: [
433
+ {
434
+ Component: PrintifyPage,
435
+ path: "/printify"
436
+ },
437
+ {
438
+ Component: PrintifyProductsPage,
439
+ path: "/printify/products"
440
+ },
441
+ {
442
+ Component: PrintifyProductDetailPage,
443
+ path: "/printify/products/:id"
444
+ }
445
+ ]
446
+ };
447
+ const menuItemModule = {
448
+ menuItems: [
449
+ {
450
+ label: config.label,
451
+ icon: config.icon,
452
+ path: "/printify",
453
+ nested: void 0,
454
+ rank: void 0,
455
+ translationNs: void 0
456
+ }
457
+ ]
458
+ };
459
+ const formModule = { customFields: {} };
460
+ const displayModule = {
461
+ displays: {}
462
+ };
463
+ const i18nModule = { resources: i18nTranslations0 };
464
+ const plugin = {
465
+ widgetModule,
466
+ routeModule,
467
+ menuItemModule,
468
+ formModule,
469
+ displayModule,
470
+ i18nModule
471
+ };
472
+ module.exports = plugin;