@elizaos/plugin-shopify-ui 2.0.3-beta.6 → 2.0.3-beta.7

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 (79) hide show
  1. package/dist/CustomersPanel.d.ts +11 -0
  2. package/dist/CustomersPanel.d.ts.map +1 -0
  3. package/dist/CustomersPanel.js +86 -0
  4. package/dist/CustomersPanel.js.map +1 -0
  5. package/dist/InventoryLevelsPanel.d.ts +10 -0
  6. package/dist/InventoryLevelsPanel.d.ts.map +1 -0
  7. package/dist/InventoryLevelsPanel.js +190 -0
  8. package/dist/InventoryLevelsPanel.js.map +1 -0
  9. package/dist/OrdersPanel.d.ts +12 -0
  10. package/dist/OrdersPanel.d.ts.map +1 -0
  11. package/dist/OrdersPanel.js +170 -0
  12. package/dist/OrdersPanel.js.map +1 -0
  13. package/dist/ProductsPanel.d.ts +13 -0
  14. package/dist/ProductsPanel.d.ts.map +1 -0
  15. package/dist/ProductsPanel.js +419 -0
  16. package/dist/ProductsPanel.js.map +1 -0
  17. package/dist/ShopifyAppView.d.ts +3 -0
  18. package/dist/ShopifyAppView.d.ts.map +1 -0
  19. package/dist/ShopifyAppView.helpers.d.ts +11 -0
  20. package/dist/ShopifyAppView.helpers.d.ts.map +1 -0
  21. package/dist/ShopifyAppView.helpers.js +59 -0
  22. package/dist/ShopifyAppView.helpers.js.map +1 -0
  23. package/dist/ShopifyAppView.interact.d.ts +2 -0
  24. package/dist/ShopifyAppView.interact.d.ts.map +1 -0
  25. package/dist/ShopifyAppView.interact.js +93 -0
  26. package/dist/ShopifyAppView.interact.js.map +1 -0
  27. package/dist/ShopifyAppView.js +667 -0
  28. package/dist/ShopifyAppView.js.map +1 -0
  29. package/dist/ShopifyView.d.ts +18 -0
  30. package/dist/ShopifyView.d.ts.map +1 -0
  31. package/dist/ShopifyView.js +143 -0
  32. package/dist/ShopifyView.js.map +1 -0
  33. package/dist/StoreOverviewCard.d.ts +13 -0
  34. package/dist/StoreOverviewCard.d.ts.map +1 -0
  35. package/dist/StoreOverviewCard.js +23 -0
  36. package/dist/StoreOverviewCard.js.map +1 -0
  37. package/dist/components/ShopifySpatialView.d.ts +57 -0
  38. package/dist/components/ShopifySpatialView.d.ts.map +1 -0
  39. package/dist/components/ShopifySpatialView.js +419 -0
  40. package/dist/components/ShopifySpatialView.js.map +1 -0
  41. package/dist/index.d.ts +14 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +20 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/plugin.d.ts +14 -0
  46. package/dist/plugin.d.ts.map +1 -0
  47. package/dist/plugin.js +94 -0
  48. package/dist/plugin.js.map +1 -0
  49. package/dist/register-routes.d.ts +2 -0
  50. package/dist/register-routes.d.ts.map +1 -0
  51. package/dist/register-routes.js +6 -0
  52. package/dist/register-routes.js.map +1 -0
  53. package/dist/register-terminal-view.d.ts +15 -0
  54. package/dist/register-terminal-view.d.ts.map +1 -0
  55. package/dist/register-terminal-view.js +37 -0
  56. package/dist/register-terminal-view.js.map +1 -0
  57. package/dist/register.d.ts +2 -0
  58. package/dist/register.d.ts.map +1 -0
  59. package/dist/register.js +17 -0
  60. package/dist/register.js.map +1 -0
  61. package/dist/routes.d.ts +18 -0
  62. package/dist/routes.d.ts.map +1 -0
  63. package/dist/routes.js +518 -0
  64. package/dist/routes.js.map +1 -0
  65. package/dist/shopify-app.d.ts +11 -0
  66. package/dist/shopify-app.d.ts.map +1 -0
  67. package/dist/shopify-app.js +16 -0
  68. package/dist/shopify-app.js.map +1 -0
  69. package/dist/shopify-view-bundle.d.ts +3 -0
  70. package/dist/shopify-view-bundle.d.ts.map +1 -0
  71. package/dist/shopify-view-bundle.js +7 -0
  72. package/dist/shopify-view-bundle.js.map +1 -0
  73. package/dist/useShopifyDashboard.d.ts +118 -0
  74. package/dist/useShopifyDashboard.d.ts.map +1 -0
  75. package/dist/useShopifyDashboard.js +212 -0
  76. package/dist/useShopifyDashboard.js.map +1 -0
  77. package/dist/views/bundle.js +948 -0
  78. package/dist/views/bundle.js.map +1 -0
  79. package/package.json +5 -5
