@jytextiles/medusa-plugin-faire-store-sync 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
2
  import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
3
- import { Container, Heading, Text, StatusBadge, Alert, Tooltip, Button, createDataTableColumnHelper, toast, createDataTableFilterHelper, useDataTable, Skeleton, DataTable, Toaster, Label, FocusModal, Input, Badge, DropdownMenu, IconButton, Drawer, Switch, clx } from "@medusajs/ui";
3
+ import { Container, Heading, Text, StatusBadge, Alert, Tooltip, Button, createDataTableColumnHelper, toast, createDataTableFilterHelper, useDataTable, Skeleton, DataTable, Toaster, Label, DropdownMenu, IconButton, FocusModal, Input, Badge, Drawer, Switch, clx } from "@medusajs/ui";
4
4
  import { useQueryClient, useQuery, useMutation, keepPreviousData } from "@tanstack/react-query";
5
5
  import Medusa from "@medusajs/js-sdk";
6
6
  import { BuildingStorefront, EllipsisHorizontal, ArrowPath, ArrowUpRightOnBox } from "@medusajs/icons";
@@ -229,7 +229,7 @@ const PAGE_SIZE$1 = 10;
229
229
  const STATUS_KEY$1 = ["faire", "status"];
230
230
  const FaireSettingsPage = () => {
231
231
  var _a, _b, _c;
232
- const navigate2 = useNavigate();
232
+ const navigate = useNavigate();
233
233
  const queryClient = useQueryClient();
234
234
  const [pagination, setPagination] = useState({
235
235
  pageIndex: 0,
@@ -297,7 +297,7 @@ const FaireSettingsPage = () => {
297
297
  rowCount: ((_b = syncsQuery.data) == null ? void 0 : _b.count) ?? 0,
298
298
  getRowId: (row) => row.id,
299
299
  onRowClick: (_, row) => {
300
- navigate2(`/settings/faire/${row.id}`);
300
+ navigate(`/settings/faire/${row.id}`);
301
301
  },
302
302
  isLoading: syncsQuery.isLoading,
303
303
  filters,
@@ -340,7 +340,16 @@ const FaireSettingsPage = () => {
340
340
  {
341
341
  size: "small",
342
342
  variant: "secondary",
343
- onClick: () => navigate2("/settings/faire/settings"),
343
+ onClick: () => navigate("/settings/faire/bulk"),
344
+ children: "Bulk sync"
345
+ }
346
+ ),
347
+ /* @__PURE__ */ jsx(
348
+ Button,
349
+ {
350
+ size: "small",
351
+ variant: "secondary",
352
+ onClick: () => navigate("/settings/faire/settings"),
344
353
  children: "Sync settings"
345
354
  }
346
355
  ),
@@ -377,7 +386,6 @@ const FaireSettingsPage = () => {
377
386
  )
378
387
  ] })
379
388
  ] }),
