@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.
@@ -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 navigate2 = reactRouterDom.useNavigate();
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
- navigate2(`/settings/faire/${row.id}`);
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: () => navigate2("/settings/faire/settings"),
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 BulkOperations = ({ connected }) => {
415
- var _a, _b, _c, _d, _e, _f;
416
- const [productIds, setProductIds] = react.useState("");
417
- const [pushBatch, setPushBatch] = react.useState(null);
418
- const [pullBatch, setPullBatch] = react.useState(null);
419
- const pushMutation = reactQuery.useMutation({
420
- mutationFn: (ids) => faireApi.syncBulk(ids),
421
- onSuccess: (res) => {
422
- setPushBatch(res.batch_id);
423
- ui.toast.success("Bulk product sync started", {
424
- description: "Running in the background. Polling progress…"
425
- });
426
- },
427
- onError: (err) => ui.toast.error("Failed to start bulk sync", { description: err.message })
428
- });
429
- const pullMutation = reactQuery.useMutation({
430
- mutationFn: () => faireApi.ingestOrders(),
431
- onSuccess: (res) => {
432
- setPullBatch(res.batch_id);
433
- ui.toast.success("Faire order pull started", {
434
- description: "Running in the background. Polling progress…"
435
- });
436
- },
437
- onError: (err) => ui.toast.error("Failed to start order pull", { description: err.message })
438
- });
439
- const pushStatus = reactQuery.useQuery({
440
- queryKey: ["faire", "bulk", pushBatch],
441
- enabled: !!pushBatch,
442
- refetchInterval: (q) => {
443
- var _a2, _b2;
444
- return ((_b2 = (_a2 = q.state.data) == null ? void 0 : _a2.progress) == null ? void 0 : _b2.finished) ? false : 3e3;
445
- },
446
- queryFn: () => faireApi.bulkStatus(pushBatch)
447
- });
448
- const pullStatus = reactQuery.useQuery({
449
- queryKey: ["faire", "ingest", pullBatch],
450
- enabled: !!pullBatch,
451
- refetchInterval: (q) => {
452
- var _a2, _b2;
453
- return ((_b2 = (_a2 = q.state.data) == null ? void 0 : _a2.progress) == null ? void 0 : _b2.finished) ? false : 3e3;
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
- return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
468
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4", children: [
469
- /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { children: "Bulk operations" }),
470
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", size: "small", children: "Push many products to Faire, or pull wholesale orders from Faire into Medusa. Both run as long-running background workflows." })
471
- ] }),
472
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-6 py-4 flex flex-col gap-6", children: [
473
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-3", children: [
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.Label, { children: "Push products to Faire" }),
476
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { className: "text-ui-fg-subtle", size: "small", children: "Comma- or line-separated Medusa product IDs." })
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
- "textarea",
521
+ Detail,
480
522
  {
481
- className: "border-ui-border-base rounded-lg border px-3 py-2 text-sm min-h-[72px]",
482
- placeholder: "prod_01..., prod_02...",
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 BulkProgress = ({
536
+ const Detail = ({
550
537
  label,
551
- progress,
552
- status
553
- }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
554
- /* @__PURE__ */ jsxRuntime.jsxs(ui.StatusBadge, { color: progress.finished ? status === "failed" ? "red" : "green" : "orange", children: [
555
- label,
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 config = adminSdk.defineRouteConfig({
567
- label: "Faire",
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 navigate2 = reactRouterDom.useNavigate();
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
- navigate2(prev, { replace: true });
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(ui.Toaster, {})
815
- ] });
816
- };
817
- const handle$2 = {
818
- breadcrumb: () => "Bulk sync"
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
- label: "Synced at",
915
- value: record.synced_at ? new Date(record.synced_at).toLocaleString() : "—"
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
- warnings.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "divide-y p-0", children: [
921
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Issues" }) }),
922
- /* @__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)) })
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: () => "Sync record"
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 navigate2 = reactRouterDom.useNavigate();
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
- navigate2(PARENT);
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 navigate2 = reactRouterDom.useNavigate();
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
- navigate2("/settings/faire", { replace: true });
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) navigate2("/settings/faire", { replace: true });
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
- navigate2("/settings/faire", { replace: true });
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, navigate2]);
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: () => navigate2("/settings/faire", { replace: true }),
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: FaireBulkPage,
1206
- path: "/settings/faire/bulk",
1113
+ Component: FaireSyncDetailPage,
1114
+ path: "/settings/faire/:id",
1207
1115
  handle: handle$2
1208
1116
  },
1209
1117
  {
1210
- Component: FaireSyncDetailPage,
1211
- path: "/settings/faire/:id",
1118
+ Component: FaireBulkPage,
1119
+ path: "/settings/faire/bulk",
1212
1120
  handle: handle$1
1213
1121
  },
1214
1122
  {