medusa-plugin-veeqo 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/medusa-config.js +29 -0
- package/.medusa/server/src/admin/index.js +686 -0
- package/.medusa/server/src/admin/index.mjs +685 -0
- package/.medusa/server/src/api/admin/veeqo/customers/[customerId]/sync/route.js +19 -0
- package/.medusa/server/src/api/admin/veeqo/customers/sync/route.js +16 -0
- package/.medusa/server/src/api/admin/veeqo/orders/[orderId]/sync/route.js +19 -0
- package/.medusa/server/src/api/admin/veeqo/orders/shipment-sync/route.js +30 -0
- package/.medusa/server/src/api/admin/veeqo/orders/sync/route.js +16 -0
- package/.medusa/server/src/api/admin/veeqo/products/[productId]/sync/route.js +19 -0
- package/.medusa/server/src/api/admin/veeqo/products/sync/route.js +14 -0
- package/.medusa/server/src/api/admin/veeqo/sales-channels/[salesChannelId]/sync/route.js +19 -0
- package/.medusa/server/src/api/admin/veeqo/sales-channels/sync/route.js +14 -0
- package/.medusa/server/src/api/admin/veeqo/shipments/[id]/tracking-events/route.js +20 -0
- package/.medusa/server/src/api/admin/veeqo/shipping-options/[shippingOptionId]/sync/route.js +19 -0
- package/.medusa/server/src/api/admin/veeqo/shipping-options/sync/route.js +14 -0
- package/.medusa/server/src/api/admin/veeqo/stock-locations/[stockLocationId]/sync/route.js +19 -0
- package/.medusa/server/src/api/admin/veeqo/stock-locations/sync/route.js +14 -0
- package/.medusa/server/src/api/middlewares.js +39 -0
- package/.medusa/server/src/api/validators.js +23 -0
- package/.medusa/server/src/jobs/veeqo-order-sync.js +34 -0
- package/.medusa/server/src/links/customer-veeqo-customer.js +18 -0
- package/.medusa/server/src/links/fulfillment-veeqo-shipment.js +18 -0
- package/.medusa/server/src/links/order-veeqo-order.js +18 -0
- package/.medusa/server/src/links/product-variant-veeqo-sellable.js +18 -0
- package/.medusa/server/src/links/product-veeqo-product.js +18 -0
- package/.medusa/server/src/links/sales-channel-veeqo-channel.js +18 -0
- package/.medusa/server/src/links/shipping-option-veeqo-delivery-method.js +18 -0
- package/.medusa/server/src/links/stock-location-veeqo-warehouse.js +18 -0
- package/.medusa/server/src/links/veeqo-channel-sales-channel.js +15 -0
- package/.medusa/server/src/links/veeqo-customer-customer.js +15 -0
- package/.medusa/server/src/links/veeqo-delivery-method.js +15 -0
- package/.medusa/server/src/links/veeqo-order-order.js +15 -0
- package/.medusa/server/src/links/veeqo-product-product.js +15 -0
- package/.medusa/server/src/links/veeqo-sellable-product-variant.js +15 -0
- package/.medusa/server/src/links/veeqo-shipment-fulfillment.js +15 -0
- package/.medusa/server/src/links/veeqo-warehouse-stock-location.js +15 -0
- package/.medusa/server/src/modules/veeqo/index.js +10 -0
- package/.medusa/server/src/modules/veeqo/migrations/Migration20260411222036.js +76 -0
- package/.medusa/server/src/modules/veeqo/models/veeqo-channel.js +10 -0
- package/.medusa/server/src/modules/veeqo/models/veeqo-customer.js +14 -0
- package/.medusa/server/src/modules/veeqo/models/veeqo-delivery-method.js +10 -0
- package/.medusa/server/src/modules/veeqo/models/veeqo-order.js +26 -0
- package/.medusa/server/src/modules/veeqo/models/veeqo-product.js +10 -0
- package/.medusa/server/src/modules/veeqo/models/veeqo-sellable.js +10 -0
- package/.medusa/server/src/modules/veeqo/models/veeqo-shipment.js +20 -0
- package/.medusa/server/src/modules/veeqo/models/veeqo-warehouse.js +10 -0
- package/.medusa/server/src/modules/veeqo/service.js +566 -0
- package/.medusa/server/src/modules/veeqo/types.js +3 -0
- package/.medusa/server/src/subscribers/veeqo-dispatcher.js +12 -0
- package/.medusa/server/src/workflows/channel.js +137 -0
- package/.medusa/server/src/workflows/customer.js +112 -0
- package/.medusa/server/src/workflows/delivery-method.js +116 -0
- package/.medusa/server/src/workflows/order.js +327 -0
- package/.medusa/server/src/workflows/product.js +163 -0
- package/.medusa/server/src/workflows/shipments.js +155 -0
- package/.medusa/server/src/workflows/warehouse.js +133 -0
- package/README.md +70 -0
- package/package.json +83 -0
|
@@ -0,0 +1,686 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const jsxRuntime = require("react/jsx-runtime");
|
|
3
|
+
const ui = require("@medusajs/ui");
|
|
4
|
+
const adminSdk = require("@medusajs/admin-sdk");
|
|
5
|
+
const reactQuery = require("@tanstack/react-query");
|
|
6
|
+
const Medusa = require("@medusajs/js-sdk");
|
|
7
|
+
const reactRouterDom = require("react-router-dom");
|
|
8
|
+
const react = require("react");
|
|
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
|
+
adminSdk.defineWidgetConfig({
|
|
19
|
+
zone: "customer.details.side.after"
|
|
20
|
+
});
|
|
21
|
+
const CustomerVeeqoWidget = ({ data: customer }) => {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
const queryClient = reactQuery.useQueryClient();
|
|
24
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
25
|
+
queryFn: () => sdk.client.fetch(`/admin/customers/${customer.id}`, {
|
|
26
|
+
query: {
|
|
27
|
+
fields: "veeqo_customer.veeqo_customer_id"
|
|
28
|
+
}
|
|
29
|
+
}),
|
|
30
|
+
queryKey: ["customer", customer.id]
|
|
31
|
+
});
|
|
32
|
+
const syncMutation = reactQuery.useMutation({
|
|
33
|
+
mutationFn: () => sdk.client.fetch(`/admin/veeqo/customers/${customer.id}/sync`, { method: "POST" }),
|
|
34
|
+
onSuccess: () => {
|
|
35
|
+
queryClient.invalidateQueries({ queryKey: ["customer", customer.id] });
|
|
36
|
+
ui.toast.success("Customer synced successfully");
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
const veeqoCustomerId = ((_b = (_a = data == null ? void 0 : data.customer) == null ? void 0 : _a.veeqo_customer) == null ? void 0 : _b.veeqo_customer_id) || "NOT SYNCED";
|
|
40
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
41
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
42
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Veeqo Customer" }),
|
|
43
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
44
|
+
ui.Button,
|
|
45
|
+
{
|
|
46
|
+
size: "small",
|
|
47
|
+
variant: "secondary",
|
|
48
|
+
onClick: () => syncMutation.mutate(),
|
|
49
|
+
disabled: syncMutation.isPending,
|
|
50
|
+
children: syncMutation.isPending ? "Syncing..." : "Sync"
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
] }),
|
|
54
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: isLoading ? "Loading..." : "ID: " + veeqoCustomerId }) })
|
|
55
|
+
] });
|
|
56
|
+
};
|
|
57
|
+
adminSdk.defineWidgetConfig({
|
|
58
|
+
zone: "order.details.side.after"
|
|
59
|
+
});
|
|
60
|
+
const OrderVeeqoWidget = ({ data: order }) => {
|
|
61
|
+
var _a, _b;
|
|
62
|
+
const queryClient = reactQuery.useQueryClient();
|
|
63
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
64
|
+
queryFn: () => sdk.client.fetch(`/admin/orders/${order.id}`, {
|
|
65
|
+
query: {
|
|
66
|
+
fields: "veeqo_order.veeqo_order_id"
|
|
67
|
+
}
|
|
68
|
+
}),
|
|
69
|
+
queryKey: ["order", order.id]
|
|
70
|
+
});
|
|
71
|
+
const syncMutation = reactQuery.useMutation({
|
|
72
|
+
mutationFn: () => sdk.client.fetch(`/admin/veeqo/orders/${order.id}/sync`, { method: "POST" }),
|
|
73
|
+
onSuccess: () => {
|
|
74
|
+
queryClient.invalidateQueries({ queryKey: ["order", order.id] });
|
|
75
|
+
ui.toast.success("Order synced successfully");
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
const veeqoOrderId = ((_b = (_a = data == null ? void 0 : data.order) == null ? void 0 : _a.veeqo_order) == null ? void 0 : _b.veeqo_order_id) || "NOT SYNCED";
|
|
79
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
80
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
81
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Veeqo Order" }),
|
|
82
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
83
|
+
ui.Button,
|
|
84
|
+
{
|
|
85
|
+
size: "small",
|
|
86
|
+
variant: "secondary",
|
|
87
|
+
onClick: () => syncMutation.mutate(),
|
|
88
|
+
disabled: syncMutation.isPending,
|
|
89
|
+
children: syncMutation.isPending ? "Syncing..." : "Sync"
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
] }),
|
|
93
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: isLoading ? "Loading..." : /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `https://app.veeqo.com/orders/${veeqoOrderId}`, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", children: [
|
|
94
|
+
"ID: ",
|
|
95
|
+
veeqoOrderId
|
|
96
|
+
] }) }) })
|
|
97
|
+
] });
|
|
98
|
+
};
|
|
99
|
+
adminSdk.defineWidgetConfig({
|
|
100
|
+
zone: "product_variant.details.side.after"
|
|
101
|
+
});
|
|
102
|
+
const ProductVariantVeeqoWidget = ({ data: variant }) => {
|
|
103
|
+
var _a, _b;
|
|
104
|
+
const queryClient = reactQuery.useQueryClient();
|
|
105
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
106
|
+
queryFn: () => sdk.client.fetch(`/admin/products/${variant.product_id}/variants/${variant.id}`, {
|
|
107
|
+
query: {
|
|
108
|
+
fields: "veeqo_sellable.*"
|
|
109
|
+
}
|
|
110
|
+
}),
|
|
111
|
+
queryKey: ["product-variant", variant.id]
|
|
112
|
+
});
|
|
113
|
+
const syncMutation = reactQuery.useMutation({
|
|
114
|
+
mutationFn: () => sdk.client.fetch(`/admin/veeqo/products/${variant.product_id}/sync`, { method: "POST" }),
|
|
115
|
+
onSuccess: () => {
|
|
116
|
+
queryClient.invalidateQueries({ queryKey: ["product-variant", variant.id] });
|
|
117
|
+
ui.toast.success("Product synced successfully");
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
const veeqoSellableId = ((_b = (_a = data == null ? void 0 : data.variant) == null ? void 0 : _a.veeqo_sellable) == null ? void 0 : _b.veeqo_sellable_id) || "NOT SYNCED";
|
|
121
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
122
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
123
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Veeqo Sellable" }),
|
|
124
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
125
|
+
ui.Button,
|
|
126
|
+
{
|
|
127
|
+
size: "small",
|
|
128
|
+
variant: "secondary",
|
|
129
|
+
onClick: () => syncMutation.mutate(),
|
|
130
|
+
disabled: syncMutation.isPending,
|
|
131
|
+
children: syncMutation.isPending ? "Syncing..." : "Sync"
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
] }),
|
|
135
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `https://app.veeqo.com/inventory/variants/${veeqoSellableId}`, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: isLoading ? "Loading..." : "ID: " + veeqoSellableId }) }) })
|
|
136
|
+
] });
|
|
137
|
+
};
|
|
138
|
+
adminSdk.defineWidgetConfig({
|
|
139
|
+
zone: "product.details.side.after"
|
|
140
|
+
});
|
|
141
|
+
const ProductVeeqoWidget = ({ data: product }) => {
|
|
142
|
+
var _a, _b, _c, _d;
|
|
143
|
+
const queryClient = reactQuery.useQueryClient();
|
|
144
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
145
|
+
queryFn: () => sdk.client.fetch(`/admin/products/${product.id}`, {
|
|
146
|
+
query: {
|
|
147
|
+
fields: "veeqo_product.veeqo_product_id,variants.id,variants.veeqo_sellable.veeqo_sellable_id"
|
|
148
|
+
}
|
|
149
|
+
}),
|
|
150
|
+
queryKey: ["product", product.id]
|
|
151
|
+
});
|
|
152
|
+
const syncMutation = reactQuery.useMutation({
|
|
153
|
+
mutationFn: () => sdk.client.fetch(`/admin/veeqo/products/${product.id}/sync`, { method: "POST" }),
|
|
154
|
+
onSuccess: () => {
|
|
155
|
+
queryClient.invalidateQueries({ queryKey: ["product", product.id] });
|
|
156
|
+
ui.toast.success("Product synced successfully");
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
const veeqoProductId = ((_b = (_a = data == null ? void 0 : data.product) == null ? void 0 : _a.veeqo_product) == null ? void 0 : _b.veeqo_product_id) || "NOT SYNCED";
|
|
160
|
+
const defaultVariantId = ((_c = data == null ? void 0 : data.product) == null ? void 0 : _c.variants) && data.product.variants.length === 1 ? (_d = data.product.variants[0].veeqo_sellable) == null ? void 0 : _d.veeqo_sellable_id : null;
|
|
161
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
162
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
163
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Veeqo Product" }),
|
|
164
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
165
|
+
ui.Button,
|
|
166
|
+
{
|
|
167
|
+
size: "small",
|
|
168
|
+
variant: "secondary",
|
|
169
|
+
onClick: () => syncMutation.mutate(),
|
|
170
|
+
disabled: syncMutation.isPending,
|
|
171
|
+
children: syncMutation.isPending ? "Syncing..." : "Sync"
|
|
172
|
+
}
|
|
173
|
+
)
|
|
174
|
+
] }),
|
|
175
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4", children: [
|
|
176
|
+
defaultVariantId && /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `https://app.veeqo.com/inventory/variants/${defaultVariantId}`, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: isLoading ? "Loading..." : "ID: " + veeqoProductId }) }),
|
|
177
|
+
!defaultVariantId && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: isLoading ? "Loading..." : "ID: " + veeqoProductId })
|
|
178
|
+
] })
|
|
179
|
+
] });
|
|
180
|
+
};
|
|
181
|
+
adminSdk.defineWidgetConfig({
|
|
182
|
+
zone: "sales_channel.details.after"
|
|
183
|
+
});
|
|
184
|
+
const SalesChannelVeeqoWidget = ({ data: sales_channel }) => {
|
|
185
|
+
var _a, _b;
|
|
186
|
+
const queryClient = reactQuery.useQueryClient();
|
|
187
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
188
|
+
queryFn: () => sdk.client.fetch(`/admin/sales-channels/${sales_channel.id}`, {
|
|
189
|
+
query: {
|
|
190
|
+
fields: "veeqo_channel.veeqo_channel_id"
|
|
191
|
+
}
|
|
192
|
+
}),
|
|
193
|
+
queryKey: ["sales-channel", sales_channel.id]
|
|
194
|
+
});
|
|
195
|
+
const syncMutation = reactQuery.useMutation({
|
|
196
|
+
mutationFn: () => sdk.client.fetch(`/admin/veeqo/sales-channels/${sales_channel.id}/sync`, {
|
|
197
|
+
method: "POST"
|
|
198
|
+
}),
|
|
199
|
+
onSuccess: () => {
|
|
200
|
+
queryClient.invalidateQueries({ queryKey: ["sales-channel", sales_channel.id] });
|
|
201
|
+
ui.toast.success("Sales channel synced successfully");
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
const veeqoSalesChannelId = ((_b = (_a = data == null ? void 0 : data.sales_channel) == null ? void 0 : _a.veeqo_channel) == null ? void 0 : _b.veeqo_channel_id) || "NOT SYNCED";
|
|
205
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
206
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
207
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Veeqo Sales Channel" }),
|
|
208
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
209
|
+
ui.Button,
|
|
210
|
+
{
|
|
211
|
+
size: "small",
|
|
212
|
+
variant: "secondary",
|
|
213
|
+
onClick: () => syncMutation.mutate(),
|
|
214
|
+
disabled: syncMutation.isPending,
|
|
215
|
+
children: syncMutation.isPending ? "Syncing..." : "Sync"
|
|
216
|
+
}
|
|
217
|
+
)
|
|
218
|
+
] }),
|
|
219
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: isLoading ? "Loading..." : /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", children: [
|
|
220
|
+
"ID: ",
|
|
221
|
+
veeqoSalesChannelId
|
|
222
|
+
] }) })
|
|
223
|
+
] });
|
|
224
|
+
};
|
|
225
|
+
adminSdk.defineWidgetConfig({
|
|
226
|
+
zone: "location.details.side.after"
|
|
227
|
+
});
|
|
228
|
+
const StockLocationVeeqoWidget = ({
|
|
229
|
+
data: stock_location
|
|
230
|
+
}) => {
|
|
231
|
+
var _a, _b;
|
|
232
|
+
const queryClient = reactQuery.useQueryClient();
|
|
233
|
+
const { data, isLoading } = reactQuery.useQuery({
|
|
234
|
+
queryFn: () => sdk.client.fetch(`/admin/stock-locations/${stock_location.id}`, {
|
|
235
|
+
query: {
|
|
236
|
+
fields: "veeqo_warehouse.veeqo_warehouse_id"
|
|
237
|
+
}
|
|
238
|
+
}),
|
|
239
|
+
queryKey: ["stock-location", stock_location.id]
|
|
240
|
+
});
|
|
241
|
+
const syncMutation = reactQuery.useMutation({
|
|
242
|
+
mutationFn: () => sdk.client.fetch(`/admin/veeqo/stock-locations/${stock_location.id}/sync`, {
|
|
243
|
+
method: "POST"
|
|
244
|
+
}),
|
|
245
|
+
onSuccess: () => {
|
|
246
|
+
queryClient.invalidateQueries({ queryKey: ["stock-location", stock_location.id] });
|
|
247
|
+
ui.toast.success("Stock location synced successfully");
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
const veeqoStockLocationId = ((_b = (_a = data == null ? void 0 : data.stock_location) == null ? void 0 : _a.veeqo_warehouse) == null ? void 0 : _b.veeqo_warehouse_id) || "NOT SYNCED";
|
|
251
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
252
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
253
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Veeqo Stock Location" }),
|
|
254
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
255
|
+
ui.Button,
|
|
256
|
+
{
|
|
257
|
+
size: "small",
|
|
258
|
+
variant: "secondary",
|
|
259
|
+
onClick: () => syncMutation.mutate(),
|
|
260
|
+
disabled: syncMutation.isPending,
|
|
261
|
+
children: syncMutation.isPending ? "Syncing..." : "Sync"
|
|
262
|
+
}
|
|
263
|
+
)
|
|
264
|
+
] }),
|
|
265
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: isLoading ? "Loading..." : /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", children: [
|
|
266
|
+
"ID: ",
|
|
267
|
+
veeqoStockLocationId
|
|
268
|
+
] }) })
|
|
269
|
+
] });
|
|
270
|
+
};
|
|
271
|
+
const useVeeqoSalesChannels = (params) => reactQuery.useQuery({
|
|
272
|
+
queryFn: () => sdk.client.fetch("/admin/sales-channels", {
|
|
273
|
+
query: { fields: "id,name,description,veeqo_channel.veeqo_channel_id", ...params }
|
|
274
|
+
}),
|
|
275
|
+
queryKey: ["sales_channels", params.limit, params.offset]
|
|
276
|
+
});
|
|
277
|
+
const useSyncVeeqoSalesChannels = () => {
|
|
278
|
+
const queryClient = reactQuery.useQueryClient();
|
|
279
|
+
return reactQuery.useMutation({
|
|
280
|
+
mutationFn: (ids) => sdk.client.fetch("/admin/veeqo/sales-channels/sync", {
|
|
281
|
+
method: "POST",
|
|
282
|
+
body: { sales_channel_ids: ids }
|
|
283
|
+
}),
|
|
284
|
+
onSuccess: () => {
|
|
285
|
+
queryClient.invalidateQueries({ queryKey: ["sales_channels"] });
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
const useVeeqoStockLocations = (params) => reactQuery.useQuery({
|
|
290
|
+
queryFn: () => sdk.client.fetch("/admin/stock-locations", {
|
|
291
|
+
query: {
|
|
292
|
+
fields: "id,name,address.city,address.country_code,veeqo_warehouse.veeqo_warehouse_id",
|
|
293
|
+
...params
|
|
294
|
+
}
|
|
295
|
+
}),
|
|
296
|
+
queryKey: ["stock_locations", params.limit, params.offset]
|
|
297
|
+
});
|
|
298
|
+
const useSyncVeeqoStockLocations = () => {
|
|
299
|
+
const queryClient = reactQuery.useQueryClient();
|
|
300
|
+
return reactQuery.useMutation({
|
|
301
|
+
mutationFn: (ids) => sdk.client.fetch("/admin/veeqo/stock-locations/sync", {
|
|
302
|
+
method: "POST",
|
|
303
|
+
body: { stock_location_ids: ids }
|
|
304
|
+
}),
|
|
305
|
+
onSuccess: () => {
|
|
306
|
+
queryClient.invalidateQueries({ queryKey: ["stock_locations"] });
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
};
|
|
310
|
+
const useVeeqoShippingOptions = (params) => reactQuery.useQuery({
|
|
311
|
+
queryFn: () => sdk.client.fetch("/admin/shipping-options", {
|
|
312
|
+
query: {
|
|
313
|
+
fields: "id,name,type,veeqo_delivery_method.veeqo_delivery_method_id",
|
|
314
|
+
...params
|
|
315
|
+
}
|
|
316
|
+
}),
|
|
317
|
+
queryKey: ["shipping-options", params.limit, params.offset]
|
|
318
|
+
});
|
|
319
|
+
const useSyncVeeqoShippingOptions = () => {
|
|
320
|
+
const queryClient = reactQuery.useQueryClient();
|
|
321
|
+
return reactQuery.useMutation({
|
|
322
|
+
mutationFn: (ids) => sdk.client.fetch("/admin/veeqo/shipping-options/sync", {
|
|
323
|
+
method: "POST",
|
|
324
|
+
body: { shipping_option_ids: ids }
|
|
325
|
+
}),
|
|
326
|
+
onSuccess: () => {
|
|
327
|
+
queryClient.invalidateQueries({ queryKey: ["shipping-options"] });
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
};
|
|
331
|
+
const useVeeqoProducts = (params) => reactQuery.useQuery({
|
|
332
|
+
queryFn: () => sdk.client.fetch("/admin/products", {
|
|
333
|
+
query: { fields: "id,title,status,veeqo_product.veeqo_product_id", ...params }
|
|
334
|
+
}),
|
|
335
|
+
queryKey: ["products", params.limit, params.offset]
|
|
336
|
+
});
|
|
337
|
+
const useVeeqoProductVariants = (params) => reactQuery.useQuery({
|
|
338
|
+
queryFn: () => sdk.client.fetch("/admin/product-variants", {
|
|
339
|
+
query: {
|
|
340
|
+
fields: "id,title,sku,product_id,product.title,veeqo_sellable.veeqo_sellable_id",
|
|
341
|
+
...params
|
|
342
|
+
}
|
|
343
|
+
}),
|
|
344
|
+
queryKey: ["product-variants", params.limit, params.offset]
|
|
345
|
+
});
|
|
346
|
+
const useSyncVeeqoProducts = () => {
|
|
347
|
+
const queryClient = reactQuery.useQueryClient();
|
|
348
|
+
return reactQuery.useMutation({
|
|
349
|
+
mutationFn: (ids) => sdk.client.fetch("/admin/veeqo/products/sync", {
|
|
350
|
+
method: "POST",
|
|
351
|
+
body: { product_ids: ids }
|
|
352
|
+
}),
|
|
353
|
+
onSuccess: () => {
|
|
354
|
+
queryClient.invalidateQueries({ queryKey: ["products"] });
|
|
355
|
+
queryClient.invalidateQueries({ queryKey: ["veeqo-products"] });
|
|
356
|
+
queryClient.invalidateQueries({ queryKey: ["product-variants"] });
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
const config = adminSdk.defineRouteConfig({ label: "Veeqo Setup", rank: 50 });
|
|
361
|
+
const handle = { breadcrumb: () => "Veeqo Setup" };
|
|
362
|
+
const STEPS = ["Stock Locations", "Shipping Options", "Sales Channels", "Products", "Product Variants"];
|
|
363
|
+
const StepIndicator = ({ currentStep }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col md:flex-row md:items-center gap-3 md:gap-4 mb-8", children: STEPS.map((label, index) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col md:flex-row md:items-center gap-2", children: [
|
|
364
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
365
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: `w-7 h-7 rounded-full flex items-center justify-center text-xs font-medium ${index < currentStep ? "bg-ui-bg-interactive text-white" : index === currentStep ? "bg-ui-bg-interactive text-white ring-2 ring-ui-border-interactive ring-offset-2" : "bg-ui-bg-subtle text-ui-fg-subtle"}`, children: index < currentStep ? "✓" : index + 1 }),
|
|
366
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: index === currentStep ? "plus" : "regular", className: index === currentStep ? "text-ui-fg-base" : "text-ui-fg-subtle", children: label })
|
|
367
|
+
] }),
|
|
368
|
+
index < STEPS.length - 1 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-3 h-6 w-px bg-ui-border-base md:ml-2 md:h-px md:w-8" })
|
|
369
|
+
] }, label)) });
|
|
370
|
+
const salesChannelColumnHelper = ui.createDataTableColumnHelper();
|
|
371
|
+
const salesChannelColumns = [
|
|
372
|
+
salesChannelColumnHelper.accessor("name", { header: "Name" }),
|
|
373
|
+
salesChannelColumnHelper.accessor((row) => row.description ?? "-", { id: "description", header: "Description" })
|
|
374
|
+
];
|
|
375
|
+
const SalesChannelsStep = ({ onNext, onBack }) => {
|
|
376
|
+
const limit = 15;
|
|
377
|
+
const [pagination, setPagination] = react.useState({ pageSize: limit, pageIndex: 0 });
|
|
378
|
+
const offset = react.useMemo(() => pagination.pageIndex * limit, [pagination]);
|
|
379
|
+
const { data, isLoading } = useVeeqoSalesChannels({ limit, offset });
|
|
380
|
+
const { mutate: syncSalesChannels, isPending } = useSyncVeeqoSalesChannels();
|
|
381
|
+
const missingSalesChannels = react.useMemo(() => (data == null ? void 0 : data.sales_channels.filter((sc) => {
|
|
382
|
+
var _a;
|
|
383
|
+
return !((_a = sc.veeqo_channel) == null ? void 0 : _a.veeqo_channel_id);
|
|
384
|
+
})) ?? [], [data]);
|
|
385
|
+
const missingCount = missingSalesChannels.length;
|
|
386
|
+
const table = ui.useDataTable({
|
|
387
|
+
columns: salesChannelColumns,
|
|
388
|
+
data: missingSalesChannels,
|
|
389
|
+
getRowId: (row) => row.id,
|
|
390
|
+
rowCount: missingCount,
|
|
391
|
+
isLoading,
|
|
392
|
+
pagination: { state: pagination, onPaginationChange: setPagination }
|
|
393
|
+
});
|
|
394
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
395
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
396
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Sync Sales Channels" }),
|
|
397
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle mt-1", children: "The following Medusa sales channels are missing a linked Veeqo channel." })
|
|
398
|
+
] }),
|
|
399
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
|
|
400
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable.Toolbar, { className: "grid grid-cols-1 md:grid-cols-2 gap-4 justify-between px-6 py-4", children: [
|
|
401
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { className: "whitespace-nowrap mr-2", color: missingCount > 0 ? "orange" : "green", size: "base", children: [
|
|
402
|
+
missingCount,
|
|
403
|
+
" missing"
|
|
404
|
+
] }) }),
|
|
405
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", onClick: () => syncSalesChannels(missingSalesChannels.map((sc) => sc.id), { onSuccess: () => ui.toast.success("Sales channels synced to Veeqo"), onError: () => ui.toast.error("Failed to sync sales channels") }), isLoading: isPending, disabled: missingCount === 0 || isPending, children: "Sync All to Veeqo" })
|
|
406
|
+
] }),
|
|
407
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
|
|
408
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Pagination, {})
|
|
409
|
+
] }),
|
|
410
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between mt-4", children: [
|
|
411
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: onBack, children: "← Back" }),
|
|
412
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: onNext, disabled: missingCount > 0, children: "Next: Products →" })
|
|
413
|
+
] })
|
|
414
|
+
] });
|
|
415
|
+
};
|
|
416
|
+
const stockLocationColumnHelper = ui.createDataTableColumnHelper();
|
|
417
|
+
const stockLocationColumns = [
|
|
418
|
+
stockLocationColumnHelper.accessor("name", { header: "Name" }),
|
|
419
|
+
stockLocationColumnHelper.accessor((row) => {
|
|
420
|
+
var _a;
|
|
421
|
+
return ((_a = row.address) == null ? void 0 : _a.city) ?? "-";
|
|
422
|
+
}, { id: "city", header: "City" }),
|
|
423
|
+
stockLocationColumnHelper.accessor((row) => {
|
|
424
|
+
var _a, _b;
|
|
425
|
+
return ((_b = (_a = row.address) == null ? void 0 : _a.country_code) == null ? void 0 : _b.toUpperCase()) ?? "-";
|
|
426
|
+
}, { id: "country_code", header: "Country" })
|
|
427
|
+
];
|
|
428
|
+
const StockLocationsStep = ({ onNext }) => {
|
|
429
|
+
const limit = 15;
|
|
430
|
+
const [pagination, setPagination] = react.useState({ pageSize: limit, pageIndex: 0 });
|
|
431
|
+
const offset = react.useMemo(() => pagination.pageIndex * limit, [pagination]);
|
|
432
|
+
const { data, isLoading } = useVeeqoStockLocations({ limit, offset });
|
|
433
|
+
const { mutate: syncStockLocations, isPending } = useSyncVeeqoStockLocations();
|
|
434
|
+
const missingStockLocations = react.useMemo(() => (data == null ? void 0 : data.stock_locations.filter((sl) => {
|
|
435
|
+
var _a;
|
|
436
|
+
return !((_a = sl.veeqo_warehouse) == null ? void 0 : _a.veeqo_warehouse_id);
|
|
437
|
+
})) ?? [], [data]);
|
|
438
|
+
const missingCount = missingStockLocations.length;
|
|
439
|
+
const table = ui.useDataTable({
|
|
440
|
+
columns: stockLocationColumns,
|
|
441
|
+
data: missingStockLocations,
|
|
442
|
+
getRowId: (row) => row.id,
|
|
443
|
+
rowCount: missingCount,
|
|
444
|
+
isLoading,
|
|
445
|
+
pagination: { state: pagination, onPaginationChange: setPagination }
|
|
446
|
+
});
|
|
447
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
448
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
449
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Sync Stock Locations" }),
|
|
450
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle mt-1", children: "The following Medusa stock locations are missing a linked Veeqo warehouse." })
|
|
451
|
+
] }),
|
|
452
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
|
|
453
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable.Toolbar, { className: "grid grid-cols-1 md:grid-cols-2 gap-4 justify-between px-6 py-4", children: [
|
|
454
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { className: "whitespace-nowrap mr-2", color: missingCount > 0 ? "orange" : "green", size: "base", children: [
|
|
455
|
+
missingCount,
|
|
456
|
+
" missing"
|
|
457
|
+
] }) }),
|
|
458
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", onClick: () => syncStockLocations(missingStockLocations.map((sl) => sl.id), { onSuccess: () => ui.toast.success("Stock locations synced to Veeqo"), onError: () => ui.toast.error("Failed to sync stock locations") }), isLoading: isPending, disabled: missingCount === 0 || isPending, children: "Sync All to Veeqo" })
|
|
459
|
+
] }),
|
|
460
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
|
|
461
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Pagination, {})
|
|
462
|
+
] }),
|
|
463
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: onNext, disabled: missingCount > 0, children: "Next: Shipping Options →" }) })
|
|
464
|
+
] });
|
|
465
|
+
};
|
|
466
|
+
const shippingOptionColumnHelper = ui.createDataTableColumnHelper();
|
|
467
|
+
const shippingOptionColumns = [
|
|
468
|
+
shippingOptionColumnHelper.accessor("name", { header: "Name" }),
|
|
469
|
+
shippingOptionColumnHelper.accessor((row) => {
|
|
470
|
+
var _a;
|
|
471
|
+
return ((_a = row.type) == null ? void 0 : _a.code) ?? "-";
|
|
472
|
+
}, { id: "code", header: "Code" })
|
|
473
|
+
];
|
|
474
|
+
const ShippingOptionsStep = ({ onNext, onBack }) => {
|
|
475
|
+
const limit = 15;
|
|
476
|
+
const [pagination, setPagination] = react.useState({ pageSize: limit, pageIndex: 0 });
|
|
477
|
+
const offset = react.useMemo(() => pagination.pageIndex * limit, [pagination]);
|
|
478
|
+
const { data, isLoading } = useVeeqoShippingOptions({ limit, offset });
|
|
479
|
+
const { mutate: syncShippingOptions, isPending } = useSyncVeeqoShippingOptions();
|
|
480
|
+
const missingShippingOptions = react.useMemo(() => (data == null ? void 0 : data.shipping_options.filter((so) => {
|
|
481
|
+
var _a;
|
|
482
|
+
return !((_a = so.veeqo_delivery_method) == null ? void 0 : _a.veeqo_delivery_method_id);
|
|
483
|
+
})) ?? [], [data]);
|
|
484
|
+
const missingCount = missingShippingOptions.length;
|
|
485
|
+
const table = ui.useDataTable({
|
|
486
|
+
columns: shippingOptionColumns,
|
|
487
|
+
data: missingShippingOptions,
|
|
488
|
+
getRowId: (row) => row.id,
|
|
489
|
+
rowCount: missingCount,
|
|
490
|
+
isLoading,
|
|
491
|
+
pagination: { state: pagination, onPaginationChange: setPagination }
|
|
492
|
+
});
|
|
493
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
494
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
495
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Sync Shipping Options" }),
|
|
496
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle mt-1", children: "The following Medusa shipping options are missing a linked Veeqo delivery method." })
|
|
497
|
+
] }),
|
|
498
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
|
|
499
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable.Toolbar, { className: "grid grid-cols-1 md:grid-cols-2 gap-4 justify-between px-6 py-4", children: [
|
|
500
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { className: "whitespace-nowrap mr-2", color: missingCount > 0 ? "orange" : "green", size: "base", children: [
|
|
501
|
+
missingCount,
|
|
502
|
+
" missing"
|
|
503
|
+
] }) }),
|
|
504
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", onClick: () => syncShippingOptions(missingShippingOptions.map((s) => s.id), { onSuccess: () => ui.toast.success("Shipping options synced to Veeqo"), onError: () => ui.toast.error("Failed to sync shipping options") }), isLoading: isPending, disabled: missingCount === 0 || isPending, children: "Sync All to Veeqo" })
|
|
505
|
+
] }),
|
|
506
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
|
|
507
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Pagination, {})
|
|
508
|
+
] }),
|
|
509
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between mt-4", children: [
|
|
510
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: onBack, children: "← Back" }),
|
|
511
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: onNext, disabled: missingCount > 0, children: "Next: Sales Channels →" })
|
|
512
|
+
] })
|
|
513
|
+
] });
|
|
514
|
+
};
|
|
515
|
+
const productColumnHelper = ui.createDataTableColumnHelper();
|
|
516
|
+
const productColumns = [
|
|
517
|
+
productColumnHelper.accessor("title", { header: "Title" }),
|
|
518
|
+
productColumnHelper.accessor("status", {
|
|
519
|
+
header: "Status",
|
|
520
|
+
cell: ({ getValue }) => /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { className: "whitespace-nowrap mr-2", color: getValue() === "published" ? "green" : "grey", size: "xsmall", children: getValue() })
|
|
521
|
+
})
|
|
522
|
+
];
|
|
523
|
+
const ProductsStep = ({ onNext, onBack }) => {
|
|
524
|
+
const limit = 15;
|
|
525
|
+
const [pagination, setPagination] = react.useState({ pageSize: limit, pageIndex: 0 });
|
|
526
|
+
const offset = react.useMemo(() => pagination.pageIndex * limit, [pagination]);
|
|
527
|
+
const { data, isLoading } = useVeeqoProducts({ limit, offset });
|
|
528
|
+
const { mutate: syncProducts, isPending } = useSyncVeeqoProducts();
|
|
529
|
+
const missingProducts = react.useMemo(() => (data == null ? void 0 : data.products.filter((p) => {
|
|
530
|
+
var _a;
|
|
531
|
+
return !((_a = p.veeqo_product) == null ? void 0 : _a.veeqo_product_id);
|
|
532
|
+
})) ?? [], [data]);
|
|
533
|
+
const missingCount = missingProducts.length;
|
|
534
|
+
const table = ui.useDataTable({
|
|
535
|
+
columns: productColumns,
|
|
536
|
+
data: missingProducts,
|
|
537
|
+
getRowId: (row) => row.id,
|
|
538
|
+
rowCount: missingCount,
|
|
539
|
+
isLoading,
|
|
540
|
+
pagination: { state: pagination, onPaginationChange: setPagination }
|
|
541
|
+
});
|
|
542
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
543
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
544
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Sync Products" }),
|
|
545
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle mt-1", children: "The following Medusa products are missing a linked Veeqo product." })
|
|
546
|
+
] }),
|
|
547
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
|
|
548
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable.Toolbar, { className: "grid grid-cols-1 md:grid-cols-2 justify-between gap-4 px-6 py-4", children: [
|
|
549
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { className: "whitespace-nowrap mr-2", color: missingCount > 0 ? "orange" : "green", size: "base", children: [
|
|
550
|
+
missingCount,
|
|
551
|
+
" missing"
|
|
552
|
+
] }) }),
|
|
553
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", onClick: () => syncProducts(missingProducts.map((p) => p.id), { onSuccess: () => ui.toast.success("Products synced to Veeqo"), onError: () => ui.toast.error("Failed to sync products") }), isLoading: isPending, disabled: missingCount === 0 || isPending, children: "Sync All to Veeqo" })
|
|
554
|
+
] }),
|
|
555
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
|
|
556
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Pagination, {})
|
|
557
|
+
] }),
|
|
558
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between mt-4", children: [
|
|
559
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: onBack, children: "← Back" }),
|
|
560
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { onClick: onNext, disabled: missingCount > 0, children: "Next: Product Variants →" })
|
|
561
|
+
] })
|
|
562
|
+
] });
|
|
563
|
+
};
|
|
564
|
+
const variantColumnHelper = ui.createDataTableColumnHelper();
|
|
565
|
+
const variantColumns = [
|
|
566
|
+
variantColumnHelper.accessor("title", { header: "Variant" }),
|
|
567
|
+
variantColumnHelper.accessor((row) => {
|
|
568
|
+
var _a;
|
|
569
|
+
return ((_a = row.product) == null ? void 0 : _a.title) ?? "-";
|
|
570
|
+
}, { id: "product_title", header: "Product" }),
|
|
571
|
+
variantColumnHelper.accessor("sku", { header: "SKU" })
|
|
572
|
+
];
|
|
573
|
+
const ProductVariantsStep = ({ onBack }) => {
|
|
574
|
+
const limit = 15;
|
|
575
|
+
const [pagination, setPagination] = react.useState({ pageSize: limit, pageIndex: 0 });
|
|
576
|
+
const offset = react.useMemo(() => pagination.pageIndex * limit, [pagination]);
|
|
577
|
+
const { data, isLoading } = useVeeqoProductVariants({ limit, offset });
|
|
578
|
+
const { mutate: syncProducts, isPending } = useSyncVeeqoProducts();
|
|
579
|
+
const missingVariants = react.useMemo(() => (data == null ? void 0 : data.variants.filter((v) => {
|
|
580
|
+
var _a;
|
|
581
|
+
return !((_a = v.veeqo_sellable) == null ? void 0 : _a.veeqo_sellable_id);
|
|
582
|
+
})) ?? [], [data]);
|
|
583
|
+
const productIdsToSync = react.useMemo(() => [...new Set(missingVariants.map((v) => v.product_id).filter(Boolean))], [missingVariants]);
|
|
584
|
+
const missingCount = missingVariants.length;
|
|
585
|
+
const table = ui.useDataTable({
|
|
586
|
+
columns: variantColumns,
|
|
587
|
+
data: missingVariants,
|
|
588
|
+
getRowId: (row) => row.id,
|
|
589
|
+
rowCount: missingCount,
|
|
590
|
+
isLoading,
|
|
591
|
+
pagination: { state: pagination, onPaginationChange: setPagination }
|
|
592
|
+
});
|
|
593
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
594
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
595
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Sync Product Variants" }),
|
|
596
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle mt-1", children: "The following Medusa product variants are missing a linked Veeqo sellable." })
|
|
597
|
+
] }),
|
|
598
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
|
|
599
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable.Toolbar, { className: "grid grid-cols-1 md:grid-cols-2 gap-4 justify-between md:px-6 py-4", children: [
|
|
600
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { className: "whitespace-nowrap mr-2", color: missingCount > 0 ? "orange" : "green", size: "base", children: [
|
|
601
|
+
missingCount,
|
|
602
|
+
" missing"
|
|
603
|
+
] }) }),
|
|
604
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", onClick: () => syncProducts(productIdsToSync, { onSuccess: () => ui.toast.success("Products with missing variants synced to Veeqo"), onError: () => ui.toast.error("Failed to sync products") }), isLoading: isPending, disabled: productIdsToSync.length === 0 || isPending, children: "Sync All to Veeqo" })
|
|
605
|
+
] }),
|
|
606
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
|
|
607
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Pagination, {})
|
|
608
|
+
] }),
|
|
609
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-start mt-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", onClick: onBack, children: "← Back" }) })
|
|
610
|
+
] });
|
|
611
|
+
};
|
|
612
|
+
const VeeqoSettingsPage = () => {
|
|
613
|
+
const [currentStep, setCurrentStep] = react.useState(0);
|
|
614
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "py-8", children: [
|
|
615
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Veeqo Setup" }),
|
|
616
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "text-ui-fg-subtle mt-2 mb-6", children: "Before Medusa can send orders to Veeqo for shipping, data in your Medusa store needs to be fully synced with Veeqo." }),
|
|
617
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "large", className: "text-ui-fg-subtle mt-2 mb-6", children: "Follow the steps below to check for critical data that is missing in Veeqo and needs to be synced." }),
|
|
618
|
+
/* @__PURE__ */ jsxRuntime.jsx(StepIndicator, { currentStep }),
|
|
619
|
+
currentStep === 0 && /* @__PURE__ */ jsxRuntime.jsx(StockLocationsStep, { onNext: () => setCurrentStep(1) }),
|
|
620
|
+
currentStep === 1 && /* @__PURE__ */ jsxRuntime.jsx(ShippingOptionsStep, { onNext: () => setCurrentStep(2), onBack: () => setCurrentStep(0) }),
|
|
621
|
+
currentStep === 2 && /* @__PURE__ */ jsxRuntime.jsx(SalesChannelsStep, { onNext: () => setCurrentStep(3), onBack: () => setCurrentStep(1) }),
|
|
622
|
+
currentStep === 3 && /* @__PURE__ */ jsxRuntime.jsx(ProductsStep, { onNext: () => setCurrentStep(4), onBack: () => setCurrentStep(2) }),
|
|
623
|
+
currentStep === 4 && /* @__PURE__ */ jsxRuntime.jsx(ProductVariantsStep, { onBack: () => setCurrentStep(3) })
|
|
624
|
+
] });
|
|
625
|
+
};
|
|
626
|
+
const widgetModule = { widgets: [
|
|
627
|
+
{
|
|
628
|
+
Component: CustomerVeeqoWidget,
|
|
629
|
+
zone: ["customer.details.side.after"]
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
Component: OrderVeeqoWidget,
|
|
633
|
+
zone: ["order.details.side.after"]
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
Component: ProductVariantVeeqoWidget,
|
|
637
|
+
zone: ["product_variant.details.side.after"]
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
Component: ProductVeeqoWidget,
|
|
641
|
+
zone: ["product.details.side.after"]
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
Component: SalesChannelVeeqoWidget,
|
|
645
|
+
zone: ["sales_channel.details.after"]
|
|
646
|
+
},
|
|
647
|
+
{
|
|
648
|
+
Component: StockLocationVeeqoWidget,
|
|
649
|
+
zone: ["location.details.side.after"]
|
|
650
|
+
}
|
|
651
|
+
] };
|
|
652
|
+
const routeModule = {
|
|
653
|
+
routes: [
|
|
654
|
+
{
|
|
655
|
+
Component: VeeqoSettingsPage,
|
|
656
|
+
path: "/settings/veeqo",
|
|
657
|
+
handle
|
|
658
|
+
}
|
|
659
|
+
]
|
|
660
|
+
};
|
|
661
|
+
const menuItemModule = {
|
|
662
|
+
menuItems: [
|
|
663
|
+
{
|
|
664
|
+
label: config.label,
|
|
665
|
+
icon: void 0,
|
|
666
|
+
path: "/settings/veeqo",
|
|
667
|
+
nested: void 0,
|
|
668
|
+
rank: 50,
|
|
669
|
+
translationNs: void 0
|
|
670
|
+
}
|
|
671
|
+
]
|
|
672
|
+
};
|
|
673
|
+
const formModule = { customFields: {} };
|
|
674
|
+
const displayModule = {
|
|
675
|
+
displays: {}
|
|
676
|
+
};
|
|
677
|
+
const i18nModule = { resources: {} };
|
|
678
|
+
const plugin = {
|
|
679
|
+
widgetModule,
|
|
680
|
+
routeModule,
|
|
681
|
+
menuItemModule,
|
|
682
|
+
formModule,
|
|
683
|
+
displayModule,
|
|
684
|
+
i18nModule
|
|
685
|
+
};
|
|
686
|
+
module.exports = plugin;
|