380
- /* @__PURE__ */ jsx(BulkOperations, { connected }),
381
389
  /* @__PURE__ */ jsx(Container, { className: "divide-y p-0", children: /* @__PURE__ */ jsxs(DataTable, { instance: table, children: [
382
390
  /* @__PURE__ */ jsxs(DataTable.Toolbar, { className: "flex flex-col md:flex-row justify-between gap-y-4 px-6 py-4", children: [
383
391
  /* @__PURE__ */ jsxs("div", { children: [
@@ -408,167 +416,133 @@ const InfoField = ({ label, value }) => /* @__PURE__ */ jsxs("div", { className:
408
416
  /* @__PURE__ */ jsx(Label, { children: label }),
409
417
  /* @__PURE__ */ jsx(Text, { children: value })
410
418
  ] });
411
- const BulkOperations = ({ connected }) => {
412
- var _a, _b, _c, _d, _e, _f;
413
- const [productIds, setProductIds] = useState("");
414
- const [pushBatch, setPushBatch] = useState(null);
415
- const [pullBatch, setPullBatch] = useState(null);
416
- const pushMutation = useMutation({
417
- mutationFn: (ids) => faireApi.syncBulk(ids),
418
- onSuccess: (res) => {
419
- setPushBatch(res.batch_id);
420
- toast.success("Bulk product sync started", {
421
- description: "Running in the background. Polling progress…"
422
- });
423
- },
424
- onError: (err) => toast.error("Failed to start bulk sync", { description: err.message })
425
- });
426
- const pullMutation = useMutation({
427
- mutationFn: () => faireApi.ingestOrders(),
428
- onSuccess: (res) => {
429
- setPullBatch(res.batch_id);
430
- toast.success("Faire order pull started", {
431
- description: "Running in the background. Polling progress…"
432
- });
433
- },
434
- onError: (err) => toast.error("Failed to start order pull", { description: err.message })
435
- });
436
- const pushStatus = useQuery({
437
- queryKey: ["faire", "bulk", pushBatch],
438
- enabled: !!pushBatch,
439
- refetchInterval: (q) => {
440
- var _a2, _b2;
441
- return ((_b2 = (_a2 = q.state.data) == null ? void 0 : _a2.progress) == null ? void 0 : _b2.finished) ? false : 3e3;
442
- },
443
- queryFn: () => faireApi.bulkStatus(pushBatch)
444
- });
445
- const pullStatus = useQuery({
446
- queryKey: ["faire", "ingest", pullBatch],
447
- enabled: !!pullBatch,
448
- refetchInterval: (q) => {
449
- var _a2, _b2;
450
- return ((_b2 = (_a2 = q.state.data) == null ? void 0 : _a2.progress) == null ? void 0 : _b2.finished) ? false : 3e3;
451
- },
452
- queryFn: () => faireApi.ingestStatus(pullBatch)
453
- });
454
- const pushProgress = (_a = pushStatus.data) == null ? void 0 : _a.progress;
455
- const pullProgress = (_b = pullStatus.data) == null ? void 0 : _b.progress;
456
- const handlePush = () => {
457
- const ids = productIds.split(/[\s,]+/).map((s) => s.trim()).filter(Boolean);
458
- if (!ids.length) {
459
- toast.error("Enter at least one product id");
460
- return;
419
+ const config = defineRouteConfig({
420
+ label: "Faire",
421
+ icon: BuildingStorefront
422
+ });
423
+ const handle$3 = {
424
+ breadcrumb: () => "Faire Sync"
425
+ };
426
+ const STATUS_COLORS = {
427
+ success: "green",
428
+ failed: "red",
429
+ draft: "orange",
430
+ pending: "grey",
431
+ syncing: "grey"
432
+ };
433
+ const FaireSyncDetailPage = () => {
434
+ const { id } = useParams();
435
+ const navigate = useNavigate();
436
+ const [record, setRecord] = useState(null);
437
+ const [loading, setLoading] = useState(true);
438
+ const [retrying, setRetrying] = useState(false);
439
+ const [error, setError] = useState(null);
440
+ useEffect(() => {
441
+ if (!id) return;
442
+ setLoading(true);
443
+ faireApi.getSync(id).then((res) => {
444
+ setRecord(res.sync || res);
445
+ }).catch((err) => setError(err.message || "Failed to load sync record")).finally(() => setLoading(false));
446
+ }, [id]);
447
+ const handleRetry = async () => {
448
+ if (!record) return;
449
+ setRetrying(true);
450
+ setError(null);
451
+ try {
452
+ await faireApi.retrySync(record.id);
453
+ const updated = await faireApi.getSync(record.id).catch(() => null);
454
+ if (updated) setRecord(updated.sync || updated);
455
+ } catch (err) {
456
+ setError(err.message || "Retry failed");
457
+ } finally {
458
+ setRetrying(false);
461
459
  }
462
- pushMutation.mutate(ids);
463
460
  };
464
- return /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
465
- /* @__PURE__ */ jsxs("div", { className: "px-6 py-4", children: [
466
- /* @__PURE__ */ jsx(Heading, { children: "Bulk operations" }),
467
- /* @__PURE__ */ jsx(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." })
468
- ] }),
469
- /* @__PURE__ */ jsxs("div", { className: "px-6 py-4 flex flex-col gap-6", children: [
470
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
461
+ if (loading) {
462
+ return /* @__PURE__ */ jsx(Container, { className: "p-6", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" }) });
463
+ }
464
+ if (!record) {
465
+ return /* @__PURE__ */ jsxs(Container, { className: "p-6 flex flex-col gap-4", children: [
466
+ /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Sync record not found" }),
467
+ /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", onClick: () => navigate("/settings/faire"), children: "Back to Faire settings" })
468
+ ] });
469
+ }
470
+ const warnings = record.error_message ? record.error_message.split(" | ") : [];
471
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
472
+ error && /* @__PURE__ */ jsx(Container, { className: "p-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-tag-red-text", children: error }) }),
473
+ /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
474
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
471
475
  /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
472
- /* @__PURE__ */ jsx(Label, { children: "Push products to Faire" }),
473
- /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", size: "small", children: "Comma- or line-separated Medusa product IDs." })
476
+ /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Sync record" }),
477
+ /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle font-mono text-xs", children: record.id })
474
478
  ] }),
479
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
480
+ /* @__PURE__ */ jsx(StatusBadge, { color: STATUS_COLORS[record.status] || "grey", children: record.status }),
481
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
482
+ /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { size: "small", variant: "transparent", disabled: retrying, children: /* @__PURE__ */ jsx(EllipsisHorizontal, {}) }) }),
483
+ /* @__PURE__ */ jsxs(DropdownMenu.Content, { align: "end", children: [
484
+ /* @__PURE__ */ jsxs(
485
+ DropdownMenu.Item,
486
+ {
487
+ className: "gap-x-2",
488
+ onClick: handleRetry,
489
+ disabled: retrying,
490
+ children: [
491
+ /* @__PURE__ */ jsx(ArrowPath, { className: "text-ui-fg-subtle" }),
492
+ retrying ? "Re-syncing…" : "Re-sync product"
493
+ ]
494
+ }
495
+ ),
496
+ record.product_url && /* @__PURE__ */ jsxs(
497
+ DropdownMenu.Item,
498
+ {
499
+ className: "gap-x-2",
500
+ onClick: () => window.open(record.product_url, "_blank"),
501
+ children: [
502
+ /* @__PURE__ */ jsx(ArrowUpRightOnBox, { className: "text-ui-fg-subtle" }),
503
+ "Open on Faire"
504
+ ]
505
+ }
506
+ )
507
+ ] })
508
+ ] })
509
+ ] })
510
+ ] }),
511
+ /* @__PURE__ */ jsxs("div", { className: "px-6 py-4 grid grid-cols-2 gap-4", children: [
512
+ /* @__PURE__ */ jsx(Detail, { label: "Product", value: record.product_id, mono: true }),
513
+ /* @__PURE__ */ jsx(Detail, { label: "Action", value: record.action }),
514
+ /* @__PURE__ */ jsx(Detail, { label: "Faire product token", value: record.product_token || "—", mono: true }),
515
+ /* @__PURE__ */ jsx(Detail, { label: "State", value: record.product_state || "—" }),
516
+ /* @__PURE__ */ jsx(Detail, { label: "Published", value: record.published ? "Yes" : "No" }),
475
517
  /* @__PURE__ */ jsx(
476
- "textarea",
518
+ Detail,
477
519
  {
478
- className: "border-ui-border-base rounded-lg border px-3 py-2 text-sm min-h-[72px]",
479
- placeholder: "prod_01..., prod_02...",
480
- value: productIds,
481
- onChange: (e) => setProductIds(e.target.value),
482
- disabled: !connected || pushMutation.isPending
520
+ label: "Synced at",
521
+ value: record.synced_at ? new Date(record.synced_at).toLocaleString() : "—"
483
522
  }
484
- ),
485
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
486
- /* @__PURE__ */ jsx(
487
- Button,
488
- {
489
- size: "small",
490
- variant: "secondary",
491
- disabled: !connected,
492
- onClick: () => navigate("/settings/faire/bulk"),
493
- children: "Select products"
494
- }
495
- ),
496
- /* @__PURE__ */ jsx(
497
- Button,
498
- {
499
- size: "small",
500
- onClick: handlePush,
501
- disabled: !connected || pushMutation.isPending,
502
- isLoading: pushMutation.isPending,
503
- children: "Start bulk push"
504
- }
505
- ),
506
- pushBatch && pushProgress && /* @__PURE__ */ jsx(
507
- BulkProgress,
508
- {
509
- label: "Push",
510
- progress: pushProgress,
511
- status: (_d = (_c = pushStatus.data) == null ? void 0 : _c.batch) == null ? void 0 : _d.status
512
- }
513
- )
514
- ] })
515
- ] }),
516
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
517
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
518
- /* @__PURE__ */ jsx(Label, { children: "Pull Faire orders into Medusa" }),
519
- /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", size: "small", children: "Ingests all available Faire orders as Medusa orders (idempotent)." })
520
- ] }),
521
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
522
- /* @__PURE__ */ jsx(
523
- Button,
524
- {
525
- size: "small",
526
- variant: "secondary",
527
- onClick: () => pullMutation.mutate(),
528
- disabled: !connected || pullMutation.isPending,
529
- isLoading: pullMutation.isPending,
530
- children: "Start order pull"
531
- }
532
- ),
533
- pullBatch && pullProgress && /* @__PURE__ */ jsx(
534
- BulkProgress,
535
- {
536
- label: "Pull",
537
- progress: pullProgress,
538
- status: (_f = (_e = pullStatus.data) == null ? void 0 : _e.batch) == null ? void 0 : _f.status
539
- }
540
- )
541
- ] })
523
+ )
542
524
  ] })