@@ -0,0 +1,419 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import {
3
+ Button,
4
+ Dialog,
5
+ DialogContent,
6
+ DialogFooter,
7
+ DialogHeader,
8
+ DialogTitle,
9
+ Input,
10
+ Skeleton
11
+ } from "@elizaos/ui";
12
+ import { useAgentElement } from "@elizaos/ui/agent-surface";
13
+ import { ChevronLeft, ChevronRight, Image, Package, Plus } from "lucide-react";
14
+ import { useState } from "react";
15
+ function ProductStatusBadge({ status }) {
16
+ const styles = {
17
+ ACTIVE: "bg-ok",
18
+ DRAFT: "bg-muted",
19
+ ARCHIVED: "bg-danger"
20
+ };
21
+ const labels = {
22
+ ACTIVE: "Active",
23
+ DRAFT: "Draft",
24
+ ARCHIVED: "Archived"
25
+ };
26
+ return /* @__PURE__ */ jsx(
27
+ "span",
28
+ {
29
+ role: "img",
30
+ "aria-label": labels[status],
31
+ title: labels[status],
32
+ className: `inline-flex h-2.5 w-2.5 rounded-full ${styles[status]}`
33
+ }
34
+ );
35
+ }
36
+ function CreateProductDialog({ open, onClose }) {
37
+ const [title, setTitle] = useState("");
38
+ const [vendor, setVendor] = useState("");
39
+ const [productType, setProductType] = useState("");
40
+ const [price, setPrice] = useState("");
41
+ const [submitting, setSubmitting] = useState(false);
42
+ const [submitError, setSubmitError] = useState(null);
43
+ const titleInput = useAgentElement({
44
+ id: "create-product-title",
45
+ role: "text-input",
46
+ label: "Product title",
47
+ group: "create-product",
48
+ description: "Title for the new product (required)",
49
+ getValue: () => title,
50
+ onFill: (value) => setTitle(value)
51
+ });
52
+ const vendorInput = useAgentElement({
53
+ id: "create-product-vendor",
54
+ role: "text-input",
55
+ label: "Product vendor",
56
+ group: "create-product",
57
+ description: "Vendor for the new product",
58
+ getValue: () => vendor,
59
+ onFill: (value) => setVendor(value)
60
+ });
61
+ const typeInput = useAgentElement({
62
+ id: "create-product-type",
63
+ role: "text-input",
64
+ label: "Product type",
65
+ group: "create-product",
66
+ description: "Type/category for the new product",
67
+ getValue: () => productType,
68
+ onFill: (value) => setProductType(value)
69
+ });
70
+ const priceInput = useAgentElement({
71
+ id: "create-product-price",
72
+ role: "number-input",
73
+ label: "Product base price",
74
+ group: "create-product",
75
+ description: "Base price for the new product",
76
+ getValue: () => price,
77
+ onFill: (value) => setPrice(value)
78
+ });
79
+ const cancelButton = useAgentElement({
80
+ id: "create-product-cancel",
81
+ role: "button",
82
+ label: "Cancel create product",
83
+ group: "create-product",
84
+ description: "Close the create-product dialog without saving",
85
+ onActivate: onClose
86
+ });
87
+ const submitButton = useAgentElement({
88
+ id: "create-product-submit",
89
+ role: "button",
90
+ label: "Submit new product",
91
+ group: "create-product",
92
+ description: "Create the new draft product"
93
+ });
94
+ function reset() {
95
+ setTitle("");
96
+ setVendor("");
97
+ setProductType("");
98
+ setPrice("");
99
+ setSubmitting(false);
100
+ setSubmitError(null);
101
+ }
102
+ async function handleSubmit(e) {
103
+ e.preventDefault();
104
+ if (!title.trim()) return;
105
+ setSubmitting(true);
106
+ setSubmitError(null);
107
+ try {
108
+ const res = await fetch("/api/shopify/products", {
109
+ method: "POST",
110
+ headers: { "Content-Type": "application/json" },
111
+ body: JSON.stringify({
112
+ title: title.trim(),
113
+ vendor: vendor.trim() || void 0,
114
+ productType: productType.trim() || void 0,
115
+ price: price.trim() || void 0
116
+ })
117
+ });
118
+ if (!res.ok) {
119
+ const text = await res.text().catch(() => "Unknown error");
120
+ throw new Error(text);
121
+ }
122
+ reset();
123
+ onClose();
124
+ } catch (err) {
125
+ setSubmitError(
126
+ err instanceof Error ? err.message : "Failed to create product."
127
+ );
128
+ } finally {
129
+ setSubmitting(false);
130
+ }
131
+ }
132
+ return /* @__PURE__ */ jsx(
133
+ Dialog,
134
+ {
135
+ open,
136
+ onOpenChange: (next) => {
137
+ if (!next) {
138
+ reset();
139
+ onClose();
140
+ }
141
+ },
142
+ children: /* @__PURE__ */ jsxs(DialogContent, { children: [
143
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: "Create product" }) }),
144
+ /* @__PURE__ */ jsxs("form", { onSubmit: (e) => void handleSubmit(e), className: "space-y-3", children: [
145
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
146
+ /* @__PURE__ */ jsxs(
147
+ "label",
148
+ {
149
+ className: "text-xs font-semibold text-muted-strong",
150
+ htmlFor: "product-title",
151
+ children: [
152
+ "Title ",
153
+ /* @__PURE__ */ jsx("span", { className: "text-danger", children: "*" })
154
+ ]
155
+ }
156
+ ),
157
+ /* @__PURE__ */ jsx(
158
+ Input,
159
+ {
160
+ ref: titleInput.ref,
161
+ id: "product-title",
162
+ placeholder: "e.g. Classic T-Shirt",
163
+ value: title,
164
+ onChange: (e) => setTitle(e.target.value),
165
+ required: true,
166
+ ...titleInput.agentProps
167
+ }
168
+ )
169
+ ] }),
170
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
171
+ /* @__PURE__ */ jsx(
172
+ "label",
173
+ {
174
+ className: "text-xs font-semibold text-muted-strong",
175
+ htmlFor: "product-vendor",
176
+ children: "Vendor"
177
+ }
178
+ ),
179
+ /* @__PURE__ */ jsx(
180
+ Input,
181
+ {
182
+ ref: vendorInput.ref,
183
+ id: "product-vendor",
184
+ placeholder: "e.g. Acme Co.",
185
+ value: vendor,
186
+ onChange: (e) => setVendor(e.target.value),
187
+ ...vendorInput.agentProps
188
+ }
189
+ )
190
+ ] }),
191
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
192
+ /* @__PURE__ */ jsx(
193
+ "label",
194
+ {
195
+ className: "text-xs font-semibold text-muted-strong",
196
+ htmlFor: "product-type",
197
+ children: "Product type"
198
+ }
199
+ ),
200
+ /* @__PURE__ */ jsx(
201
+ Input,
202
+ {
203
+ ref: typeInput.ref,
204
+ id: "product-type",
205
+ placeholder: "e.g. Apparel",
206
+ value: productType,
207
+ onChange: (e) => setProductType(e.target.value),
208
+ ...typeInput.agentProps
209
+ }
210
+ )
211
+ ] }),
212
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
213
+ /* @__PURE__ */ jsx(
214
+ "label",
215
+ {
216
+ className: "text-xs font-semibold text-muted-strong",
217
+ htmlFor: "product-price",
218
+ children: "Base price"
219
+ }
220
+ ),
221
+ /* @__PURE__ */ jsx(
222
+ Input,
223
+ {
224
+ ref: priceInput.ref,
225
+ id: "product-price",
226
+ type: "number",
227
+ min: "0",
228
+ step: "0.01",
229
+ placeholder: "0.00",
230
+ value: price,
231
+ onChange: (e) => setPrice(e.target.value),
232
+ ...priceInput.agentProps
233
+ }
234
+ )
235
+ ] }),
236
+ submitError ? /* @__PURE__ */ jsx("div", { className: "px-1 py-2 text-xs text-danger", children: submitError }) : null,
237
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
238
+ /* @__PURE__ */ jsx(
239
+ Button,
240
+ {
241
+ ref: cancelButton.ref,
242
+ type: "button",
243
+ variant: "outline",
244
+ onClick: onClose,
245
+ ...cancelButton.agentProps,
246
+ children: "Cancel"
247
+ }
248
+ ),
249
+ /* @__PURE__ */ jsx(
250
+ Button,
251
+ {
252
+ ref: submitButton.ref,
253
+ type: "submit",
254
+ disabled: submitting || !title.trim(),
255
+ ...submitButton.agentProps,
256
+ children: submitting ? "Creating\u2026" : "Create product"
257
+ }
258
+ )
259
+ ] })
260
+ ] })
261
+ ] })
262
+ }
263
+ );
264
+ }
265
+ function ProductRow({ product }) {
266
+ const priceLabel = product.priceRange.min === product.priceRange.max ? product.priceRange.min : `${product.priceRange.min} \u2013 ${product.priceRange.max}`;
267
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-2 py-2 transition-colors hover:bg-bg-muted/20", children: [
268
+ /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden", children: product.imageUrl ? /* @__PURE__ */ jsx(
269
+ "img",
270
+ {
271
+ src: product.imageUrl,
272
+ alt: product.title,
273
+ className: "h-full w-full object-cover"
274
+ }
275
+ ) : /* @__PURE__ */ jsx(Image, { className: "h-4 w-4 text-muted/50" }) }),
276
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
277
+ /* @__PURE__ */ jsx("div", { className: "truncate text-sm font-semibold text-txt", children: product.title }),
278
+ /* @__PURE__ */ jsxs("div", { className: "mt-0.5 flex flex-wrap items-center gap-1.5 text-xs-tight text-muted", children: [
279
+ product.vendor ? /* @__PURE__ */ jsx("span", { children: product.vendor }) : null,
280
+ product.vendor && product.productType ? /* @__PURE__ */ jsx("span", { children: "\xB7" }) : null,
281
+ product.productType ? /* @__PURE__ */ jsx("span", { children: product.productType }) : null
282
+ ] })
283
+ ] }),
284
+ /* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right", children: [
285
+ /* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-txt", children: priceLabel }),
286
+ /* @__PURE__ */ jsxs(
287
+ "div",
288
+ {
289
+ className: "mt-0.5 flex items-center justify-end gap-1 text-xs-tight text-muted",
290
+ title: "Inventory",
291
+ children: [
292
+ /* @__PURE__ */ jsx(Package, { className: "h-3 w-3", "aria-hidden": true }),
293
+ product.totalInventory.toLocaleString()
294
+ ]
295
+ }
296
+ )
297
+ ] }),
298
+ /* @__PURE__ */ jsx("div", { className: "shrink-0", children: /* @__PURE__ */ jsx(ProductStatusBadge, { status: product.status }) })
299
+ ] });
300
+ }
301
+ const PAGE_SIZE = 20;
302
+ function ProductsPanel({
303
+ products,
304
+ total,
305
+ page,
306
+ loading,
307
+ error,
308
+ onPageChange
309
+ }) {
310
+ const [createOpen, setCreateOpen] = useState(false);
311
+ const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
312
+ const createButton = useAgentElement({
313
+ id: "action-create-product",
314
+ role: "button",
315
+ label: "Create product",
316
+ group: "products",
317
+ description: "Open the dialog to create a new product"
318
+ });
319
+ const prevPageButton = useAgentElement({
320
+ id: "products-page-prev",
321
+ role: "button",
322
+ label: "Previous products page",
323
+ group: "products",
324
+ description: "Go to the previous page of products",
325
+ onActivate: () => onPageChange(page - 1)
326
+ });
327
+ const nextPageButton = useAgentElement({
328
+ id: "products-page-next",
329
+ role: "button",
330
+ label: "Next products page",
331
+ group: "products",
332
+ description: "Go to the next page of products",
333
+ onActivate: () => onPageChange(page + 1)
334
+ });
335
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
336
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
337
+ /* @__PURE__ */ jsx(
338
+ "p",
339
+ {
340
+ "data-testid": "chat-search-hint",
341
+ className: "sr-only text-[13px] leading-relaxed text-txt/60",
342
+ children: "Search products by typing in the chat."
343
+ }
344
+ ),
345
+ /* @__PURE__ */ jsxs(
346
+ Button,
347
+ {
348
+ ref: createButton.ref,
349
+ type: "button",
350
+ size: "sm",
351
+ onClick: () => setCreateOpen(true),
352
+ className: "shrink-0 gap-1.5",
353
+ ...createButton.agentProps,
354
+ children: [
355
+ /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" }),
356
+ "Create"
357
+ ]
358
+ }
359
+ )
360
+ ] }),
361
+ error ? /* @__PURE__ */ jsx("div", { className: "px-1 py-2 text-sm text-danger", children: error }) : null,
362
+ loading && products.length === 0 ? /* @__PURE__ */ jsx("div", { className: "space-y-2", children: Array.from({ length: 6 }, (_, i) => i).map((i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-16 w-full" }, i)) }) : products.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-3 py-10 text-center", children: [
363
+ /* @__PURE__ */ jsx(Package, { className: "h-8 w-8 text-muted/40" }),
364
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-muted", children: "None" })
365
+ ] }) : /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: products.map((product) => /* @__PURE__ */ jsx(ProductRow, { product }, product.id)) }),
366
+ total > PAGE_SIZE ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-1", children: [
367
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted", children: [
368
+ total.toLocaleString(),
369
+ " products \xB7 page ",
370
+ page,
371
+ " of ",
372
+ totalPages
373
+ ] }),
374
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
375
+ /* @__PURE__ */ jsx(
376
+ Button,
377
+ {
378
+ ref: prevPageButton.ref,
379
+ type: "button",
380
+ variant: "outline",
381
+ size: "icon",
382
+ className: "h-8 w-8",
383
+ disabled: page <= 1 || loading,
384
+ onClick: () => onPageChange(page - 1),
385
+ "aria-label": "Previous page",
386
+ ...prevPageButton.agentProps,
387
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
388
+ }
389
+ ),
390
+ /* @__PURE__ */ jsx(
391
+ Button,
392
+ {
393
+ ref: nextPageButton.ref,
394
+ type: "button",
395
+ variant: "outline",
396
+ size: "icon",
397
+ className: "h-8 w-8",
398
+ disabled: page >= totalPages || loading,
399
+ onClick: () => onPageChange(page + 1),
400
+ "aria-label": "Next page",
401
+ ...nextPageButton.agentProps,
402
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
403
+ }
404
+ )
405
+ ] })
406
+ ] }) : null,
407
+ /* @__PURE__ */ jsx(
408
+ CreateProductDialog,
409
+ {
410
+ open: createOpen,
411
+ onClose: () => setCreateOpen(false)
412
+ }
413
+ )
414
+ ] });
415
+ }
416
+ export {
417
+ ProductsPanel
418
+ };
419
+ //# sourceMappingURL=ProductsPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ProductsPanel.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogContent,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n Input,\n Skeleton,\n} from \"@elizaos/ui\";\nimport { useAgentElement } from \"@elizaos/ui/agent-surface\";\nimport { ChevronLeft, ChevronRight, Image, Package, Plus } from \"lucide-react\";\nimport { useState } from \"react\";\nimport type { ShopifyProduct } from \"./useShopifyDashboard.js\";\n\nfunction ProductStatusBadge({ status }: { status: ShopifyProduct[\"status\"] }) {\n const styles = {\n ACTIVE: \"bg-ok\",\n DRAFT: \"bg-muted\",\n ARCHIVED: \"bg-danger\",\n } satisfies Record<ShopifyProduct[\"status\"], string>;\n\n const labels: Record<ShopifyProduct[\"status\"], string> = {\n ACTIVE: \"Active\",\n DRAFT: \"Draft\",\n ARCHIVED: \"Archived\",\n };\n\n return (\n <span\n role=\"img\"\n aria-label={labels[status]}\n title={labels[status]}\n className={`inline-flex h-2.5 w-2.5 rounded-full ${styles[status]}`}\n />\n );\n}\n\ninterface CreateProductDialogProps {\n open: boolean;\n onClose: () => void;\n}\n\nfunction CreateProductDialog({ open, onClose }: CreateProductDialogProps) {\n const [title, setTitle] = useState(\"\");\n const [vendor, setVendor] = useState(\"\");\n const [productType, setProductType] = useState(\"\");\n const [price, setPrice] = useState(\"\");\n const [submitting, setSubmitting] = useState(false);\n const [submitError, setSubmitError] = useState<string | null>(null);\n\n const titleInput = useAgentElement<HTMLInputElement>({\n id: \"create-product-title\",\n role: \"text-input\",\n label: \"Product title\",\n group: \"create-product\",\n description: \"Title for the new product (required)\",\n getValue: () => title,\n onFill: (value) => setTitle(value),\n });\n const vendorInput = useAgentElement<HTMLInputElement>({\n id: \"create-product-vendor\",\n role: \"text-input\",\n label: \"Product vendor\",\n group: \"create-product\",\n description: \"Vendor for the new product\",\n getValue: () => vendor,\n onFill: (value) => setVendor(value),\n });\n const typeInput = useAgentElement<HTMLInputElement>({\n id: \"create-product-type\",\n role: \"text-input\",\n label: \"Product type\",\n group: \"create-product\",\n description: \"Type/category for the new product\",\n getValue: () => productType,\n onFill: (value) => setProductType(value),\n });\n const priceInput = useAgentElement<HTMLInputElement>({\n id: \"create-product-price\",\n role: \"number-input\",\n label: \"Product base price\",\n group: \"create-product\",\n description: \"Base price for the new product\",\n getValue: () => price,\n onFill: (value) => setPrice(value),\n });\n const cancelButton = useAgentElement<HTMLButtonElement>({\n id: \"create-product-cancel\",\n role: \"button\",\n label: \"Cancel create product\",\n group: \"create-product\",\n description: \"Close the create-product dialog without saving\",\n onActivate: onClose,\n });\n const submitButton = useAgentElement<HTMLButtonElement>({\n id: \"create-product-submit\",\n role: \"button\",\n label: \"Submit new product\",\n group: \"create-product\",\n description: \"Create the new draft product\",\n });\n\n function reset() {\n setTitle(\"\");\n setVendor(\"\");\n setProductType(\"\");\n setPrice(\"\");\n setSubmitting(false);\n setSubmitError(null);\n }\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault();\n if (!title.trim()) return;\n setSubmitting(true);\n setSubmitError(null);\n try {\n const res = await fetch(\"/api/shopify/products\", {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n title: title.trim(),\n vendor: vendor.trim() || undefined,\n productType: productType.trim() || undefined,\n price: price.trim() || undefined,\n }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"Unknown error\");\n throw new Error(text);\n }\n reset();\n onClose();\n } catch (err) {\n setSubmitError(\n err instanceof Error ? err.message : \"Failed to create product.\",\n );\n } finally {\n setSubmitting(false);\n }\n }\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!next) {\n reset();\n onClose();\n }\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>Create product</DialogTitle>\n </DialogHeader>\n <form onSubmit={(e) => void handleSubmit(e)} className=\"space-y-3\">\n <div className=\"space-y-1.5\">\n <label\n className=\"text-xs font-semibold text-muted-strong\"\n htmlFor=\"product-title\"\n >\n Title <span className=\"text-danger\">*</span>\n </label>\n <Input\n ref={titleInput.ref}\n id=\"product-title\"\n placeholder=\"e.g. Classic T-Shirt\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n required\n {...titleInput.agentProps}\n />\n </div>\n <div className=\"space-y-1.5\">\n <label\n className=\"text-xs font-semibold text-muted-strong\"\n htmlFor=\"product-vendor\"\n >\n Vendor\n </label>\n <Input\n ref={vendorInput.ref}\n id=\"product-vendor\"\n placeholder=\"e.g. Acme Co.\"\n value={vendor}\n onChange={(e) => setVendor(e.target.value)}\n {...vendorInput.agentProps}\n />\n </div>\n <div className=\"space-y-1.5\">\n <label\n className=\"text-xs font-semibold text-muted-strong\"\n htmlFor=\"product-type\"\n >\n Product type\n </label>\n <Input\n ref={typeInput.ref}\n id=\"product-type\"\n placeholder=\"e.g. Apparel\"\n value={productType}\n onChange={(e) => setProductType(e.target.value)}\n {...typeInput.agentProps}\n />\n </div>\n <div className=\"space-y-1.5\">\n <label\n className=\"text-xs font-semibold text-muted-strong\"\n htmlFor=\"product-price\"\n >\n Base price\n </label>\n <Input\n ref={priceInput.ref}\n id=\"product-price\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n placeholder=\"0.00\"\n value={price}\n onChange={(e) => setPrice(e.target.value)}\n {...priceInput.agentProps}\n />\n </div>\n\n {submitError ? (\n <div className=\"px-1 py-2 text-xs text-danger\">{submitError}</div>\n ) : null}\n\n <DialogFooter>\n <Button\n ref={cancelButton.ref}\n type=\"button\"\n variant=\"outline\"\n onClick={onClose}\n {...cancelButton.agentProps}\n >\n Cancel\n </Button>\n <Button\n ref={submitButton.ref}\n type=\"submit\"\n disabled={submitting || !title.trim()}\n {...submitButton.agentProps}\n >\n {submitting ? \"Creating…\" : \"Create product\"}\n </Button>\n </DialogFooter>\n </form>\n </DialogContent>\n </Dialog>\n );\n}\n\nfunction ProductRow({ product }: { product: ShopifyProduct }) {\n const priceLabel =\n product.priceRange.min === product.priceRange.max\n ? product.priceRange.min\n : `${product.priceRange.min} – ${product.priceRange.max}`;\n\n return (\n <div className=\"flex items-center gap-3 px-2 py-2 transition-colors hover:bg-bg-muted/20\">\n <div className=\"flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden\">\n {product.imageUrl ? (\n <img\n src={product.imageUrl}\n alt={product.title}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <Image className=\"h-4 w-4 text-muted/50\" />\n )}\n </div>\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"truncate text-sm font-semibold text-txt\">\n {product.title}\n </div>\n <div className=\"mt-0.5 flex flex-wrap items-center gap-1.5 text-xs-tight text-muted\">\n {product.vendor ? <span>{product.vendor}</span> : null}\n {product.vendor && product.productType ? <span>·</span> : null}\n {product.productType ? <span>{product.productType}</span> : null}\n </div>\n </div>\n\n <div className=\"shrink-0 text-right\">\n <div className=\"text-sm font-semibold text-txt\">{priceLabel}</div>\n <div\n className=\"mt-0.5 flex items-center justify-end gap-1 text-xs-tight text-muted\"\n title=\"Inventory\"\n >\n <Package className=\"h-3 w-3\" aria-hidden />\n {product.totalInventory.toLocaleString()}\n </div>\n </div>\n\n <div className=\"shrink-0\">\n <ProductStatusBadge status={product.status} />\n </div>\n </div>\n );\n}\n\ninterface ProductsPanelProps {\n products: ShopifyProduct[];\n total: number;\n page: number;\n loading: boolean;\n error: string | null;\n search: string;\n onPageChange: (page: number) => void;\n}\n\nconst PAGE_SIZE = 20;\n\nexport function ProductsPanel({\n products,\n total,\n page,\n loading,\n error,\n onPageChange,\n}: ProductsPanelProps) {\n const [createOpen, setCreateOpen] = useState(false);\n const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));\n\n const createButton = useAgentElement<HTMLButtonElement>({\n id: \"action-create-product\",\n role: \"button\",\n label: \"Create product\",\n group: \"products\",\n description: \"Open the dialog to create a new product\",\n });\n const prevPageButton = useAgentElement<HTMLButtonElement>({\n id: \"products-page-prev\",\n role: \"button\",\n label: \"Previous products page\",\n group: \"products\",\n description: \"Go to the previous page of products\",\n onActivate: () => onPageChange(page - 1),\n });\n const nextPageButton = useAgentElement<HTMLButtonElement>({\n id: \"products-page-next\",\n role: \"button\",\n label: \"Next products page\",\n group: \"products\",\n description: \"Go to the next page of products\",\n onActivate: () => onPageChange(page + 1),\n });\n\n return (\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-2\">\n <p\n data-testid=\"chat-search-hint\"\n className=\"sr-only text-[13px] leading-relaxed text-txt/60\"\n >\n Search products by typing in the chat.\n </p>\n <Button\n ref={createButton.ref}\n type=\"button\"\n size=\"sm\"\n onClick={() => setCreateOpen(true)}\n className=\"shrink-0 gap-1.5\"\n {...createButton.agentProps}\n >\n <Plus className=\"h-4 w-4\" />\n Create\n </Button>\n </div>\n\n {error ? (\n <div className=\"px-1 py-2 text-sm text-danger\">{error}</div>\n ) : null}\n\n {loading && products.length === 0 ? (\n <div className=\"space-y-2\">\n {Array.from({ length: 6 }, (_, i) => i).map((i) => (\n <Skeleton key={i} className=\"h-16 w-full\" />\n ))}\n </div>\n ) : products.length === 0 ? (\n <div className=\"flex flex-col items-center gap-3 py-10 text-center\">\n <Package className=\"h-8 w-8 text-muted/40\" />\n <div className=\"text-sm text-muted\">None</div>\n </div>\n ) : (\n <div className=\"space-y-1.5\">\n {products.map((product) => (\n <ProductRow key={product.id} product={product} />\n ))}\n </div>\n )}\n\n {total > PAGE_SIZE ? (\n <div className=\"flex items-center justify-between pt-1\">\n <span className=\"text-xs text-muted\">\n {total.toLocaleString()} products · page {page} of {totalPages}\n </span>\n <div className=\"flex items-center gap-1.5\">\n <Button\n ref={prevPageButton.ref}\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8\"\n disabled={page <= 1 || loading}\n onClick={() => onPageChange(page - 1)}\n aria-label=\"Previous page\"\n {...prevPageButton.agentProps}\n >\n <ChevronLeft className=\"h-4 w-4\" />\n </Button>\n <Button\n ref={nextPageButton.ref}\n type=\"button\"\n variant=\"outline\"\n size=\"icon\"\n className=\"h-8 w-8\"\n disabled={page >= totalPages || loading}\n onClick={() => onPageChange(page + 1)}\n aria-label=\"Next page\"\n {...nextPageButton.agentProps}\n >\n <ChevronRight className=\"h-4 w-4\" />\n </Button>\n </div>\n </div>\n ) : null}\n\n <CreateProductDialog\n open={createOpen}\n onClose={() => setCreateOpen(false)}\n />\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,3 @@
1
+ import type { OverlayAppContext } from "@elizaos/ui";
2
+ export declare function ShopifyAppView({ exitToApps }: OverlayAppContext): import("react/jsx-runtime").JSX.Element;
3
+ //# sourceMappingURL=ShopifyAppView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ShopifyAppView.d.ts","sourceRoot":"","sources":["../src/ShopifyAppView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AA6VrD,wBAAgB,cAAc,CAAC,EAAE,UAAU,EAAE,EAAE,iBAAiB,2CA2V/D"}
@@ -0,0 +1,11 @@
1
+ import type { ShopifyCustomersResponse, ShopifyInventoryResponse, ShopifyOrdersResponse, ShopifyProductsResponse, ShopifyStatus } from "./useShopifyDashboard";
2
+ export declare function fetchShopifyTuiJson<T>(url: string): Promise<T | null>;
3
+ export declare function postShopifyTuiJson(url: string, body: Record<string, unknown>): Promise<unknown>;
4
+ export declare function loadShopifyTuiState(): Promise<{
5
+ status: ShopifyStatus;
6
+ products: ShopifyProductsResponse | null;
7
+ orders: ShopifyOrdersResponse | null;
8
+ inventory: ShopifyInventoryResponse | null;
9
+ customers: ShopifyCustomersResponse | null;
10
+ }>;
11
+ //# sourceMappingURL=ShopifyAppView.helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ShopifyAppView.helpers.d.ts","sourceRoot":"","sources":["../src/ShopifyAppView.helpers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,aAAa,EACd,MAAM,uBAAuB,CAAC;AAE/B,wBAAsB,mBAAmB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAY3E;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,OAAO,CAAC,CAelB;AAED,wBAAsB,mBAAmB,IAAI,OAAO,CAAC;IACnD,MAAM,EAAE,aAAa,CAAC;IACtB,QAAQ,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACzC,MAAM,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACrC,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC3C,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAAC;CAC5C,CAAC,CAgCD"}
@@ -0,0 +1,59 @@
1
+ async function fetchShopifyTuiJson(url) {
2
+ const response = await fetch(url);
3
+ if (response.status === 404) return null;
4
+ const data = await response.json().catch(() => null);
5
+ if (!response.ok) {
6
+ const message = data && typeof data === "object" && "error" in data ? String(data.error) : `Shopify request failed with ${response.status}`;
7
+ throw new Error(message);
8
+ }
9
+ return data;
10
+ }
11
+ async function postShopifyTuiJson(url, body) {
12
+ const response = await fetch(url, {
13
+ method: "POST",
14
+ headers: { "Content-Type": "application/json" },
15
+ body: JSON.stringify(body)
16
+ });
17
+ const data = await response.json().catch(() => ({}));
18
+ if (!response.ok) {
19
+ const message = data && typeof data === "object" && "error" in data ? String(data.error) : `Shopify request failed with ${response.status}`;
20
+ throw new Error(message);
21
+ }
22
+ return data;
23
+ }
24
+ async function loadShopifyTuiState() {
25
+ const status = await fetchShopifyTuiJson(
26
+ "/api/shopify/status"
27
+ ) ?? {
28
+ connected: false,
29
+ shop: null
30
+ };
31
+ if (!status.connected) {
32
+ return {
33
+ status,
34
+ products: null,
35
+ orders: null,
36
+ inventory: null,
37
+ customers: null
38
+ };
39
+ }
40
+ const [products, orders, inventory, customers] = await Promise.all([
41
+ fetchShopifyTuiJson(
42
+ "/api/shopify/products?page=1&limit=10&q="
43
+ ),
44
+ fetchShopifyTuiJson(
45
+ "/api/shopify/orders?status=any&limit=10"
46
+ ),
47
+ fetchShopifyTuiJson("/api/shopify/inventory"),
48
+ fetchShopifyTuiJson(
49
+ "/api/shopify/customers?q=&limit=10"
50
+ )
51
+ ]);
52
+ return { status, products, orders, inventory, customers };
53
+ }
54
+ export {
55
+ fetchShopifyTuiJson,
56
+ loadShopifyTuiState,
57
+ postShopifyTuiJson
58
+ };
59
+ //# sourceMappingURL=ShopifyAppView.helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ShopifyAppView.helpers.ts"],"sourcesContent":["// Shared fetch helpers for the Shopify view, used by the `interact` capability\n// handler (in ShopifyAppView.interact.ts). Kept out of the .tsx so that file\n// exports only React components and stays Fast-Refresh-compatible in dev.\nimport type {\n ShopifyCustomersResponse,\n ShopifyInventoryResponse,\n ShopifyOrdersResponse,\n ShopifyProductsResponse,\n ShopifyStatus,\n} from \"./useShopifyDashboard.js\";\n\nexport async function fetchShopifyTuiJson<T>(url: string): Promise<T | null> {\n const response = await fetch(url);\n if (response.status === 404) return null;\n const data = await response.json().catch(() => null);\n if (!response.ok) {\n const message =\n data && typeof data === \"object\" && \"error\" in data\n ? String(data.error)\n : `Shopify request failed with ${response.status}`;\n throw new Error(message);\n }\n return data as T;\n}\n\nexport async function postShopifyTuiJson(\n url: string,\n body: Record<string, unknown>,\n): Promise<unknown> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n const data = await response.json().catch(() => ({}));\n if (!response.ok) {\n const message =\n data && typeof data === \"object\" && \"error\" in data\n ? String(data.error)\n : `Shopify request failed with ${response.status}`;\n throw new Error(message);\n }\n return data;\n}\n\nexport async function loadShopifyTuiState(): Promise<{\n status: ShopifyStatus;\n products: ShopifyProductsResponse | null;\n orders: ShopifyOrdersResponse | null;\n inventory: ShopifyInventoryResponse | null;\n customers: ShopifyCustomersResponse | null;\n}> {\n const status = (await fetchShopifyTuiJson<ShopifyStatus>(\n \"/api/shopify/status\",\n )) ?? {\n connected: false,\n shop: null,\n };\n\n if (!status.connected) {\n return {\n status,\n products: null,\n orders: null,\n inventory: null,\n customers: null,\n };\n }\n\n const [products, orders, inventory, customers] = await Promise.all([\n fetchShopifyTuiJson<ShopifyProductsResponse>(\n \"/api/shopify/products?page=1&limit=10&q=\",\n ),\n fetchShopifyTuiJson<ShopifyOrdersResponse>(\n \"/api/shopify/orders?status=any&limit=10\",\n ),\n fetchShopifyTuiJson<ShopifyInventoryResponse>(\"/api/shopify/inventory\"),\n fetchShopifyTuiJson<ShopifyCustomersResponse>(\n \"/api/shopify/customers?q=&limit=10\",\n ),\n ]);\n\n return { status, products, orders, inventory, customers };\n}\n"],"mappings":"AAWA,eAAsB,oBAAuB,KAAgC;AAC3E,QAAM,WAAW,MAAM,MAAM,GAAG;AAChC,MAAI,SAAS,WAAW,IAAK,QAAO;AACpC,QAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI;AACnD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,UACJ,QAAQ,OAAO,SAAS,YAAY,WAAW,OAC3C,OAAO,KAAK,KAAK,IACjB,+BAA+B,SAAS,MAAM;AACpD,UAAM,IAAI,MAAM,OAAO;AAAA,EACzB;AACA,SAAO;AACT;AAEA,eAAsB,mBACpB,KACA,MACkB;AAClB,QAAM,WAAW,MAAM,MAAM,KAAK;AAAA,IAChC,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,EAC3B,CAAC;AACD,QAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACnD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,UACJ,QAAQ,OAAO,SAAS,YAAY,WAAW,OAC3C,OAAO,KAAK,KAAK,IACjB,+BAA+B,SAAS,MAAM;AACpD,UAAM,IAAI,MAAM,OAAO;AAAA,EACzB;AACA,SAAO;AACT;AAEA,eAAsB,sBAMnB;AACD,QAAM,SAAU,MAAM;AAAA,IACpB;AAAA,EACF,KAAM;AAAA,IACJ,WAAW;AAAA,IACX,MAAM;AAAA,EACR;AAEA,MAAI,CAAC,OAAO,WAAW;AACrB,WAAO;AAAA,MACL;AAAA,MACA,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,EACF;AAEA,QAAM,CAAC,UAAU,QAAQ,WAAW,SAAS,IAAI,MAAM,QAAQ,IAAI;AAAA,IACjE;AAAA,MACE;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,IACF;AAAA,IACA,oBAA8C,wBAAwB;AAAA,IACtE;AAAA,MACE;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO,EAAE,QAAQ,UAAU,QAAQ,WAAW,UAAU;AAC1D;","names":[]}
@@ -0,0 +1,2 @@
1
+ export declare function interact(capability: string, params?: Record<string, unknown>): Promise<unknown>;
2
+ //# sourceMappingURL=ShopifyAppView.interact.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ShopifyAppView.interact.d.ts","sourceRoot":"","sources":["../src/ShopifyAppView.interact.ts"],"names":[],"mappings":"AAgBA,wBAAsB,QAAQ,CAC5B,UAAU,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAAC,OAAO,CAAC,CAqGlB"}
@@ -0,0 +1,93 @@
1
+ import {
2
+ fetchShopifyTuiJson,
3
+ loadShopifyTuiState,
4
+ postShopifyTuiJson
5
+ } from "./ShopifyAppView.helpers";
6
+ async function interact(capability, params) {
7
+ if (capability === "terminal-shopify-state") {
8
+ return { viewType: "tui", ...await loadShopifyTuiState() };
9
+ }
10
+ if (capability === "terminal-shopify-products") {
11
+ const query = typeof params?.query === "string" ? params.query.trim() : "";
12
+ const page = typeof params?.page === "number" ? params.page : 1;
13
+ const limit = typeof params?.limit === "number" ? params.limit : 20;
14
+ return {
15
+ viewType: "tui",
16
+ products: await fetchShopifyTuiJson(
17
+ `/api/shopify/products?${new URLSearchParams({
18
+ page: String(page),
19
+ limit: String(limit),
20
+ q: query
21
+ })}`
22
+ )
23
+ };
24
+ }
25
+ if (capability === "terminal-shopify-orders") {
26
+ const status = typeof params?.status === "string" ? params.status.trim() : "any";
27
+ const limit = typeof params?.limit === "number" ? params.limit : 20;
28
+ return {
29
+ viewType: "tui",
30
+ orders: await fetchShopifyTuiJson(
31
+ `/api/shopify/orders?${new URLSearchParams({
32
+ status,
33
+ limit: String(limit)
34
+ })}`
35
+ )
36
+ };
37
+ }
38
+ if (capability === "terminal-shopify-inventory") {
39
+ return {
40
+ viewType: "tui",
41
+ inventory: await fetchShopifyTuiJson(
42
+ "/api/shopify/inventory"
43
+ )
44
+ };
45
+ }
46
+ if (capability === "terminal-shopify-customers") {
47
+ const query = typeof params?.query === "string" ? params.query.trim() : "";
48
+ const limit = typeof params?.limit === "number" ? params.limit : 20;
49
+ return {
50
+ viewType: "tui",
51
+ customers: await fetchShopifyTuiJson(
52
+ `/api/shopify/customers?${new URLSearchParams({
53
+ q: query,
54
+ limit: String(limit)
55
+ })}`
56
+ )
57
+ };
58
+ }
59
+ if (capability === "terminal-shopify-create-product") {
60
+ const title = typeof params?.title === "string" ? params.title.trim() : "";
61
+ if (!title) throw new Error("title is required");
62
+ return {
63
+ viewType: "tui",
64
+ product: await postShopifyTuiJson("/api/shopify/products", {
65
+ title,
66
+ vendor: typeof params?.vendor === "string" ? params.vendor : void 0,
67
+ productType: typeof params?.productType === "string" ? params.productType : void 0,
68
+ price: typeof params?.price === "string" || typeof params?.price === "number" ? params.price : void 0
69
+ })
70
+ };
71
+ }
72
+ if (capability === "terminal-shopify-adjust-inventory") {
73
+ const itemId = typeof params?.itemId === "string" ? params.itemId.trim() : "";
74
+ const delta = typeof params?.delta === "number" ? params.delta : null;
75
+ if (!itemId) throw new Error("itemId is required");
76
+ if (delta === null) throw new Error("delta is required");
77
+ return {
78
+ viewType: "tui",
79
+ inventory: await postShopifyTuiJson(
80
+ `/api/shopify/inventory/${encodeURIComponent(itemId)}/adjust`,
81
+ {
82
+ delta,
83
+ locationId: typeof params?.locationId === "string" ? params.locationId : void 0
84
+ }
85
+ )
86
+ };
87
+ }
88
+ throw new Error(`Unsupported capability "${capability}"`);
89
+ }
90
+ export {
91
+ interact
92
+ };
93
+ //# sourceMappingURL=ShopifyAppView.interact.js.map