@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.
- 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/service.js +51 -10
- 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/service.ts +57 -9
|
@@ -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,
|
|
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
|
|
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
|
-
|
|
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: () =>
|
|
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
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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
|
-
|
|
465
|
-
/* @__PURE__ */
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
/* @__PURE__ */
|
|
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(
|
|
473
|
-
/* @__PURE__ */ jsx(Text, { className: "text-ui-fg-subtle
|
|
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
|
-
|
|
518
|
+
Detail,
|
|
477
519
|
{
|
|
478
|
-
|
|
479
|
-
|
|
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
|
|
533
|
+
const Detail = ({
|
|
547
534
|
label,
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
}) => /* @__PURE__ */ jsxs("div", { className: "flex
|
|
551
|
-
/* @__PURE__ */
|
|
552
|
-
|
|
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
|
|
564
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
812
|
-
|
|
813
|
-
}
|
|
814
|
-
|
|
815
|
-
|
|
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
|
-
|
|
912
|
-
|
|
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
|
-
|
|
918
|
-
|
|
919
|
-
|
|
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: () => "
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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,
|
|
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: () =>
|
|
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:
|
|
1203
|
-
path: "/settings/faire
|
|
1110
|
+
Component: FaireSyncDetailPage,
|
|
1111
|
+
path: "/settings/faire/:id",
|
|
1204
1112
|
handle: handle$2
|
|
1205
1113
|
},
|
|
1206
1114
|
{
|
|
1207
|
-
Component:
|
|
1208
|
-
path: "/settings/faire
|
|
1115
|
+
Component: FaireBulkPage,
|
|
1116
|
+
path: "/settings/faire/bulk",
|
|
1209
1117
|
handle: handle$1
|
|
1210
1118
|
},
|
|
1211
1119
|
{
|