543
- ] })
525
+ ] }),
526
+ warnings.length > 0 && /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
527
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Issues" }) }),
528
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-4 flex flex-col gap-2", children: warnings.map((w, i) => /* @__PURE__ */ jsx(Text, { className: "text-ui-tag-red-text text-sm", children: w }, i)) })
529
+ ] }),
530
+ /* @__PURE__ */ jsx(Toaster, {})
544
531
  ] });
545
532
  };
546
- const BulkProgress = ({
533
+ const Detail = ({
547
534
  label,
548
- progress,
549
- status
550
- }) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
551
- /* @__PURE__ */ jsxs(StatusBadge, { color: progress.finished ? status === "failed" ? "red" : "green" : "orange", children: [
552
- label,
553
- ": ",
554
- progress.done,
555
- "/",
556
- progress.total || "?",
557
- " (",
558
- progress.pct,
559
- "%)"
560
- ] }),
561
- progress.finished && /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle", size: "small", children: status === "failed" ? "Completed with errors" : "Done" })
535
+ value,
536
+ mono
537
+ }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
538
+ /* @__PURE__ */ jsx(Label, { children: label }),
539
+ /* @__PURE__ */ jsx(Text, { className: mono ? "font-mono text-xs" : "", children: value })
562
540
  ] });
563
- const config = defineRouteConfig({
564
- label: "Faire",
565
- icon: BuildingStorefront
566
- });
567
- const handle$3 = {
568
- breadcrumb: () => "Faire Sync"
541
+ const handle$2 = {
542
+ breadcrumb: () => "Sync record"
569
543
  };
570
544
  const Root = ({ prev = "..", children }) => {
571
- const navigate2 = useNavigate();
545
+ const navigate = useNavigate();
572
546
  const [open, setOpen] = useState(false);
573
547
  useEffect(() => {
574
548
  setOpen(true);
@@ -577,7 +551,7 @@ const Root = ({ prev = "..", children }) => {
577
551
  const handleOpenChange = (next) => {
578
552
  if (!next) {
579
553
  document.body.style.pointerEvents = "auto";
580
- navigate2(prev, { replace: true });
554
+ navigate(prev, { replace: true });
581
555
  return;
582
556
  }
583
557
  setOpen(next);
@@ -640,7 +614,7 @@ const listProducts = (query) => sdk.admin.product.list({
640
614
  ...query
641
615
  });
642
616
  const FaireBulkPage = () => {
643
- var _a, _b, _c, _d;
617
+ var _a, _b, _c, _d, _e, _f, _g;
644
618
  const queryClient = useQueryClient();
645
619
  const [pagination, setPagination] = useState({
646
620
  pageIndex: 0,
@@ -710,6 +684,27 @@ const FaireBulkPage = () => {
710
684
  },
711
685
  onError: (err) => toast.error("Failed to start bulk sync", { description: err.message })
712
686
  });
687
+ const [pullBatch, setPullBatch] = useState(null);
688
+ const pullMutation = useMutation({
689
+ mutationFn: () => faireApi.ingestOrders(),
690
+ onSuccess: (res) => {
691
+ setPullBatch(res.batch_id);
692
+ toast.success("Faire order pull started", {
693
+ description: "Running in the background. Polling progress…"
694
+ });
695
+ },
696
+ onError: (err) => toast.error("Failed to start order pull", { description: err.message })
697
+ });
698
+ const pullStatus = useQuery({
699
+ queryKey: ["faire", "ingest", pullBatch],
700
+ enabled: !!pullBatch,
701
+ refetchInterval: (q) => {
702
+ var _a2, _b2;
703
+ return ((_b2 = (_a2 = q.state.data) == null ? void 0 : _a2.progress) == null ? void 0 : _b2.finished) ? false : 3e3;
704
+ },
705
+ queryFn: () => faireApi.ingestStatus(pullBatch)
706
+ });
707
+ const pullProgress = (_e = pullStatus.data) == null ? void 0 : _e.progress;
713
708
  const columns = useBulkProductColumns();
714
709
  const commands = useMemo(
715
710
  () => [
@@ -808,129 +803,42 @@ const FaireBulkPage = () => {
808
803
  productsQuery.isLoading ? /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-3 p-6", children: Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }, i)) }) : /* @__PURE__ */ jsx(DataTable.Table, {}),
809
804
  /* @__PURE__ */ jsx(DataTable.Pagination, {})
810
805
  ] }) }),
811
- /* @__PURE__ */ jsx(Toaster, {})
812
- ] });
813
- };
814
- const handle$2 = {
815
- breadcrumb: () => "Bulk sync"
816
- };
817
- const STATUS_COLORS = {
818
- success: "green",
819
- failed: "red",
820
- draft: "orange",
821
- pending: "grey",
822
- syncing: "grey"
823
- };
824
- const FaireSyncDetailPage = () => {
825
- const { id } = useParams();
826
- const navigate2 = useNavigate();
827
- const [record, setRecord] = useState(null);
828
- const [loading, setLoading] = useState(true);
829
- const [retrying, setRetrying] = useState(false);
830
- const [error, setError] = useState(null);
831
- useEffect(() => {
832
- if (!id) return;
833
- setLoading(true);
834
- faireApi.getSync(id).then((res) => {
835
- setRecord(res.sync || res);
836
- }).catch((err) => setError(err.message || "Failed to load sync record")).finally(() => setLoading(false));
837
- }, [id]);
838
- const handleRetry = async () => {
839
- if (!record) return;
840
- setRetrying(true);
841
- setError(null);
842
- try {
843
- await faireApi.retrySync(record.id);
844
- const updated = await faireApi.getSync(record.id).catch(() => null);
845
- if (updated) setRecord(updated.sync || updated);
846
- } catch (err) {
847
- setError(err.message || "Retry failed");
848
- } finally {
849
- setRetrying(false);
850
- }
851
- };
852
- if (loading) {
853
- return /* @__PURE__ */ jsx(Container, { className: "p-6", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-7 w-12 rounded-md" }) });
854
- }
855
- if (!record) {
856
- return /* @__PURE__ */ jsxs(Container, { className: "p-6 flex flex-col gap-4", children: [
857
- /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Sync record not found" }),
858
- /* @__PURE__ */ jsx(Button, { size: "small", variant: "secondary", onClick: () => navigate2("/settings/faire"), children: "Back to Faire settings" })
859
- ] });
860
- }
861
- const warnings = record.error_message ? record.error_message.split(" | ") : [];
862
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
863
- error && /* @__PURE__ */ jsx(Container, { className: "p-4", children: /* @__PURE__ */ jsx(Text, { className: "text-ui-tag-red-text", children: error }) }),
864
- /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
865
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-6 py-4", children: [
866
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
867
- /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Sync record" }),
868
- /* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle font-mono text-xs", children: record.id })
869
- ] }),
870
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
871
- /* @__PURE__ */ jsx(StatusBadge, { color: STATUS_COLORS[record.status] || "grey", children: record.status }),
872
- /* @__PURE__ */ jsxs(DropdownMenu, { children: [
873
- /* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { size: "small", variant: "transparent", disabled: retrying, children: /* @__PURE__ */ jsx(EllipsisHorizontal, {}) }) }),
874
- /* @__PURE__ */ jsxs(DropdownMenu.Content, { align: "end", children: [
875
- /* @__PURE__ */ jsxs(
876
- DropdownMenu.Item,
877
- {
878
- className: "gap-x-2",
879
- onClick: handleRetry,
880
- disabled: retrying,
881
- children: [
882
- /* @__PURE__ */ jsx(ArrowPath, { className: "text-ui-fg-subtle" }),
883
- retrying ? "Re-syncing…" : "Re-sync product"
884
- ]
885
- }
886
- ),
887
- record.product_url && /* @__PURE__ */ jsxs(
888
- DropdownMenu.Item,
889
- {
890
- className: "gap-x-2",
891
- onClick: () => window.open(record.product_url, "_blank"),
892
- children: [
893
- /* @__PURE__ */ jsx(ArrowUpRightOnBox, { className: "text-ui-fg-subtle" }),
894
- "Open on Faire"
895
- ]
896
- }
897
- )
898
- ] })
899
- ] })
900
- ] })
901
- ] }),
902
- /* @__PURE__ */ jsxs("div", { className: "px-6 py-4 grid grid-cols-2 gap-4", children: [
903
- /* @__PURE__ */ jsx(Detail, { label: "Product", value: record.product_id, mono: true }),
904
- /* @__PURE__ */ jsx(Detail, { label: "Action", value: record.action }),
905
- /* @__PURE__ */ jsx(Detail, { label: "Faire product token", value: record.product_token || "—", mono: true }),
906
- /* @__PURE__ */ jsx(Detail, { label: "State", value: record.product_state || "—" }),
907
- /* @__PURE__ */ jsx(Detail, { label: "Published", value: record.published ? "Yes" : "No" }),
908
- /* @__PURE__ */ jsx(
909
- Detail,
806
+ /* @__PURE__ */ jsx(RouteFocusModal.Footer, { children: /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center justify-between gap-x-3", children: [
807
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-x-3", children: [
808
+ /* @__PURE__ */ jsx(Text, { size: "small", className: "text-ui-fg-subtle", children: "Pull Faire orders into Medusa (idempotent)" }),
809
+ pullBatch && pullProgress && /* @__PURE__ */ jsxs(
810
+ StatusBadge,
910
811
  {
911
- label: "Synced at",
912
- value: record.synced_at ? new Date(record.synced_at).toLocaleString() : "—"
812
+ color: pullProgress.finished ? ((_g = (_f = pullStatus.data) == null ? void 0 : _f.batch) == null ? void 0 : _g.status) === "failed" ? "red" : "green" : "orange",
813
+ children: [
814
+ "Pull: ",
815
+ pullProgress.done,
816
+ "/",
817
+ pullProgress.total || "?",
818
+ " (",
819
+ pullProgress.pct,
820
+ "%)"
821
+ ]
913
822
  }
914
823
  )
915
- ] })
916
- ] }),
917
- warnings.length > 0 && /* @__PURE__ */ jsxs(Container, { className: "divide-y p-0", children: [
918
- /* @__PURE__ */ jsx("div", { className: "px-6 py-4", children: /* @__PURE__ */ jsx(Heading, { level: "h2", children: "Issues" }) }),
919
- /* @__PURE__ */ jsx("div", { className: "px-6 py-4 flex flex-col gap-2", children: warnings.map((w, i) => /* @__PURE__ */ jsx(Text, { className: "text-ui-tag-red-text text-sm", children: w }, i)) })
920
- ] }),
824
+ ] }),
825
+ /* @__PURE__ */ jsx(
826
+ Button,
827
+ {
828
+ size: "small",
829
+ variant: "secondary",
830
+ onClick: () => pullMutation.mutate(),
831
+ disabled: !connected || pullMutation.isPending,
832
+ isLoading: pullMutation.isPending,
833
+ children: "Pull orders"
834
+ }
835
+ )
836
+ ] }) }),
921
837
  /* @__PURE__ */ jsx(Toaster, {})
