@jytextiles/medusa-plugin-faire-store-sync 0.2.6 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/__tests__/faire-client.spec.js +98 -1
- package/.medusa/server/src/admin/index.js +312 -151
- package/.medusa/server/src/admin/index.mjs +314 -153
- package/.medusa/server/src/api/admin/faire/status/route.js +4 -4
- package/.medusa/server/src/api/admin/faire/taxonomy/route.js +12 -0
- package/.medusa/server/src/lib/faire-client.js +21 -1
- package/.medusa/server/src/modules/faire-sync/service.js +8 -1
- package/.medusa/server/src/workflows/sync-product-to-faire.js +11 -5
- package/package.json +1 -1
- package/src/__tests__/faire-client.spec.ts +107 -0
- package/src/admin/lib/api.ts +5 -1
- package/src/admin/routes/settings/faire/{bulk → @bulk}/page.tsx +17 -7
- package/src/admin/routes/settings/faire/{settings → @settings}/page.tsx +59 -21
- package/src/admin/routes/settings/faire/page.tsx +9 -2
- package/src/admin/widgets/faire-product-widget.tsx +182 -1
- package/src/api/admin/faire/status/route.ts +3 -3
- package/src/api/admin/faire/taxonomy/route.ts +10 -0
- package/src/lib/faire-client.ts +27 -0
- package/src/modules/faire-sync/service.ts +7 -0
- package/src/workflows/sync-product-to-faire.ts +16 -6
- /package/src/admin/routes/settings/faire/{bulk → @bulk}/use-bulk-product-columns.tsx +0 -0
|
@@ -3,9 +3,9 @@ const jsxRuntime = require("react/jsx-runtime");
|
|
|
3
3
|
const adminSdk = require("@medusajs/admin-sdk");
|
|
4
4
|
const ui = require("@medusajs/ui");
|
|
5
5
|
const reactQuery = require("@tanstack/react-query");
|
|
6
|
+
const react = require("react");
|
|
6
7
|
const Medusa = require("@medusajs/js-sdk");
|
|
7
8
|
const icons = require("@medusajs/icons");
|
|
8
|
-
const react = require("react");
|
|
9
9
|
const reactRouterDom = require("react-router-dom");
|
|
10
10
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
11
11
|
const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
|
|
@@ -63,6 +63,9 @@ const faireApi = {
|
|
|
63
63
|
getSync: (id) => sdk.client.fetch(`/admin/faire/syncs/${id}`),
|
|
64
64
|
retrySync: (id) => sdk.client.fetch(`/admin/faire/syncs/${id}`, { method: "POST" }),
|
|
65
65
|
brand: () => sdk.client.fetch("/admin/faire/brand"),
|
|
66
|
+
taxonomy: () => sdk.client.fetch(
|
|
67
|
+
"/admin/faire/taxonomy"
|
|
68
|
+
),
|
|
66
69
|
products: (opts = {}) => {
|
|
67
70
|
const query = {};
|
|
68
71
|
if (opts.limit !== void 0) query.limit = String(opts.limit);
|
|
@@ -71,6 +74,7 @@ const faireApi = {
|
|
|
71
74
|
return sdk.client.fetch("/admin/faire/products", { query });
|
|
72
75
|
}
|
|
73
76
|
};
|
|
77
|
+
const MAX_RENDERED = 60;
|
|
74
78
|
const badgeColor = (status) => {
|
|
75
79
|
switch (status) {
|
|
76
80
|
case "synced":
|
|
@@ -86,7 +90,7 @@ const badgeColor = (status) => {
|
|
|
86
90
|
}
|
|
87
91
|
};
|
|
88
92
|
const FaireProductWidget = ({ data }) => {
|
|
89
|
-
var _a, _b, _c;
|
|
93
|
+
var _a, _b, _c, _d;
|
|
90
94
|
const queryClient = reactQuery.useQueryClient();
|
|
91
95
|
const productKey = ["faire", "product-status", data.id];
|
|
92
96
|
const statusQuery = reactQuery.useQuery({
|
|
@@ -129,6 +133,45 @@ const FaireProductWidget = ({ data }) => {
|
|
|
129
133
|
status: latest.status
|
|
130
134
|
} : null;
|
|
131
135
|
const notReady = connected && readiness && !readiness.ready_to_publish;
|
|
136
|
+
const metadata = data.metadata || {};
|
|
137
|
+
const productType = (_d = data.type) == null ? void 0 : _d.value;
|
|
138
|
+
const [pickerOpen, setPickerOpen] = react.useState(false);
|
|
139
|
+
const [pickerSearch, setPickerSearch] = react.useState("");
|
|
140
|
+
const [pinnedOverride, setPinnedOverride] = react.useState(
|
|
141
|
+
void 0
|
|
142
|
+
);
|
|
143
|
+
const taxonomyQuery = reactQuery.useQuery({
|
|
144
|
+
queryKey: ["faire", "taxonomy"],
|
|
145
|
+
enabled: pickerOpen,
|
|
146
|
+
queryFn: async () => (await faireApi.taxonomy()).taxonomy ?? []
|
|
147
|
+
});
|
|
148
|
+
const taxonomy = taxonomyQuery.data ?? [];
|
|
149
|
+
const pinnedId = pinnedOverride !== void 0 ? pinnedOverride ?? void 0 : metadata.faire_taxonomy_type_id;
|
|
150
|
+
const pinnedName = react.useMemo(() => {
|
|
151
|
+
var _a2;
|
|
152
|
+
if (!pinnedId) return void 0;
|
|
153
|
+
if (!/^tt_/.test(String(pinnedId))) return String(pinnedId);
|
|
154
|
+
return (_a2 = taxonomy.find((t) => t.id === pinnedId)) == null ? void 0 : _a2.name;
|
|
155
|
+
}, [pinnedId, taxonomy]);
|
|
156
|
+
const resolvedSource = pinnedId ? { label: pinnedName || String(pinnedId), from: "pinned" } : metadata.faire_category ? { label: String(metadata.faire_category), from: "metadata" } : productType ? { label: productType, from: "type" } : { label: "Account fallback", from: "fallback" };
|
|
157
|
+
const filteredTaxonomy = react.useMemo(() => {
|
|
158
|
+
const q = pickerSearch.trim().toLowerCase();
|
|
159
|
+
const list = q ? taxonomy.filter((t) => t.name.toLowerCase().includes(q)) : taxonomy;
|
|
160
|
+
return list.slice(0, MAX_RENDERED);
|
|
161
|
+
}, [taxonomy, pickerSearch]);
|
|
162
|
+
const saveCategory = reactQuery.useMutation({
|
|
163
|
+
mutationFn: (value) => sdk.admin.product.update(data.id, {
|
|
164
|
+
metadata: { ...metadata, faire_taxonomy_type_id: value }
|
|
165
|
+
}),
|
|
166
|
+
onSuccess: (_res, value) => {
|
|
167
|
+
setPinnedOverride(value);
|
|
168
|
+
queryClient.invalidateQueries({ queryKey: ["product", data.id] });
|
|
169
|
+
setPickerOpen(false);
|
|
170
|
+
setPickerSearch("");
|
|
171
|
+
ui.toast.success(value ? "Faire category set" : "Faire category cleared");
|
|
172
|
+
},
|
|
173
|
+
onError: (err) => ui.toast.error("Failed to save category", { description: err == null ? void 0 : err.message })
|
|
174
|
+
});
|
|
132
175
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
133
176
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
134
177
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
@@ -155,6 +198,25 @@ const FaireProductWidget = ({ data }) => {
|
|
|
155
198
|
}
|
|
156
199
|
)
|
|
157
200
|
] }) }),
|
|
201
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 rounded-lg border px-3 py-2", children: [
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-0.5", children: [
|
|
203
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
204
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: "Faire category" }),
|
|
205
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: resolvedSource.from === "fallback" ? "orange" : "grey", children: resolvedSource.from === "pinned" ? "Pinned" : resolvedSource.from === "type" ? "From Type" : resolvedSource.from === "metadata" ? "Metadata" : "Fallback" })
|
|
206
|
+
] }),
|
|
207
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: resolvedSource.label })
|
|
208
|
+
] }),
|
|
209
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
210
|
+
ui.Button,
|
|
211
|
+
{
|
|
212
|
+
size: "small",
|
|
213
|
+
variant: "secondary",
|
|
214
|
+
disabled: !connected,
|
|
215
|
+
onClick: () => setPickerOpen(true),
|
|
216
|
+
children: pinnedId ? "Change" : "Set category"
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
] }),
|
|
158
220
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-3", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
159
221
|
ui.Tooltip,
|
|
160
222
|
{
|
|
@@ -171,7 +233,68 @@ const FaireProductWidget = ({ data }) => {
|
|
|
171
233
|
)
|
|
172
234
|
}
|
|
173
235
|
) })
|
|
174
|
-
] })
|
|
236
|
+
] }),
|
|
237
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer, { open: pickerOpen, onOpenChange: setPickerOpen, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Content, { children: [
|
|
238
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Header, { children: [
|
|
239
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Title, { children: "Pick a Faire category" }),
|
|
240
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Description, { children: "Search Faire's product taxonomy and pin an exact category to this product." })
|
|
241
|
+
] }),
|
|
242
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Body, { className: "flex flex-col gap-3 overflow-hidden", children: [
|
|
243
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
244
|
+
ui.Input,
|
|
245
|
+
{
|
|
246
|
+
type: "search",
|
|
247
|
+
autoFocus: true,
|
|
248
|
+
placeholder: "Search categories…",
|
|
249
|
+
value: pickerSearch,
|
|
250
|
+
onChange: (e) => setPickerSearch(e.target.value)
|
|
251
|
+
}
|
|
252
|
+
),
|
|
253
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1 overflow-y-auto", children: [
|
|
254
|
+
taxonomyQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle px-1 py-2", children: "Loading categories…" }) : filteredTaxonomy.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle px-1 py-2", children: [
|
|
255
|
+
"No categories match “",
|
|
256
|
+
pickerSearch,
|
|
257
|
+
"”."
|
|
258
|
+
] }) : filteredTaxonomy.map((t) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
259
|
+
"button",
|
|
260
|
+
{
|
|
261
|
+
type: "button",
|
|
262
|
+
onClick: () => saveCategory.mutate(t.id),
|
|
263
|
+
disabled: saveCategory.isPending,
|
|
264
|
+
className: ui.clx(
|
|
265
|
+
"flex items-center justify-between rounded-md px-3 py-2 text-left text-sm",
|
|
266
|
+
"hover:bg-ui-bg-base-hover",
|
|
267
|
+
t.id === pinnedId && "bg-ui-bg-highlight"
|
|
268
|
+
),
|
|
269
|
+
children: [
|
|
270
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: t.name }),
|
|
271
|
+
t.id === pinnedId && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "green", children: "Current" })
|
|
272
|
+
]
|
|
273
|
+
},
|
|
274
|
+
t.id
|
|
275
|
+
)),
|
|
276
|
+
!taxonomyQuery.isLoading && taxonomy.length > filteredTaxonomy.length && /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", className: "text-ui-fg-muted px-1 py-1", children: [
|
|
277
|
+
"Showing ",
|
|
278
|
+
filteredTaxonomy.length,
|
|
279
|
+
" of ",
|
|
280
|
+
taxonomy.length,
|
|
281
|
+
" — refine your search to narrow down."
|
|
282
|
+
] })
|
|
283
|
+
] })
|
|
284
|
+
] }),
|
|
285
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Drawer.Footer, { children: [
|
|
286
|
+
pinnedId && /* @__PURE__ */ jsxRuntime.jsx(
|
|
287
|
+
ui.Button,
|
|
288
|
+
{
|
|
289
|
+
variant: "secondary",
|
|
290
|
+
onClick: () => saveCategory.mutate(null),
|
|
291
|
+
isLoading: saveCategory.isPending,
|
|
292
|
+
children: "Clear"
|
|
293
|
+
}
|
|
294
|
+
),
|
|
295
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Drawer.Close, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "secondary", children: "Cancel" }) })
|
|
296
|
+
] })
|
|
297
|
+
] }) })
|
|
175
298
|
] });
|
|
176
299
|
};
|
|
177
300
|
adminSdk.defineWidgetConfig({
|
|
@@ -339,7 +462,8 @@ const FaireSettingsPage = () => {
|
|
|
339
462
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-0", children: [
|
|
340
463
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-5 w-32" }) }),
|
|
341
464
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-3 px-6 pb-6", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-10 w-full" }, i)) })
|
|
342
|
-
] })
|
|
465
|
+
] }),
|
|
466
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Outlet, {})
|
|
343
467
|
] });
|
|
344
468
|
}
|
|
345
469
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
@@ -467,6 +591,7 @@ const FaireSettingsPage = () => {
|
|
|
467
591
|
)
|
|
468
592
|
] })
|
|
469
593
|
] }) }),
|
|
594
|
+
/* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Outlet, {}),
|
|
470
595
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
|
|
471
596
|
] });
|
|
472
597
|
};
|
|
@@ -689,10 +814,7 @@ const FaireBulkPage = () => {
|
|
|
689
814
|
search: { state: search, onSearchChange: setSearch }
|
|
690
815
|
});
|
|
691
816
|
return /* @__PURE__ */ jsxRuntime.jsxs(RouteFocusModal, { prev: "/settings/faire", children: [
|
|
692
|
-
/* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Header, { children: /* @__PURE__ */ jsxRuntime.
|
|
693
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Bulk sync products to Faire" }),
|
|
694
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", size: "small", children: "Select products (or all matching) and push them to Faire as a background sync via the command bar." })
|
|
695
|
-
] }) }),
|
|
817
|
+
/* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Title, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Bulk sync" }) }) }),
|
|
696
818
|
/* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Body, { className: "flex flex-1 flex-col overflow-hidden p-0", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
|
|
697
819
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable.Toolbar, { className: "flex flex-col gap-y-3 border-b px-6 py-4", children: [
|
|
698
820
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2 md:flex-row md:items-center md:justify-between", children: [
|
|
@@ -732,6 +854,22 @@ const FaireBulkPage = () => {
|
|
|
732
854
|
},
|
|
733
855
|
children: selectAllMatching ? `All ${count} selected` : `Select all ${count}`
|
|
734
856
|
}
|
|
857
|
+
),
|
|
858
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
859
|
+
ui.Button,
|
|
860
|
+
{
|
|
861
|
+
size: "small",
|
|
862
|
+
onClick: () => {
|
|
863
|
+
if (!connected) {
|
|
864
|
+
ui.toast.error("Faire is not connected");
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
pushMutation.mutate();
|
|
868
|
+
},
|
|
869
|
+
disabled: !connected || targetCount === 0,
|
|
870
|
+
isLoading: pushMutation.isPending,
|
|
871
|
+
children: "Push to Faire"
|
|
872
|
+
}
|
|
735
873
|
)
|
|
736
874
|
] })
|
|
737
875
|
] }),
|
|
@@ -780,127 +918,14 @@ const FaireBulkPage = () => {
|
|
|
780
918
|
const handle$2 = {
|
|
781
919
|
breadcrumb: () => "Bulk sync"
|
|
782
920
|
};
|
|
783
|
-
const PARENT$1 = "/settings/faire";
|
|
784
|
-
const STATUS_COLORS = {
|
|
785
|
-
success: "green",
|
|
786
|
-
failed: "red",
|
|
787
|
-
draft: "orange",
|
|
788
|
-
pending: "grey",
|
|
789
|
-
syncing: "grey"
|
|
790
|
-
};
|
|
791
|
-
const FaireSyncDetailPage = () => {
|
|
792
|
-
const { id } = reactRouterDom.useParams();
|
|
793
|
-
const navigate = reactRouterDom.useNavigate();
|
|
794
|
-
const [record, setRecord] = react.useState(null);
|
|
795
|
-
const [loading, setLoading] = react.useState(true);
|
|
796
|
-
const [retrying, setRetrying] = react.useState(false);
|
|
797
|
-
const [error, setError] = react.useState(null);
|
|
798
|
-
react.useEffect(() => {
|
|
799
|
-
if (!id) return;
|
|
800
|
-
setLoading(true);
|
|
801
|
-
faireApi.getSync(id).then((res) => {
|
|
802
|
-
setRecord(res.sync || res);
|
|
803
|
-
}).catch((err) => setError(err.message || "Failed to load sync record")).finally(() => setLoading(false));
|
|
804
|
-
}, [id]);
|
|
805
|
-
const handleRetry = async () => {
|
|
806
|
-
if (!record) return;
|
|
807
|
-
setRetrying(true);
|
|
808
|
-
setError(null);
|
|
809
|
-
try {
|
|
810
|
-
await faireApi.retrySync(record.id);
|
|
811
|
-
const updated = await faireApi.getSync(record.id).catch(() => null);
|
|
812
|
-
if (updated) setRecord(updated.sync || updated);
|
|
813
|
-
} catch (err) {
|
|
814
|
-
setError(err.message || "Retry failed");
|
|
815
|
-
} finally {
|
|
816
|
-
setRetrying(false);
|
|
817
|
-
}
|
|
818
|
-
};
|
|
819
|
-
const warnings = (record == null ? void 0 : record.error_message) ? record.error_message.split(" | ") : [];
|
|
820
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(RouteFocusModal, { prev: PARENT$1, children: [
|
|
821
|
-
/* @__PURE__ */ jsxRuntime.jsxs(RouteFocusModal.Header, { children: [
|
|
822
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
823
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Sync record" }),
|
|
824
|
-
record && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: STATUS_COLORS[record.status] || "grey", children: record.status })
|
|
825
|
-
] }),
|
|
826
|
-
record && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
827
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", disabled: retrying, children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
828
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { align: "end", children: [
|
|
829
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
830
|
-
ui.DropdownMenu.Item,
|
|
831
|
-
{
|
|
832
|
-
className: "gap-x-2",
|
|
833
|
-
onClick: handleRetry,
|
|
834
|
-
disabled: retrying,
|
|
835
|
-
children: [
|
|
836
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowPath, { className: "text-ui-fg-subtle" }),
|
|
837
|
-
retrying ? "Re-syncing…" : "Re-sync product"
|
|
838
|
-
]
|
|
839
|
-
}
|
|
840
|
-
),
|
|
841
|
-
record.product_url && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
842
|
-
ui.DropdownMenu.Item,
|
|
843
|
-
{
|
|
844
|
-
className: "gap-x-2",
|
|
845
|
-
onClick: () => window.open(record.product_url, "_blank"),
|
|
846
|
-
children: [
|
|
847
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, { className: "text-ui-fg-subtle" }),
|
|
848
|
-
"Open on Faire"
|
|
849
|
-
]
|
|
850
|
-
}
|
|
851
|
-
)
|
|
852
|
-
] })
|
|
853
|
-
] }) })
|
|
854
|
-
] }),
|
|
855
|
-
/* @__PURE__ */ jsxRuntime.jsxs(RouteFocusModal.Body, { className: "flex flex-col gap-4 overflow-y-auto p-6", children: [
|
|
856
|
-
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-tag-red-text", children: error }),
|
|
857
|
-
loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-3", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-10 w-full" }, i)) }) : !record ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-4", children: [
|
|
858
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Sync record not found" }),
|
|
859
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", onClick: () => navigate(PARENT$1), children: "Back to Faire settings" })
|
|
860
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
861
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-ui-border-base grid grid-cols-2 gap-4 rounded-lg border p-4", children: [
|
|
862
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Record id", value: record.id, mono: true }),
|
|
863
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Product", value: record.product_id, mono: true }),
|
|
864
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Action", value: record.action }),
|
|
865
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Faire product token", value: record.product_token || "—", mono: true }),
|
|
866
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "State", value: record.product_state || "—" }),
|
|
867
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Published", value: record.published ? "Yes" : "No" }),
|
|
868
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
869
|
-
Detail,
|
|
870
|
-
{
|
|
871
|
-
label: "Synced at",
|
|
872
|
-
value: record.synced_at ? new Date(record.synced_at).toLocaleString() : "—"
|
|
873
|
-
}
|
|
874
|
-
)
|
|
875
|
-
] }),
|
|
876
|
-
warnings.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-ui-border-base flex flex-col gap-2 rounded-lg border p-4", children: [
|
|
877
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Issues" }),
|
|
878
|
-
warnings.map((w, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-tag-red-text text-sm", children: w }, i))
|
|
879
|
-
] })
|
|
880
|
-
] })
|
|
881
|
-
] }),
|
|
882
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
|
|
883
|
-
] });
|
|
884
|
-
};
|
|
885
|
-
const Detail = ({
|
|
886
|
-
label,
|
|
887
|
-
value,
|
|
888
|
-
mono
|
|
889
|
-
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
890
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: label }),
|
|
891
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: mono ? "font-mono text-xs" : "", children: value })
|
|
892
|
-
] });
|
|
893
|
-
const handle$1 = {
|
|
894
|
-
breadcrumb: () => "Sync record"
|
|
895
|
-
};
|
|
896
921
|
const STATUS_KEY = ["faire", "status"];
|
|
897
|
-
const
|
|
922
|
+
const TAXONOMY_KEY = ["faire", "taxonomy"];
|
|
923
|
+
const PARENT$1 = "/settings/faire";
|
|
898
924
|
const NONE = "__none__";
|
|
899
925
|
const cleanSettings = (form) => {
|
|
900
926
|
const out = { ...form };
|
|
901
927
|
for (const key of [
|
|
902
928
|
"default_brand_id",
|
|
903
|
-
"default_shipping_policy_id",
|
|
904
929
|
"default_category"
|
|
905
930
|
]) {
|
|
906
931
|
if (!out[key] || out[key] === NONE) out[key] = null;
|
|
@@ -920,7 +945,7 @@ const FaireSyncSettingsDrawer = () => {
|
|
|
920
945
|
const [form, setForm] = react.useState({});
|
|
921
946
|
const close = () => {
|
|
922
947
|
setOpen(false);
|
|
923
|
-
navigate(PARENT);
|
|
948
|
+
navigate(PARENT$1);
|
|
924
949
|
};
|
|
925
950
|
const statusQuery = reactQuery.useQuery({
|
|
926
951
|
queryKey: STATUS_KEY,
|
|
@@ -928,6 +953,11 @@ const FaireSyncSettingsDrawer = () => {
|
|
|
928
953
|
});
|
|
929
954
|
const status = statusQuery.data;
|
|
930
955
|
const connected = !!(status == null ? void 0 : status.connected);
|
|
956
|
+
const taxonomyQuery = reactQuery.useQuery({
|
|
957
|
+
queryKey: TAXONOMY_KEY,
|
|
958
|
+
queryFn: async () => (await faireApi.taxonomy().catch(() => ({ taxonomy: [] }))).taxonomy || []
|
|
959
|
+
});
|
|
960
|
+
const taxonomy = taxonomyQuery.data ?? [];
|
|
931
961
|
react.useEffect(() => {
|
|
932
962
|
if (status == null ? void 0 : status.settings) setForm(status.settings);
|
|
933
963
|
}, [status == null ? void 0 : status.settings]);
|
|
@@ -956,7 +986,7 @@ const FaireSyncSettingsDrawer = () => {
|
|
|
956
986
|
/* @__PURE__ */ jsxRuntime.jsx(ChecklistItem, { ok: status == null ? void 0 : status.readiness.connected, label: "Faire connected" }),
|
|
957
987
|
/* @__PURE__ */ jsxRuntime.jsx(ChecklistItem, { ok: status == null ? void 0 : status.readiness.brand, label: "Brand configured" }),
|
|
958
988
|
/* @__PURE__ */ jsxRuntime.jsx(ChecklistItem, { ok: status == null ? void 0 : status.readiness.wholesale_pricing, label: "Wholesale pricing" }),
|
|
959
|
-
/* @__PURE__ */ jsxRuntime.jsx(ChecklistItem, { ok: status == null ? void 0 : status.readiness.
|
|
989
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChecklistItem, { ok: status == null ? void 0 : status.readiness.taxonomy, label: "Default category" })
|
|
960
990
|
] })
|
|
961
991
|
] }),
|
|
962
992
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
@@ -966,9 +996,8 @@ const FaireSyncSettingsDrawer = () => {
|
|
|
966
996
|
] }),
|
|
967
997
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4", children: [
|
|
968
998
|
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Brand ID", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
969
|
-
|
|
999
|
+
ui.Input,
|
|
970
1000
|
{
|
|
971
|
-
className: "border-ui-border-base rounded-lg border px-3 py-2 text-sm",
|
|
972
1001
|
value: String(form.default_brand_id ?? ""),
|
|
973
1002
|
onChange: (e) => setForm({ ...form, default_brand_id: e.target.value || null }),
|
|
974
1003
|
disabled: !connected,
|
|
@@ -976,10 +1005,9 @@ const FaireSyncSettingsDrawer = () => {
|
|
|
976
1005
|
}
|
|
977
1006
|
) }),
|
|
978
1007
|
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Wholesale markup % (off retail)", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { content: "Wholesale price = retail × (100 − markup%) / 100. E.g. 50 → half of retail.", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
979
|
-
|
|
1008
|
+
ui.Input,
|
|
980
1009
|
{
|
|
981
1010
|
type: "number",
|
|
982
|
-
className: "border-ui-border-base rounded-lg border px-3 py-2 text-sm",
|
|
983
1011
|
value: String(form.default_wholesale_markup_percent ?? ""),
|
|
984
1012
|
onChange: (e) => setForm({
|
|
985
1013
|
...form,
|
|
@@ -990,10 +1018,9 @@ const FaireSyncSettingsDrawer = () => {
|
|
|
990
1018
|
}
|
|
991
1019
|
) }) }),
|
|
992
1020
|
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Default min order quantity", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
993
|
-
|
|
1021
|
+
ui.Input,
|
|
994
1022
|
{
|
|
995
1023
|
type: "number",
|
|
996
|
-
className: "border-ui-border-base rounded-lg border px-3 py-2 text-sm",
|
|
997
1024
|
value: String(form.default_min_order_quantity ?? 1),
|
|
998
1025
|
onChange: (e) => setForm({
|
|
999
1026
|
...form,
|
|
@@ -1003,10 +1030,9 @@ const FaireSyncSettingsDrawer = () => {
|
|
|
1003
1030
|
}
|
|
1004
1031
|
) }),
|
|
1005
1032
|
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Default lead time (days)", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1006
|
-
|
|
1033
|
+
ui.Input,
|
|
1007
1034
|
{
|
|
1008
1035
|
type: "number",
|
|
1009
|
-
className: "border-ui-border-base rounded-lg border px-3 py-2 text-sm",
|
|
1010
1036
|
value: String(form.default_lead_time_days ?? ""),
|
|
1011
1037
|
onChange: (e) => setForm({
|
|
1012
1038
|
...form,
|
|
@@ -1016,16 +1042,15 @@ const FaireSyncSettingsDrawer = () => {
|
|
|
1016
1042
|
placeholder: "e.g. 14"
|
|
1017
1043
|
}
|
|
1018
1044
|
) }),
|
|
1019
|
-
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "
|
|
1020
|
-
|
|
1045
|
+
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Fallback category (taxonomy)", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { content: "Faire requires a category per product. Resolution order: product metadata → the product's Type (matched by name) → this fallback. Set the product Type or pick a category on the product page for accuracy.", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1046
|
+
SelectField,
|
|
1021
1047
|
{
|
|
1022
|
-
className: "border-ui-border-base rounded-lg border px-3 py-2 text-sm",
|
|
1023
1048
|
value: String(form.default_category ?? ""),
|
|
1024
|
-
|
|
1049
|
+
onValueChange: (v) => setForm({ ...form, default_category: v || null }),
|
|
1025
1050
|
disabled: !connected,
|
|
1026
|
-
|
|
1051
|
+
options: taxonomy.map((t) => ({ value: t.id, label: t.name }))
|
|
1027
1052
|
}
|
|
1028
|
-
) }),
|
|
1053
|
+
) }) }),
|
|
1029
1054
|
/* @__PURE__ */ jsxRuntime.jsx(Field, { label: "Follow product status", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Tooltip, { content: "If on, published Medusa products are published on Faire; draft products sync as drafts.", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1030
1055
|
ui.Switch,
|
|
1031
1056
|
{
|
|
@@ -1063,6 +1088,27 @@ const Field = ({ label, children }) => /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
|
1063
1088
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: label }),
|
|
1064
1089
|
children
|
|
1065
1090
|
] });
|
|
1091
|
+
const SelectField = ({
|
|
1092
|
+
value,
|
|
1093
|
+
onValueChange,
|
|
1094
|
+
options,
|
|
1095
|
+
disabled
|
|
1096
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1097
|
+
ui.Select,
|
|
1098
|
+
{
|
|
1099
|
+
value: value ? value : NONE,
|
|
1100
|
+
onValueChange: (v) => onValueChange(v === NONE ? "" : v),
|
|
1101
|
+
disabled,
|
|
1102
|
+
size: "small",
|
|
1103
|
+
children: [
|
|
1104
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Not set" }) }),
|
|
1105
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
|
|
1106
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: NONE, children: "Not set" }),
|
|
1107
|
+
options.map((o) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: o.value, children: o.label }, o.value))
|
|
1108
|
+
] })
|
|
1109
|
+
]
|
|
1110
|
+
}
|
|
1111
|
+
);
|
|
1066
1112
|
const ChecklistItem = ({ ok, label }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1067
1113
|
"div",
|
|
1068
1114
|
{
|
|
@@ -1076,9 +1122,122 @@ const ChecklistItem = ({ ok, label }) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
|
1076
1122
|
]
|
|
1077
1123
|
}
|
|
1078
1124
|
);
|
|
1079
|
-
const handle = {
|
|
1125
|
+
const handle$1 = {
|
|
1080
1126
|
breadcrumb: () => "Sync settings"
|
|
1081
1127
|
};
|
|
1128
|
+
const PARENT = "/settings/faire";
|
|
1129
|
+
const STATUS_COLORS = {
|
|
1130
|
+
success: "green",
|
|
1131
|
+
failed: "red",
|
|
1132
|
+
draft: "orange",
|
|
1133
|
+
pending: "grey",
|
|
1134
|
+
syncing: "grey"
|
|
1135
|
+
};
|
|
1136
|
+
const FaireSyncDetailPage = () => {
|
|
1137
|
+
const { id } = reactRouterDom.useParams();
|
|
1138
|
+
const navigate = reactRouterDom.useNavigate();
|
|
1139
|
+
const [record, setRecord] = react.useState(null);
|
|
1140
|
+
const [loading, setLoading] = react.useState(true);
|
|
1141
|
+
const [retrying, setRetrying] = react.useState(false);
|
|
1142
|
+
const [error, setError] = react.useState(null);
|
|
1143
|
+
react.useEffect(() => {
|
|
1144
|
+
if (!id) return;
|
|
1145
|
+
setLoading(true);
|
|
1146
|
+
faireApi.getSync(id).then((res) => {
|
|
1147
|
+
setRecord(res.sync || res);
|
|
1148
|
+
}).catch((err) => setError(err.message || "Failed to load sync record")).finally(() => setLoading(false));
|
|
1149
|
+
}, [id]);
|
|
1150
|
+
const handleRetry = async () => {
|
|
1151
|
+
if (!record) return;
|
|
1152
|
+
setRetrying(true);
|
|
1153
|
+
setError(null);
|
|
1154
|
+
try {
|
|
1155
|
+
await faireApi.retrySync(record.id);
|
|
1156
|
+
const updated = await faireApi.getSync(record.id).catch(() => null);
|
|
1157
|
+
if (updated) setRecord(updated.sync || updated);
|
|
1158
|
+
} catch (err) {
|
|
1159
|
+
setError(err.message || "Retry failed");
|
|
1160
|
+
} finally {
|
|
1161
|
+
setRetrying(false);
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
const warnings = (record == null ? void 0 : record.error_message) ? record.error_message.split(" | ") : [];
|
|
1165
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(RouteFocusModal, { prev: PARENT, children: [
|
|
1166
|
+
/* @__PURE__ */ jsxRuntime.jsxs(RouteFocusModal.Header, { children: [
|
|
1167
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1168
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Sync record" }),
|
|
1169
|
+
record && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: STATUS_COLORS[record.status] || "grey", children: record.status })
|
|
1170
|
+
] }),
|
|
1171
|
+
record && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
1172
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", disabled: retrying, children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
1173
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { align: "end", children: [
|
|
1174
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1175
|
+
ui.DropdownMenu.Item,
|
|
1176
|
+
{
|
|
1177
|
+
className: "gap-x-2",
|
|
1178
|
+
onClick: handleRetry,
|
|
1179
|
+
disabled: retrying,
|
|
1180
|
+
children: [
|
|
1181
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowPath, { className: "text-ui-fg-subtle" }),
|
|
1182
|
+
retrying ? "Re-syncing…" : "Re-sync product"
|
|
1183
|
+
]
|
|
1184
|
+
}
|
|
1185
|
+
),
|
|
1186
|
+
record.product_url && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1187
|
+
ui.DropdownMenu.Item,
|
|
1188
|
+
{
|
|
1189
|
+
className: "gap-x-2",
|
|
1190
|
+
onClick: () => window.open(record.product_url, "_blank"),
|
|
1191
|
+
children: [
|
|
1192
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, { className: "text-ui-fg-subtle" }),
|
|
1193
|
+
"Open on Faire"
|
|
1194
|
+
]
|
|
1195
|
+
}
|
|
1196
|
+
)
|
|
1197
|
+
] })
|
|
1198
|
+
] }) })
|
|
1199
|
+
] }),
|
|
1200
|
+
/* @__PURE__ */ jsxRuntime.jsxs(RouteFocusModal.Body, { className: "flex flex-col gap-4 overflow-y-auto p-6", children: [
|
|
1201
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-tag-red-text", children: error }),
|
|
1202
|
+
loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-3", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-10 w-full" }, i)) }) : !record ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-start gap-4", children: [
|
|
1203
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Sync record not found" }),
|
|
1204
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", onClick: () => navigate(PARENT), children: "Back to Faire settings" })
|
|
1205
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1206
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-ui-border-base grid grid-cols-2 gap-4 rounded-lg border p-4", children: [
|
|
1207
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Record id", value: record.id, mono: true }),
|
|
1208
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Product", value: record.product_id, mono: true }),
|
|
1209
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Action", value: record.action }),
|
|
1210
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Faire product token", value: record.product_token || "—", mono: true }),
|
|
1211
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "State", value: record.product_state || "—" }),
|
|
1212
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Published", value: record.published ? "Yes" : "No" }),
|
|
1213
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1214
|
+
Detail,
|
|
1215
|
+
{
|
|
1216
|
+
label: "Synced at",
|
|
1217
|
+
value: record.synced_at ? new Date(record.synced_at).toLocaleString() : "—"
|
|
1218
|
+
}
|
|
1219
|
+
)
|
|
1220
|
+
] }),
|
|
1221
|
+
warnings.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-ui-border-base flex flex-col gap-2 rounded-lg border p-4", children: [
|
|
1222
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Issues" }),
|
|
1223
|
+
warnings.map((w, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-tag-red-text text-sm", children: w }, i))
|
|
1224
|
+
] })
|
|
1225
|
+
] })
|
|
1226
|
+
] }),
|
|
1227
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
|
|
1228
|
+
] });
|
|
1229
|
+
};
|
|
1230
|
+
const Detail = ({
|
|
1231
|
+
label,
|
|
1232
|
+
value,
|
|
1233
|
+
mono
|
|
1234
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
1235
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: label }),
|
|
1236
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: mono ? "font-mono text-xs" : "", children: value })
|
|
1237
|
+
] });
|
|
1238
|
+
const handle = {
|
|
1239
|
+
breadcrumb: () => "Sync record"
|
|
1240
|
+
};
|
|
1082
1241
|
const EXCHANGED_PREFIX = "faire:oauth:exchanged:";
|
|
1083
1242
|
const FaireOauthCallback = () => {
|
|
1084
1243
|
const [params] = reactRouterDom.useSearchParams();
|
|
@@ -1157,21 +1316,23 @@ const routeModule = {
|
|
|
1157
1316
|
{
|
|
1158
1317
|
Component: FaireSettingsPage,
|
|
1159
1318
|
path: "/settings/faire",
|
|
1160
|
-
handle: handle$3
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1319
|
+
handle: handle$3,
|
|
1320
|
+
children: [
|
|
1321
|
+
{
|
|
1322
|
+
Component: FaireBulkPage,
|
|
1323
|
+
path: "/settings/faire/bulk",
|
|
1324
|
+
handle: handle$2
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
Component: FaireSyncSettingsDrawer,
|
|
1328
|
+
path: "/settings/faire/settings",
|
|
1329
|
+
handle: handle$1
|
|
1330
|
+
}
|
|
1331
|
+
]
|
|
1166
1332
|
},
|
|
1167
1333
|
{
|
|
1168
1334
|
Component: FaireSyncDetailPage,
|
|
1169
1335
|
path: "/settings/faire/:id",
|
|
1170
|
-
handle: handle$1
|
|
1171
|
-
},
|
|
1172
|
-
{
|
|
1173
|
-
Component: FaireSyncSettingsDrawer,
|
|
1174
|
-
path: "/settings/faire/settings",
|
|
1175
1336
|
handle
|
|
1176
1337
|
},
|
|
1177
1338
|
{
|