@jytextiles/medusa-plugin-faire-store-sync 0.2.2 → 0.2.4
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/admin/index.js +189 -281
- package/.medusa/server/src/admin/index.mjs +190 -282
- package/.medusa/server/src/lib/crypto.js +84 -0
- package/.medusa/server/src/lib/faire-client.js +25 -1
- package/.medusa/server/src/modules/faire-sync/migrations/Migration20260709154411.js +18 -0
- package/.medusa/server/src/modules/faire-sync/service.js +64 -13
- package/README.md +21 -19
- package/package.json +1 -1
- package/src/admin/routes/settings/faire/bulk/page.tsx +55 -0
- package/src/admin/routes/settings/faire/page.tsx +7 -172
- package/src/lib/crypto.ts +84 -0
- package/src/lib/faire-client.ts +24 -0
- package/src/modules/faire-sync/migrations/.snapshot-medusa-faire-sync.json +6 -237
- package/src/modules/faire-sync/migrations/Migration20260709154411.ts +19 -0
- package/src/modules/faire-sync/service.ts +70 -12
- package/.medusa/server/src/modules/faire-sync/models/faire-webhook-event.js +0 -21
- package/src/modules/faire-sync/models/faire-webhook-event.ts +0 -20
|
@@ -232,7 +232,7 @@ const PAGE_SIZE$1 = 10;
|
|
|
232
232
|
const STATUS_KEY$1 = ["faire", "status"];
|
|
233
233
|
const FaireSettingsPage = () => {
|
|
234
234
|
var _a, _b, _c;
|
|
235
|
-
const
|
|
235
|
+
const navigate = reactRouterDom.useNavigate();
|
|
236
236
|
const queryClient = reactQuery.useQueryClient();
|
|
237
237
|
const [pagination, setPagination] = react.useState({
|
|
238
238
|
pageIndex: 0,
|
|
@@ -300,7 +300,7 @@ const FaireSettingsPage = () => {
|
|
|
300
300
|
rowCount: ((_b = syncsQuery.data) == null ? void 0 : _b.count) ?? 0,
|
|
301
301
|
getRowId: (row) => row.id,
|
|
302
302
|
onRowClick: (_, row) => {
|
|
303
|
-
|
|
303
|
+
navigate(`/settings/faire/${row.id}`);
|
|
304
304
|
},
|
|
305
305
|
isLoading: syncsQuery.isLoading,
|
|
306
306
|
filters,
|
|
@@ -343,7 +343,16 @@ const FaireSettingsPage = () => {
|
|
|
343
343
|
{
|
|
344
344
|
size: "small",
|
|
345
345
|
variant: "secondary",
|
|
346
|
-
onClick: () =>
|
|
346
|
+
onClick: () => navigate("/settings/faire/bulk"),
|
|
347
|
+
children: "Bulk sync"
|
|
348
|
+
}
|
|
349
|
+
),
|
|
350
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
351
|
+
ui.Button,
|
|
352
|
+
{
|
|
353
|
+
size: "small",
|
|
354
|
+
variant: "secondary",
|
|
355
|
+
onClick: () => navigate("/settings/faire/settings"),
|
|
347
356
|
children: "Sync settings"
|
|
348
357
|
}
|
|
349
358
|
),
|
|
@@ -380,7 +389,6 @@ const FaireSettingsPage = () => {
|
|
|
380
389
|
)
|
|
381
390
|
] })
|
|
382
391
|
] }),
|
|
383
|
-
/* @__PURE__ */ jsxRuntime.jsx(BulkOperations, { connected }),
|
|
384
392
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "divide-y p-0", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
|
|
385
393
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable.Toolbar, { className: "flex flex-col md:flex-row justify-between gap-y-4 px-6 py-4", children: [
|
|
386
394
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
@@ -411,167 +419,133 @@ const InfoField = ({ label, value }) => /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
|
411
419
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: label }),
|
|
412
420
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: value })
|
|
413
421
|
] });
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
},
|
|
455
|
-
queryFn: () => faireApi.ingestStatus(pullBatch)
|
|
456
|
-
});
|
|
457
|
-
const pushProgress = (_a = pushStatus.data) == null ? void 0 : _a.progress;
|
|
458
|
-
const pullProgress = (_b = pullStatus.data) == null ? void 0 : _b.progress;
|
|
459
|
-
const handlePush = () => {
|
|
460
|
-
const ids = productIds.split(/[\s,]+/).map((s) => s.trim()).filter(Boolean);
|
|
461
|
-
if (!ids.length) {
|
|
462
|
-
ui.toast.error("Enter at least one product id");
|
|
463
|
-
return;
|
|
422
|
+
const config = adminSdk.defineRouteConfig({
|
|
423
|
+
label: "Faire",
|
|
424
|
+
icon: icons.BuildingStorefront
|
|
425
|
+
});
|
|
426
|
+
const handle$3 = {
|
|
427
|
+
breadcrumb: () => "Faire Sync"
|
|
428
|
+
};
|
|
429
|
+
const STATUS_COLORS = {
|
|
430
|
+
success: "green",
|
|
431
|
+
failed: "red",
|
|
432
|
+
draft: "orange",
|
|
433
|
+
pending: "grey",
|
|
434
|
+
syncing: "grey"
|
|
435
|
+
};
|
|
436
|
+
const FaireSyncDetailPage = () => {
|
|
437
|
+
const { id } = reactRouterDom.useParams();
|
|
438
|
+
const navigate = reactRouterDom.useNavigate();
|
|
439
|
+
const [record, setRecord] = react.useState(null);
|
|
440
|
+
const [loading, setLoading] = react.useState(true);
|
|
441
|
+
const [retrying, setRetrying] = react.useState(false);
|
|
442
|
+
const [error, setError] = react.useState(null);
|
|
443
|
+
react.useEffect(() => {
|
|
444
|
+
if (!id) return;
|
|
445
|
+
setLoading(true);
|
|
446
|
+
faireApi.getSync(id).then((res) => {
|
|
447
|
+
setRecord(res.sync || res);
|
|
448
|
+
}).catch((err) => setError(err.message || "Failed to load sync record")).finally(() => setLoading(false));
|
|
449
|
+
}, [id]);
|
|
450
|
+
const handleRetry = async () => {
|
|
451
|
+
if (!record) return;
|
|
452
|
+
setRetrying(true);
|
|
453
|
+
setError(null);
|
|
454
|
+
try {
|
|
455
|
+
await faireApi.retrySync(record.id);
|
|
456
|
+
const updated = await faireApi.getSync(record.id).catch(() => null);
|
|
457
|
+
if (updated) setRecord(updated.sync || updated);
|
|
458
|
+
} catch (err) {
|
|
459
|
+
setError(err.message || "Retry failed");
|
|
460
|
+
} finally {
|
|
461
|
+
setRetrying(false);
|
|
464
462
|
}
|
|
465
|
-
pushMutation.mutate(ids);
|
|
466
463
|
};
|
|
467
|
-
|
|
468
|
-
/* @__PURE__ */ jsxRuntime.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
/* @__PURE__ */ jsxRuntime.
|
|
464
|
+
if (loading) {
|
|
465
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" }) });
|
|
466
|
+
}
|
|
467
|
+
if (!record) {
|
|
468
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-6 flex flex-col gap-4", children: [
|
|
469
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Sync record not found" }),
|
|
470
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", onClick: () => navigate("/settings/faire"), children: "Back to Faire settings" })
|
|
471
|
+
] });
|
|
472
|
+
}
|
|
473
|
+
const warnings = record.error_message ? record.error_message.split(" | ") : [];
|
|
474
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
475
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-tag-red-text", children: error }) }),
|
|
476
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
477
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
474
478
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
475
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.
|
|
476
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle
|
|
479
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Sync record" }),
|
|
480
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle font-mono text-xs", children: record.id })
|
|
477
481
|
] }),
|
|
482
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
483
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: STATUS_COLORS[record.status] || "grey", children: record.status }),
|
|
484
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
485
|
+
/* @__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, {}) }) }),
|
|
486
|
+
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { align: "end", children: [
|
|
487
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
488
|
+
ui.DropdownMenu.Item,
|
|
489
|
+
{
|
|
490
|
+
className: "gap-x-2",
|
|
491
|
+
onClick: handleRetry,
|
|
492
|
+
disabled: retrying,
|
|
493
|
+
children: [
|
|
494
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowPath, { className: "text-ui-fg-subtle" }),
|
|
495
|
+
retrying ? "Re-syncing…" : "Re-sync product"
|
|
496
|
+
]
|
|
497
|
+
}
|
|
498
|
+
),
|
|
499
|
+
record.product_url && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
500
|
+
ui.DropdownMenu.Item,
|
|
501
|
+
{
|
|
502
|
+
className: "gap-x-2",
|
|
503
|
+
onClick: () => window.open(record.product_url, "_blank"),
|
|
504
|
+
children: [
|
|
505
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, { className: "text-ui-fg-subtle" }),
|
|
506
|
+
"Open on Faire"
|
|
507
|
+
]
|
|
508
|
+
}
|
|
509
|
+
)
|
|
510
|
+
] })
|
|
511
|
+
] })
|
|
512
|
+
] })
|
|
513
|
+
] }),
|
|
514
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 grid grid-cols-2 gap-4", children: [
|
|
515
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Product", value: record.product_id, mono: true }),
|
|
516
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Action", value: record.action }),
|
|
517
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Faire product token", value: record.product_token || "—", mono: true }),
|
|
518
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "State", value: record.product_state || "—" }),
|
|
519
|
+
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Published", value: record.published ? "Yes" : "No" }),
|
|
478
520
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
479
|
-
|
|
521
|
+
Detail,
|
|
480
522
|
{
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
value: productIds,
|
|
484
|
-
onChange: (e) => setProductIds(e.target.value),
|
|
485
|
-
disabled: !connected || pushMutation.isPending
|
|
523
|
+
label: "Synced at",
|
|
524
|
+
value: record.synced_at ? new Date(record.synced_at).toLocaleString() : "—"
|
|
486
525
|
}
|
|
487
|
-
)
|
|
488
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
489
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
490
|
-
ui.Button,
|
|
491
|
-
{
|
|
492
|
-
size: "small",
|
|
493
|
-
variant: "secondary",
|
|
494
|
-
disabled: !connected,
|
|
495
|
-
onClick: () => navigate("/settings/faire/bulk"),
|
|
496
|
-
children: "Select products"
|
|
497
|
-
}
|
|
498
|
-
),
|
|
499
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
500
|
-
ui.Button,
|
|
501
|
-
{
|
|
502
|
-
size: "small",
|
|
503
|
-
onClick: handlePush,
|
|
504
|
-
disabled: !connected || pushMutation.isPending,
|
|
505
|
-
isLoading: pushMutation.isPending,
|
|
506
|
-
children: "Start bulk push"
|
|
507
|
-
}
|
|
508
|
-
),
|
|
509
|
-
pushBatch && pushProgress && /* @__PURE__ */ jsxRuntime.jsx(
|
|
510
|
-
BulkProgress,
|
|
511
|
-
{
|
|
512
|
-
label: "Push",
|
|
513
|
-
progress: pushProgress,
|
|
514
|
-
status: (_d = (_c = pushStatus.data) == null ? void 0 : _c.batch) == null ? void 0 : _d.status
|
|
515
|
-
}
|
|
516
|
-
)
|
|
517
|
-
] })
|
|
518
|
-
] }),
|
|
519
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
520
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
521
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: "Pull Faire orders into Medusa" }),
|
|
522
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", size: "small", children: "Ingests all available Faire orders as Medusa orders (idempotent)." })
|
|
523
|
-
] }),
|
|
524
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
525
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
526
|
-
ui.Button,
|
|
527
|
-
{
|
|
528
|
-
size: "small",
|
|
529
|
-
variant: "secondary",
|
|
530
|
-
onClick: () => pullMutation.mutate(),
|
|
531
|
-
disabled: !connected || pullMutation.isPending,
|
|
532
|
-
isLoading: pullMutation.isPending,
|
|
533
|
-
children: "Start order pull"
|
|
534
|
-
}
|
|
535
|
-
),
|
|
536
|
-
pullBatch && pullProgress && /* @__PURE__ */ jsxRuntime.jsx(
|
|
537
|
-
BulkProgress,
|
|
538
|
-
{
|
|
539
|
-
label: "Pull",
|
|
540
|
-
progress: pullProgress,
|
|
541
|
-
status: (_f = (_e = pullStatus.data) == null ? void 0 : _e.batch) == null ? void 0 : _f.status
|
|
542
|
-
}
|
|
543
|
-
)
|
|
544
|
-
] })
|
|
526
|
+
)
|
|
545
527
|
] })
|
|
546
|
-
] })
|
|
528
|
+
] }),
|
|
529
|
+
warnings.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
530
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Issues" }) }),
|
|
531
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4 flex flex-col gap-2", children: warnings.map((w, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-tag-red-text text-sm", children: w }, i)) })
|
|
532
|
+
] }),
|
|
533
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
|
|
547
534
|
] });
|
|
548
535
|
};
|
|
549
|
-
const
|
|
536
|
+
const Detail = ({
|
|
550
537
|
label,
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex
|
|
554
|
-
/* @__PURE__ */ jsxRuntime.
|
|
555
|
-
|
|
556
|
-
": ",
|
|
557
|
-
progress.done,
|
|
558
|
-
"/",
|
|
559
|
-
progress.total || "?",
|
|
560
|
-
" (",
|
|
561
|
-
progress.pct,
|
|
562
|
-
"%)"
|
|
563
|
-
] }),
|
|
564
|
-
progress.finished && /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", size: "small", children: status === "failed" ? "Completed with errors" : "Done" })
|
|
538
|
+
value,
|
|
539
|
+
mono
|
|
540
|
+
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
541
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: label }),
|
|
542
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: mono ? "font-mono text-xs" : "", children: value })
|
|
565
543
|
] });
|
|
566
|
-
const
|
|
567
|
-
|
|
568
|
-
icon: icons.BuildingStorefront
|
|
569
|
-
});
|
|
570
|
-
const handle$3 = {
|
|
571
|
-
breadcrumb: () => "Faire Sync"
|
|
544
|
+
const handle$2 = {
|
|
545
|
+
breadcrumb: () => "Sync record"
|
|
572
546
|
};
|
|
573
547
|
const Root = ({ prev = "..", children }) => {
|
|
574
|
-
const
|
|
548
|
+
const navigate = reactRouterDom.useNavigate();
|
|
575
549
|
const [open, setOpen] = react.useState(false);
|
|
576
550
|
react.useEffect(() => {
|
|
577
551
|
setOpen(true);
|
|
@@ -580,7 +554,7 @@ const Root = ({ prev = "..", children }) => {
|
|
|
580
554
|
const handleOpenChange = (next) => {
|
|
581
555
|
if (!next) {
|
|
582
556
|
document.body.style.pointerEvents = "auto";
|
|
583
|
-
|
|
557
|
+
navigate(prev, { replace: true });
|
|
584
558
|
return;
|
|
585
559
|
}
|
|
586
560
|
setOpen(next);
|
|
@@ -643,7 +617,7 @@ const listProducts = (query) => sdk.admin.product.list({
|
|
|
643
617
|
...query
|
|
644
618
|
});
|
|
645
619
|
const FaireBulkPage = () => {
|
|
646
|
-
var _a, _b, _c, _d;
|
|
620
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
647
621
|
const queryClient = reactQuery.useQueryClient();
|
|
648
622
|
const [pagination, setPagination] = react.useState({
|
|
649
623
|
pageIndex: 0,
|
|
@@ -713,6 +687,27 @@ const FaireBulkPage = () => {
|
|
|
713
687
|
},
|
|
714
688
|
onError: (err) => ui.toast.error("Failed to start bulk sync", { description: err.message })
|
|
715
689
|
});
|
|
690
|
+
const [pullBatch, setPullBatch] = react.useState(null);
|
|
691
|
+
const pullMutation = reactQuery.useMutation({
|
|
692
|
+
mutationFn: () => faireApi.ingestOrders(),
|
|
693
|
+
onSuccess: (res) => {
|
|
694
|
+
setPullBatch(res.batch_id);
|
|
695
|
+
ui.toast.success("Faire order pull started", {
|
|
696
|
+
description: "Running in the background. Polling progress…"
|
|
697
|
+
});
|
|
698
|
+
},
|
|
699
|
+
onError: (err) => ui.toast.error("Failed to start order pull", { description: err.message })
|
|
700
|
+
});
|
|
701
|
+
const pullStatus = reactQuery.useQuery({
|
|
702
|
+
queryKey: ["faire", "ingest", pullBatch],
|
|
703
|
+
enabled: !!pullBatch,
|
|
704
|
+
refetchInterval: (q) => {
|
|
705
|
+
var _a2, _b2;
|
|
706
|
+
return ((_b2 = (_a2 = q.state.data) == null ? void 0 : _a2.progress) == null ? void 0 : _b2.finished) ? false : 3e3;
|
|
707
|
+
},
|
|
708
|
+
queryFn: () => faireApi.ingestStatus(pullBatch)
|
|
709
|
+
});
|
|
710
|
+
const pullProgress = (_e = pullStatus.data) == null ? void 0 : _e.progress;
|
|
716
711
|
const columns = useBulkProductColumns();
|
|
717
712
|
const commands = react.useMemo(
|
|
718
713
|
() => [
|
|
@@ -811,129 +806,42 @@ const FaireBulkPage = () => {
|
|
|
811
806
|
productsQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-3 p-6", children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-10 w-full" }, i)) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
|
|
812
807
|
/* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Pagination, {})
|
|
813
808
|
] }) }),
|
|
814
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
815
|
-
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
};
|
|
820
|
-
const STATUS_COLORS = {
|
|
821
|
-
success: "green",
|
|
822
|
-
failed: "red",
|
|
823
|
-
draft: "orange",
|
|
824
|
-
pending: "grey",
|
|
825
|
-
syncing: "grey"
|
|
826
|
-
};
|
|
827
|
-
const FaireSyncDetailPage = () => {
|
|
828
|
-
const { id } = reactRouterDom.useParams();
|
|
829
|
-
const navigate2 = reactRouterDom.useNavigate();
|
|
830
|
-
const [record, setRecord] = react.useState(null);
|
|
831
|
-
const [loading, setLoading] = react.useState(true);
|
|
832
|
-
const [retrying, setRetrying] = react.useState(false);
|
|
833
|
-
const [error, setError] = react.useState(null);
|
|
834
|
-
react.useEffect(() => {
|
|
835
|
-
if (!id) return;
|
|
836
|
-
setLoading(true);
|
|
837
|
-
faireApi.getSync(id).then((res) => {
|
|
838
|
-
setRecord(res.sync || res);
|
|
839
|
-
}).catch((err) => setError(err.message || "Failed to load sync record")).finally(() => setLoading(false));
|
|
840
|
-
}, [id]);
|
|
841
|
-
const handleRetry = async () => {
|
|
842
|
-
if (!record) return;
|
|
843
|
-
setRetrying(true);
|
|
844
|
-
setError(null);
|
|
845
|
-
try {
|
|
846
|
-
await faireApi.retrySync(record.id);
|
|
847
|
-
const updated = await faireApi.getSync(record.id).catch(() => null);
|
|
848
|
-
if (updated) setRecord(updated.sync || updated);
|
|
849
|
-
} catch (err) {
|
|
850
|
-
setError(err.message || "Retry failed");
|
|
851
|
-
} finally {
|
|
852
|
-
setRetrying(false);
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
if (loading) {
|
|
856
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-6", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Skeleton, { className: "h-7 w-12 rounded-md" }) });
|
|
857
|
-
}
|
|
858
|
-
if (!record) {
|
|
859
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "p-6 flex flex-col gap-4", children: [
|
|
860
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Sync record not found" }),
|
|
861
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Button, { size: "small", variant: "secondary", onClick: () => navigate2("/settings/faire"), children: "Back to Faire settings" })
|
|
862
|
-
] });
|
|
863
|
-
}
|
|
864
|
-
const warnings = record.error_message ? record.error_message.split(" | ") : [];
|
|
865
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
866
|
-
error && /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-tag-red-text", children: error }) }),
|
|
867
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
|
|
868
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
|
|
869
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
870
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Sync record" }),
|
|
871
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle font-mono text-xs", children: record.id })
|
|
872
|
-
] }),
|
|
873
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
874
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: STATUS_COLORS[record.status] || "grey", children: record.status }),
|
|
875
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
876
|
-
/* @__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, {}) }) }),
|
|
877
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { align: "end", children: [
|
|
878
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
879
|
-
ui.DropdownMenu.Item,
|
|
880
|
-
{
|
|
881
|
-
className: "gap-x-2",
|
|
882
|
-
onClick: handleRetry,
|
|
883
|
-
disabled: retrying,
|
|
884
|
-
children: [
|
|
885
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowPath, { className: "text-ui-fg-subtle" }),
|
|
886
|
-
retrying ? "Re-syncing…" : "Re-sync product"
|
|
887
|
-
]
|
|
888
|
-
}
|
|
889
|
-
),
|
|
890
|
-
record.product_url && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
891
|
-
ui.DropdownMenu.Item,
|
|
892
|
-
{
|
|
893
|
-
className: "gap-x-2",
|
|
894
|
-
onClick: () => window.open(record.product_url, "_blank"),
|
|
895
|
-
children: [
|
|
896
|
-
/* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUpRightOnBox, { className: "text-ui-fg-subtle" }),
|
|
897
|
-
"Open on Faire"
|
|
898
|
-
]
|
|
899
|
-
}
|
|
900
|
-
)
|
|
901
|
-
] })
|
|
902
|
-
] })
|
|
903
|
-
] })
|
|
904
|
-
] }),
|
|
905
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 grid grid-cols-2 gap-4", children: [
|
|
906
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Product", value: record.product_id, mono: true }),
|
|
907
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Action", value: record.action }),
|
|
908
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Faire product token", value: record.product_token || "—", mono: true }),
|
|
909
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "State", value: record.product_state || "—" }),
|
|
910
|
-
/* @__PURE__ */ jsxRuntime.jsx(Detail, { label: "Published", value: record.published ? "Yes" : "No" }),
|
|
911
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
912
|
-
Detail,
|
|
809
|
+
/* @__PURE__ */ jsxRuntime.jsx(RouteFocusModal.Footer, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center justify-between gap-x-3", children: [
|
|
810
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
|
|
811
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Pull Faire orders into Medusa (idempotent)" }),
|
|
812
|
+
pullBatch && pullProgress && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
813
|
+
ui.StatusBadge,
|
|
913
814
|
{
|
|
914
|
-
|
|
915
|
-
|
|
815
|
+
color: pullProgress.finished ? ((_g = (_f = pullStatus.data) == null ? void 0 : _f.batch) == null ? void 0 : _g.status) === "failed" ? "red" : "green" : "orange",
|
|
816
|
+
children: [
|
|
817
|
+
"Pull: ",
|
|
818
|
+
pullProgress.done,
|
|
819
|
+
"/",
|
|
820
|
+
pullProgress.total || "?",
|
|
821
|
+
" (",
|
|
822
|
+
pullProgress.pct,
|
|
823
|
+
"%)"
|
|
824
|
+
]
|
|
916
825
|
}
|
|
917
826
|
)
|
|
918
|
-
] })
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
827
|
+
] }),
|
|
828
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
829
|
+
ui.Button,
|
|
830
|
+
{
|
|
831
|
+
size: "small",
|
|
832
|
+
variant: "secondary",
|
|
833
|
+
onClick: () => pullMutation.mutate(),
|
|
834
|
+
disabled: !connected || pullMutation.isPending,
|
|
835
|
+
isLoading: pullMutation.isPending,
|
|
836
|
+
children: "Pull orders"
|
|
837
|
+
}
|
|
838
|
+
)
|
|
839
|
+
] }) }),
|
|
924
840
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Toaster, {})
|
|
925
841
|
] });
|
|
926
842
|
};
|
|
927
|
-
const Detail = ({
|
|
928
|
-
label,
|
|
929
|
-
value,
|
|
930
|
-
mono
|
|
931
|
-
}) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1", children: [
|
|
932
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Label, { children: label }),
|
|
933
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: mono ? "font-mono text-xs" : "", children: value })
|
|
934
|
-
] });
|
|
935
843
|
const handle$1 = {
|
|
936
|
-
breadcrumb: () => "
|
|
844
|
+
breadcrumb: () => "Bulk sync"
|
|
937
845
|
};
|
|
938
846
|
const STATUS_KEY = ["faire", "status"];
|
|
939
847
|
const PARENT = "/settings/faire";
|
|
@@ -956,13 +864,13 @@ const cleanSettings = (form) => {
|
|
|
956
864
|
return out;
|
|
957
865
|
};
|
|
958
866
|
const FaireSyncSettingsDrawer = () => {
|
|
959
|
-
const
|
|
867
|
+
const navigate = reactRouterDom.useNavigate();
|
|
960
868
|
const queryClient = reactQuery.useQueryClient();
|
|
961
869
|
const [open, setOpen] = react.useState(true);
|
|
962
870
|
const [form, setForm] = react.useState({});
|
|
963
871
|
const close = () => {
|
|
964
872
|
setOpen(false);
|
|
965
|
-
|
|
873
|
+
navigate(PARENT);
|
|
966
874
|
};
|
|
967
875
|
const statusQuery = reactQuery.useQuery({
|
|
968
876
|
queryKey: STATUS_KEY,
|
|
@@ -1124,7 +1032,7 @@ const handle = {
|
|
|
1124
1032
|
const EXCHANGED_PREFIX = "faire:oauth:exchanged:";
|
|
1125
1033
|
const FaireOauthCallback = () => {
|
|
1126
1034
|
const [params] = reactRouterDom.useSearchParams();
|
|
1127
|
-
const
|
|
1035
|
+
const navigate = reactRouterDom.useNavigate();
|
|
1128
1036
|
const [error, setError] = react.useState(null);
|
|
1129
1037
|
const [exchanging, setExchanging] = react.useState(true);
|
|
1130
1038
|
react.useEffect(() => {
|
|
@@ -1144,7 +1052,7 @@ const FaireOauthCallback = () => {
|
|
|
1144
1052
|
return;
|
|
1145
1053
|
}
|
|
1146
1054
|
if (sessionStorage.getItem(EXCHANGED_PREFIX + code)) {
|
|
1147
|
-
|
|
1055
|
+
navigate("/settings/faire", { replace: true });
|
|
1148
1056
|
return;
|
|
1149
1057
|
}
|
|
1150
1058
|
sessionStorage.setItem(EXCHANGED_PREFIX + code, "1");
|
|
@@ -1152,13 +1060,13 @@ const FaireOauthCallback = () => {
|
|
|
1152
1060
|
const run = async () => {
|
|
1153
1061
|
try {
|
|
1154
1062
|
await faireApi.callback(code, state);
|
|
1155
|
-
if (!cancelled)
|
|
1063
|
+
if (!cancelled) navigate("/settings/faire", { replace: true });
|
|
1156
1064
|
} catch (err) {
|
|
1157
1065
|
if (!cancelled) {
|
|
1158
1066
|
try {
|
|
1159
1067
|
const status = await faireApi.status();
|
|
1160
1068
|
if (status == null ? void 0 : status.connected) {
|
|
1161
|
-
|
|
1069
|
+
navigate("/settings/faire", { replace: true });
|
|
1162
1070
|
return;
|
|
1163
1071
|
}
|
|
1164
1072
|
} catch {
|
|
@@ -1172,7 +1080,7 @@ const FaireOauthCallback = () => {
|
|
|
1172
1080
|
return () => {
|
|
1173
1081
|
cancelled = true;
|
|
1174
1082
|
};
|
|
1175
|
-
}, [params,
|
|
1083
|
+
}, [params, navigate]);
|
|
1176
1084
|
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex flex-col items-center gap-4 p-8", children: [
|
|
1177
1085
|
/* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h1", children: "Connecting Faire…" }),
|
|
1178
1086
|
error ? /* @__PURE__ */ jsxRuntime.jsx(ui.Alert, { variant: "error", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: error }) }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { children: exchanging ? "Completing authorization, please wait…" : "Redirecting…" }),
|
|
@@ -1181,7 +1089,7 @@ const FaireOauthCallback = () => {
|
|
|
1181
1089
|
{
|
|
1182
1090
|
size: "small",
|
|
1183
1091
|
variant: "secondary",
|
|
1184
|
-
onClick: () =>
|
|
1092
|
+
onClick: () => navigate("/settings/faire", { replace: true }),
|
|
1185
1093
|
children: "Back to Faire settings"
|
|
1186
1094
|
}
|
|
1187
1095
|
)
|
|
@@ -1202,13 +1110,13 @@ const routeModule = {
|
|
|
1202
1110
|
handle: handle$3
|
|
1203
1111
|
},
|
|
1204
1112
|
{
|
|
1205
|
-
Component:
|
|
1206
|
-
path: "/settings/faire
|
|
1113
|
+
Component: FaireSyncDetailPage,
|
|
1114
|
+
path: "/settings/faire/:id",
|
|
1207
1115
|
handle: handle$2
|
|
1208
1116
|
},
|
|
1209
1117
|
{
|
|
1210
|
-
Component:
|
|
1211
|
-
path: "/settings/faire
|
|
1118
|
+
Component: FaireBulkPage,
|
|
1119
|
+
path: "/settings/faire/bulk",
|
|
1212
1120
|
handle: handle$1
|
|
1213
1121
|
},
|
|
1214
1122
|
{
|