922
838
  ] });
923
839
  };
924
- const Detail = ({
925
- label,
926
- value,
927
- mono
928
- }) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-1", children: [
929
- /* @__PURE__ */ jsx(Label, { children: label }),
930
- /* @__PURE__ */ jsx(Text, { className: mono ? "font-mono text-xs" : "", children: value })
931
- ] });
932
840
  const handle$1 = {
933
- breadcrumb: () => "Sync record"
841
+ breadcrumb: () => "Bulk sync"
934
842
  };
935
843
  const STATUS_KEY = ["faire", "status"];
936
844
  const PARENT = "/settings/faire";
@@ -953,13 +861,13 @@ const cleanSettings = (form) => {
953
861
  return out;
954
862
  };
955
863
  const FaireSyncSettingsDrawer = () => {
956
- const navigate2 = useNavigate();
864
+ const navigate = useNavigate();
957
865
  const queryClient = useQueryClient();
958
866
  const [open, setOpen] = useState(true);
959
867
  const [form, setForm] = useState({});
960
868
  const close = () => {
961
869
  setOpen(false);
962
- navigate2(PARENT);
870
+ navigate(PARENT);
963
871
  };
964
872
  const statusQuery = useQuery({
965
873
  queryKey: STATUS_KEY,
@@ -1121,7 +1029,7 @@ const handle = {
1121
1029
  const EXCHANGED_PREFIX = "faire:oauth:exchanged:";
1122
1030
  const FaireOauthCallback = () => {
1123
1031
  const [params] = useSearchParams();
1124
- const navigate2 = useNavigate();
1032
+ const navigate = useNavigate();
1125
1033
  const [error, setError] = useState(null);
1126
1034
  const [exchanging, setExchanging] = useState(true);
1127
1035
  useEffect(() => {
@@ -1141,7 +1049,7 @@ const FaireOauthCallback = () => {
1141
1049
  return;
1142
1050
  }
1143
1051
  if (sessionStorage.getItem(EXCHANGED_PREFIX + code)) {
1144
- navigate2("/settings/faire", { replace: true });
1052
+ navigate("/settings/faire", { replace: true });
1145
1053
  return;
1146
1054
  }
1147
1055
  sessionStorage.setItem(EXCHANGED_PREFIX + code, "1");
@@ -1149,13 +1057,13 @@ const FaireOauthCallback = () => {
1149
1057
  const run = async () => {
1150
1058
  try {
1151
1059
  await faireApi.callback(code, state);
1152
- if (!cancelled) navigate2("/settings/faire", { replace: true });
1060
+ if (!cancelled) navigate("/settings/faire", { replace: true });
1153
1061
  } catch (err) {
1154
1062
  if (!cancelled) {
1155
1063
  try {
1156
1064
  const status = await faireApi.status();
1157
1065
  if (status == null ? void 0 : status.connected) {
1158
- navigate2("/settings/faire", { replace: true });
1066
+ navigate("/settings/faire", { replace: true });
1159
1067
  return;
1160
1068
  }
1161
1069
  } catch {
@@ -1169,7 +1077,7 @@ const FaireOauthCallback = () => {
1169
1077
  return () => {
1170
1078
  cancelled = true;
1171
1079
  };
1172
- }, [params, navigate2]);
1080
+ }, [params, navigate]);
1173
1081
  return /* @__PURE__ */ jsxs(Container, { className: "flex flex-col items-center gap-4 p-8", children: [
1174
1082
  /* @__PURE__ */ jsx(Heading, { level: "h1", children: "Connecting Faire…" }),
1175
1083
  error ? /* @__PURE__ */ jsx(Alert, { variant: "error", children: /* @__PURE__ */ jsx(Text, { children: error }) }) : /* @__PURE__ */ jsx(Text, { children: exchanging ? "Completing authorization, please wait…" : "Redirecting…" }),
@@ -1178,7 +1086,7 @@ const FaireOauthCallback = () => {
1178
1086
  {
1179
1087
  size: "small",
1180
1088
  variant: "secondary",
1181
- onClick: () => navigate2("/settings/faire", { replace: true }),
1089
+ onClick: () => navigate("/settings/faire", { replace: true }),
1182
1090
  children: "Back to Faire settings"
1183
1091
  }
1184
1092
  )
@@ -1199,13 +1107,13 @@ const routeModule = {
1199
1107
  handle: handle$3
1200
1108
  },
1201
1109
  {
1202
- Component: FaireBulkPage,
1203
- path: "/settings/faire/bulk",
1110
+ Component: FaireSyncDetailPage,
1111
+ path: "/settings/faire/:id",
1204
1112
  handle: handle$2
1205
1113
  },
1206
1114
  {
1207
- Component: FaireSyncDetailPage,
1208
- path: "/settings/faire/:id",
1115
+ Component: FaireBulkPage,
1116
+ path: "/settings/faire/bulk",
1209
1117
  handle: handle$1
1210
1118
  },
1211
1119
  {