@jytextiles/medusa-plugin-faire-store-sync 0.2.13 → 0.2.14
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 +39 -1
- package/.medusa/server/src/admin/index.mjs +41 -3
- package/.medusa/server/src/api/admin/faire/syncs/[id]/route.js +19 -2
- package/.medusa/server/src/workflows/remove-product-from-faire.js +92 -0
- package/package.json +1 -1
- package/src/admin/lib/api.ts +2 -0
- package/src/admin/routes/settings/faire/[id]/page.tsx +38 -2
- package/src/api/admin/faire/syncs/[id]/route.ts +19 -0
- package/src/workflows/remove-product-from-faire.ts +121 -0
|
@@ -62,6 +62,7 @@ const faireApi = {
|
|
|
62
62
|
},
|
|
63
63
|
getSync: (id) => sdk.client.fetch(`/admin/faire/syncs/${id}`),
|
|
64
64
|
retrySync: (id) => sdk.client.fetch(`/admin/faire/syncs/${id}`, { method: "POST" }),
|
|
65
|
+
deleteSync: (id) => sdk.client.fetch(`/admin/faire/syncs/${id}`, { method: "DELETE" }),
|
|
65
66
|
brand: () => sdk.client.fetch("/admin/faire/brand"),
|
|
66
67
|
taxonomy: (opts = {}) => {
|
|
67
68
|
var _a;
|
|
@@ -1149,9 +1150,11 @@ const STATUS_COLORS = {
|
|
|
1149
1150
|
const FaireSyncDetailPage = () => {
|
|
1150
1151
|
const { id } = reactRouterDom.useParams();
|
|
1151
1152
|
const navigate = reactRouterDom.useNavigate();
|
|
1153
|
+
const prompt = ui.usePrompt();
|
|
1152
1154
|
const [record, setRecord] = react.useState(null);
|
|
1153
1155
|
const [loading, setLoading] = react.useState(true);
|
|
1154
1156
|
const [retrying, setRetrying] = react.useState(false);
|
|
1157
|
+
const [deleting, setDeleting] = react.useState(false);
|
|
1155
1158
|
const [error, setError] = react.useState(null);
|
|
1156
1159
|
react.useEffect(() => {
|
|
1157
1160
|
if (!id) return;
|
|
@@ -1174,6 +1177,28 @@ const FaireSyncDetailPage = () => {
|
|
|
1174
1177
|
setRetrying(false);
|
|
1175
1178
|
}
|
|
1176
1179
|
};
|
|
1180
|
+
const handleDelete = async () => {
|
|
1181
|
+
if (!record) return;
|
|
1182
|
+
const confirmed = await prompt({
|
|
1183
|
+
title: "Remove from Faire?",
|
|
1184
|
+
description: "Faire has no hard-delete API, so this unpublishes the product (sets it back to draft, hidden from buyers) and unlinks it from the product. Re-sync to publish it again, or delete it permanently from the Faire brand portal.",
|
|
1185
|
+
confirmText: "Remove",
|
|
1186
|
+
cancelText: "Cancel"
|
|
1187
|
+
});
|
|
1188
|
+
if (!confirmed) return;
|
|
1189
|
+
setDeleting(true);
|
|
1190
|
+
setError(null);
|
|
1191
|
+
try {
|
|
1192
|
+
await faireApi.deleteSync(record.id);
|
|
1193
|
+
const updated = await faireApi.getSync(record.id).catch(() => null);
|
|
1194
|
+
if (updated) setRecord(updated.sync || updated);
|
|
1195
|
+
navigate(PARENT);
|
|
1196
|
+
} catch (err) {
|
|
1197
|
+
setError(err.message || "Remove failed");
|
|
1198
|
+
} finally {
|
|
1199
|
+
setDeleting(false);
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1177
1202
|
const warnings = (record == null ? void 0 : record.error_message) ? record.error_message.split(" | ") : [];
|
|
1178
1203
|
return /* @__PURE__ */ jsxRuntime.jsxs(RouteFocusModal, { prev: PARENT, children: [
|
|
1179
1204
|
/* @__PURE__ */ jsxRuntime.jsxs(RouteFocusModal.Header, { children: [
|
|
@@ -1182,7 +1207,7 @@ const FaireSyncDetailPage = () => {
|
|
|
1182
1207
|
record && /* @__PURE__ */ jsxRuntime.jsx(ui.StatusBadge, { color: STATUS_COLORS[record.status] || "grey", children: record.status })
|
|
1183
1208
|
] }),
|
|
1184
1209
|
record && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
|
|
1185
|
-
/* @__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, {}) }) }),
|
|
1210
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", disabled: retrying || deleting, children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
|
|
1186
1211
|
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Content, { align: "end", children: [
|
|
1187
1212
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1188
1213
|
ui.DropdownMenu.Item,
|
|
@@ -1206,6 +1231,19 @@ const FaireSyncDetailPage = () => {
|
|
|
1206
1231
|
"Open on Faire"
|
|
1207
1232
|
]
|
|
1208
1233
|
}
|
|
1234
|
+
),
|
|
1235
|
+
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
|
|
1236
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1237
|
+
ui.DropdownMenu.Item,
|
|
1238
|
+
{
|
|
1239
|
+
className: "gap-x-2 text-ui-fg-error",
|
|
1240
|
+
onClick: handleDelete,
|
|
1241
|
+
disabled: deleting,
|
|
1242
|
+
children: [
|
|
1243
|
+
/* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-error" }),
|
|
1244
|
+
deleting ? "Removing…" : "Remove from Faire"
|
|
1245
|
+
]
|
|
1246
|
+
}
|
|
1209
1247
|
)
|
|
1210
1248
|
] })
|
|
1211
1249
|
] }) })
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { defineWidgetConfig, defineRouteConfig } from "@medusajs/admin-sdk";
|
|
3
|
-
import { toast, Container, Heading, Text, StatusBadge, Alert, Badge, Button, Tooltip, Drawer, Input, clx, createDataTableColumnHelper, createDataTableFilterHelper, useDataTable, Skeleton, DropdownMenu, DataTable, Label, Toaster, FocusModal, Switch, Select, IconButton } from "@medusajs/ui";
|
|
3
|
+
import { toast, Container, Heading, Text, StatusBadge, Alert, Badge, Button, Tooltip, Drawer, Input, clx, createDataTableColumnHelper, createDataTableFilterHelper, useDataTable, Skeleton, DropdownMenu, DataTable, Label, Toaster, FocusModal, Switch, Select, usePrompt, IconButton } from "@medusajs/ui";
|
|
4
4
|
import { useQueryClient, useQuery, useMutation, keepPreviousData } from "@tanstack/react-query";
|
|
5
5
|
import { useState, useEffect, useMemo } from "react";
|
|
6
6
|
import Medusa from "@medusajs/js-sdk";
|
|
7
|
-
import { BuildingStorefront, ChevronDownMini, EllipsisHorizontal, ArrowPath, ArrowUpRightOnBox } from "@medusajs/icons";
|
|
7
|
+
import { BuildingStorefront, ChevronDownMini, EllipsisHorizontal, ArrowPath, ArrowUpRightOnBox, Trash } from "@medusajs/icons";
|
|
8
8
|
import { useNavigate, Outlet, useParams, useSearchParams } from "react-router-dom";
|
|
9
9
|
const sdk = new Medusa({
|
|
10
10
|
baseUrl: "/",
|
|
@@ -59,6 +59,7 @@ const faireApi = {
|
|
|
59
59
|
},
|
|
60
60
|
getSync: (id) => sdk.client.fetch(`/admin/faire/syncs/${id}`),
|
|
61
61
|
retrySync: (id) => sdk.client.fetch(`/admin/faire/syncs/${id}`, { method: "POST" }),
|
|
62
|
+
deleteSync: (id) => sdk.client.fetch(`/admin/faire/syncs/${id}`, { method: "DELETE" }),
|
|
62
63
|
brand: () => sdk.client.fetch("/admin/faire/brand"),
|
|
63
64
|
taxonomy: (opts = {}) => {
|
|
64
65
|
var _a;
|
|
@@ -1146,9 +1147,11 @@ const STATUS_COLORS = {
|
|
|
1146
1147
|
const FaireSyncDetailPage = () => {
|
|
1147
1148
|
const { id } = useParams();
|
|
1148
1149
|
const navigate = useNavigate();
|
|
1150
|
+
const prompt = usePrompt();
|
|
1149
1151
|
const [record, setRecord] = useState(null);
|
|
1150
1152
|
const [loading, setLoading] = useState(true);
|
|
1151
1153
|
const [retrying, setRetrying] = useState(false);
|
|
1154
|
+
const [deleting, setDeleting] = useState(false);
|
|
1152
1155
|
const [error, setError] = useState(null);
|
|
1153
1156
|
useEffect(() => {
|
|
1154
1157
|
if (!id) return;
|
|
@@ -1171,6 +1174,28 @@ const FaireSyncDetailPage = () => {
|
|
|
1171
1174
|
setRetrying(false);
|
|
1172
1175
|
}
|
|
1173
1176
|
};
|
|
1177
|
+
const handleDelete = async () => {
|
|
1178
|
+
if (!record) return;
|
|
1179
|
+
const confirmed = await prompt({
|
|
1180
|
+
title: "Remove from Faire?",
|
|
1181
|
+
description: "Faire has no hard-delete API, so this unpublishes the product (sets it back to draft, hidden from buyers) and unlinks it from the product. Re-sync to publish it again, or delete it permanently from the Faire brand portal.",
|
|
1182
|
+
confirmText: "Remove",
|
|
1183
|
+
cancelText: "Cancel"
|
|
1184
|
+
});
|
|
1185
|
+
if (!confirmed) return;
|
|
1186
|
+
setDeleting(true);
|
|
1187
|
+
setError(null);
|
|
1188
|
+
try {
|
|
1189
|
+
await faireApi.deleteSync(record.id);
|
|
1190
|
+
const updated = await faireApi.getSync(record.id).catch(() => null);
|
|
1191
|
+
if (updated) setRecord(updated.sync || updated);
|
|
1192
|
+
navigate(PARENT);
|
|
1193
|
+
} catch (err) {
|
|
1194
|
+
setError(err.message || "Remove failed");
|
|
1195
|
+
} finally {
|
|
1196
|
+
setDeleting(false);
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1174
1199
|
const warnings = (record == null ? void 0 : record.error_message) ? record.error_message.split(" | ") : [];
|
|
1175
1200
|
return /* @__PURE__ */ jsxs(RouteFocusModal, { prev: PARENT, children: [
|
|
1176
1201
|
/* @__PURE__ */ jsxs(RouteFocusModal.Header, { children: [
|
|
@@ -1179,7 +1204,7 @@ const FaireSyncDetailPage = () => {
|
|
|
1179
1204
|
record && /* @__PURE__ */ jsx(StatusBadge, { color: STATUS_COLORS[record.status] || "grey", children: record.status })
|
|
1180
1205
|
] }),
|
|
1181
1206
|
record && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
1182
|
-
/* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { size: "small", variant: "transparent", disabled: retrying, children: /* @__PURE__ */ jsx(EllipsisHorizontal, {}) }) }),
|
|
1207
|
+
/* @__PURE__ */ jsx(DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsx(IconButton, { size: "small", variant: "transparent", disabled: retrying || deleting, children: /* @__PURE__ */ jsx(EllipsisHorizontal, {}) }) }),
|
|
1183
1208
|
/* @__PURE__ */ jsxs(DropdownMenu.Content, { align: "end", children: [
|
|
1184
1209
|
/* @__PURE__ */ jsxs(
|
|
1185
1210
|
DropdownMenu.Item,
|
|
@@ -1203,6 +1228,19 @@ const FaireSyncDetailPage = () => {
|
|
|
1203
1228
|
"Open on Faire"
|
|
1204
1229
|
]
|
|
1205
1230
|
}
|
|
1231
|
+
),
|
|
1232
|
+
/* @__PURE__ */ jsx(DropdownMenu.Separator, {}),
|
|
1233
|
+
/* @__PURE__ */ jsxs(
|
|
1234
|
+
DropdownMenu.Item,
|
|
1235
|
+
{
|
|
1236
|
+
className: "gap-x-2 text-ui-fg-error",
|
|
1237
|
+
onClick: handleDelete,
|
|
1238
|
+
disabled: deleting,
|
|
1239
|
+
children: [
|
|
1240
|
+
/* @__PURE__ */ jsx(Trash, { className: "text-ui-fg-error" }),
|
|
1241
|
+
deleting ? "Removing…" : "Remove from Faire"
|
|
1242
|
+
]
|
|
1243
|
+
}
|
|
1206
1244
|
)
|
|
1207
1245
|
] })
|
|
1208
1246
|
] }) })
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.POST = exports.GET = void 0;
|
|
3
|
+
exports.DELETE = exports.POST = exports.GET = void 0;
|
|
4
4
|
const utils_1 = require("@medusajs/framework/utils");
|
|
5
5
|
const faire_sync_1 = require("../../../../../modules/faire-sync");
|
|
6
6
|
const sync_product_to_faire_1 = require("../../../../../workflows/sync-product-to-faire");
|
|
7
|
+
const remove_product_from_faire_1 = require("../../../../../workflows/remove-product-from-faire");
|
|
7
8
|
// GET /admin/faire/syncs/:id — fetch a single sync record
|
|
8
9
|
const GET = async (req, res) => {
|
|
9
10
|
const service = req.scope.resolve(faire_sync_1.FAIRE_SYNC_MODULE);
|
|
@@ -29,4 +30,20 @@ const POST = async (req, res) => {
|
|
|
29
30
|
res.json({ result });
|
|
30
31
|
};
|
|
31
32
|
exports.POST = POST;
|
|
32
|
-
|
|
33
|
+
// DELETE /admin/faire/syncs/:id — remove (unpublish) the Faire product for this
|
|
34
|
+
// record's product and clear the local link. Faire has no hard-delete API, so
|
|
35
|
+
// this sets the product back to DRAFT (hidden from buyers).
|
|
36
|
+
const DELETE = async (req, res) => {
|
|
37
|
+
const service = req.scope.resolve(faire_sync_1.FAIRE_SYNC_MODULE);
|
|
38
|
+
const { id } = req.params;
|
|
39
|
+
const [record] = await service.listFaireSyncRecords({ id }, { take: 1 });
|
|
40
|
+
if (!record) {
|
|
41
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_FOUND, "Sync record not found");
|
|
42
|
+
}
|
|
43
|
+
const { result } = await (0, remove_product_from_faire_1.removeProductFromFaireWorkflow)(req.scope).run({
|
|
44
|
+
input: { product_id: record.product_id },
|
|
45
|
+
});
|
|
46
|
+
res.json({ result });
|
|
47
|
+
};
|
|
48
|
+
exports.DELETE = DELETE;
|
|
49
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm91dGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvYXBpL2FkbWluL2ZhaXJlL3N5bmNzL1tpZF0vcm91dGUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBQ0EscURBQXVEO0FBQ3ZELGtFQUFxRTtBQUVyRSwwRkFBMkY7QUFDM0Ysa0dBQW1HO0FBRW5HLDBEQUEwRDtBQUNuRCxNQUFNLEdBQUcsR0FBRyxLQUFLLEVBQUUsR0FBa0IsRUFBRSxHQUFtQixFQUFFLEVBQUU7SUFDbkUsTUFBTSxPQUFPLEdBQXFCLEdBQUcsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLDhCQUFpQixDQUFDLENBQUE7SUFDdEUsTUFBTSxFQUFFLEVBQUUsRUFBRSxHQUFHLEdBQUcsQ0FBQyxNQUFNLENBQUE7SUFDekIsTUFBTSxDQUFDLE1BQU0sQ0FBQyxHQUFHLE1BQU0sT0FBTyxDQUFDLG9CQUFvQixDQUFDLEVBQUUsRUFBRSxFQUFTLEVBQUUsRUFBRSxJQUFJLEVBQUUsQ0FBQyxFQUFTLENBQUMsQ0FBQTtJQUN0RixJQUFJLENBQUMsTUFBTSxFQUFFLENBQUM7UUFDWixNQUFNLElBQUksbUJBQVcsQ0FBQyxtQkFBVyxDQUFDLEtBQUssQ0FBQyxTQUFTLEVBQUUsdUJBQXVCLENBQUMsQ0FBQTtJQUM3RSxDQUFDO0lBQ0QsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsQ0FBQyxDQUFBO0FBQzVCLENBQUMsQ0FBQTtBQVJZLFFBQUEsR0FBRyxPQVFmO0FBRUQsb0ZBQW9GO0FBQzdFLE1BQU0sSUFBSSxHQUFHLEtBQUssRUFBRSxHQUFrQixFQUFFLEdBQW1CLEVBQUUsRUFBRTtJQUNwRSxNQUFNLE9BQU8sR0FBcUIsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsOEJBQWlCLENBQUMsQ0FBQTtJQUN0RSxNQUFNLEVBQUUsRUFBRSxFQUFFLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQTtJQUN6QixNQUFNLENBQUMsTUFBTSxDQUFDLEdBQUcsTUFBTSxPQUFPLENBQUMsb0JBQW9CLENBQUMsRUFBRSxFQUFFLEVBQVMsRUFBRSxFQUFFLElBQUksRUFBRSxDQUFDLEVBQVMsQ0FBQyxDQUFBO0lBQ3RGLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztRQUNaLE1BQU0sSUFBSSxtQkFBVyxDQUFDLG1CQUFXLENBQUMsS0FBSyxDQUFDLFNBQVMsRUFBRSx1QkFBdUIsQ0FBQyxDQUFBO0lBQzdFLENBQUM7SUFFRCxNQUFNLEVBQUUsTUFBTSxFQUFFLEdBQUcsTUFBTSxJQUFBLGtEQUEwQixFQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLENBQUM7UUFDakUsS0FBSyxFQUFFLEVBQUUsVUFBVSxFQUFHLE1BQWMsQ0FBQyxVQUFVLEVBQUU7S0FDbEQsQ0FBQyxDQUFBO0lBRUYsR0FBRyxDQUFDLElBQUksQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUE7QUFDdEIsQ0FBQyxDQUFBO0FBYlksUUFBQSxJQUFJLFFBYWhCO0FBRUQsZ0ZBQWdGO0FBQ2hGLDhFQUE4RTtBQUM5RSw0REFBNEQ7QUFDckQsTUFBTSxNQUFNLEdBQUcsS0FBSyxFQUFFLEdBQWtCLEVBQUUsR0FBbUIsRUFBRSxFQUFFO0lBQ3RFLE1BQU0sT0FBTyxHQUFxQixHQUFHLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyw4QkFBaUIsQ0FBQyxDQUFBO0lBQ3RFLE1BQU0sRUFBRSxFQUFFLEVBQUUsR0FBRyxHQUFHLENBQUMsTUFBTSxDQUFBO0lBQ3pCLE1BQU0sQ0FBQyxNQUFNLENBQUMsR0FBRyxNQUFNLE9BQU8sQ0FBQyxvQkFBb0IsQ0FBQyxFQUFFLEVBQUUsRUFBUyxFQUFFLEVBQUUsSUFBSSxFQUFFLENBQUMsRUFBUyxDQUFDLENBQUE7SUFDdEYsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO1FBQ1osTUFBTSxJQUFJLG1CQUFXLENBQUMsbUJBQVcsQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFFLHVCQUF1QixDQUFDLENBQUE7SUFDN0UsQ0FBQztJQUVELE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxNQUFNLElBQUEsMERBQThCLEVBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQztRQUNyRSxLQUFLLEVBQUUsRUFBRSxVQUFVLEVBQUcsTUFBYyxDQUFDLFVBQVUsRUFBRTtLQUNsRCxDQUFDLENBQUE7SUFFRixHQUFHLENBQUMsSUFBSSxDQUFDLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQTtBQUN0QixDQUFDLENBQUE7QUFiWSxRQUFBLE1BQU0sVUFhbEIifQ==
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.removeProductFromFaireWorkflow = exports.REMOVE_PRODUCT_FROM_FAIRE = void 0;
|
|
4
|
+
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
5
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
6
|
+
const faire_sync_1 = require("../modules/faire-sync");
|
|
7
|
+
exports.REMOVE_PRODUCT_FROM_FAIRE = "faire-remove-product";
|
|
8
|
+
// ── Step: unpublish the Faire product + clean up the local link/record ─────
|
|
9
|
+
//
|
|
10
|
+
// Faire's public Brand API exposes no hard-delete for products (only
|
|
11
|
+
// POST/PUT/GET on /products), so "remove" means unpublish: set the product's
|
|
12
|
+
// lifecycle_state back to DRAFT via the update endpoint, which pulls it from
|
|
13
|
+
// buyers' view. A true hard-delete has to be done in the Faire brand portal.
|
|
14
|
+
const removeFaireProductStep = (0, workflows_sdk_1.createStep)("faire-remove-product-step", async (input, { container }) => {
|
|
15
|
+
const service = container.resolve(faire_sync_1.FAIRE_SYNC_MODULE);
|
|
16
|
+
const remoteLink = container.resolve(utils_1.ContainerRegistrationKeys.LINK);
|
|
17
|
+
const account = await service.ensureFreshToken();
|
|
18
|
+
const client = service.getClient(account.auth_mode ?? "oauth");
|
|
19
|
+
const warnings = [];
|
|
20
|
+
// Find the linked Faire product for this product.
|
|
21
|
+
let product_token = null;
|
|
22
|
+
try {
|
|
23
|
+
const linkRows = await remoteLink.list({
|
|
24
|
+
[utils_1.Modules.PRODUCT]: { product_id: input.product_id },
|
|
25
|
+
[faire_sync_1.FAIRE_SYNC_MODULE]: { faire_sync_account_id: account.id },
|
|
26
|
+
});
|
|
27
|
+
const row = linkRows?.[0];
|
|
28
|
+
product_token = row?.faire_product_token ?? null;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// no link
|
|
32
|
+
}
|
|
33
|
+
// Unpublish (DRAFT) so it disappears from the Faire storefront. Tolerate a
|
|
34
|
+
// 404 — a product already gone on Faire still cleans up our side.
|
|
35
|
+
if (product_token) {
|
|
36
|
+
try {
|
|
37
|
+
await client.updateProduct(account.access_token, product_token, {
|
|
38
|
+
lifecycle_state: "DRAFT",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
const msg = String(err?.message || err);
|
|
43
|
+
if (/404|not found/i.test(msg)) {
|
|
44
|
+
warnings.push("Product was already removed on Faire.");
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
warnings.push(`Faire unpublish failed: ${msg}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
warnings.push("No linked Faire product found for this product.");
|
|
53
|
+
}
|
|
54
|
+
// Drop the product↔account link so the product reads as un-synced.
|
|
55
|
+
try {
|
|
56
|
+
await remoteLink.dismiss([
|
|
57
|
+
{
|
|
58
|
+
[utils_1.Modules.PRODUCT]: { product_id: input.product_id },
|
|
59
|
+
[faire_sync_1.FAIRE_SYNC_MODULE]: { faire_sync_account_id: account.id },
|
|
60
|
+
},
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// ignore dismiss errors
|
|
65
|
+
}
|
|
66
|
+
// Historical record of the removal.
|
|
67
|
+
await service.createSyncRecord({
|
|
68
|
+
product_id: input.product_id,
|
|
69
|
+
account_id: account.id,
|
|
70
|
+
product_token,
|
|
71
|
+
product_url: null,
|
|
72
|
+
product_state: "draft",
|
|
73
|
+
action: "delete",
|
|
74
|
+
status: "success",
|
|
75
|
+
published: false,
|
|
76
|
+
error_message: warnings.length ? warnings.join(" | ") : null,
|
|
77
|
+
warnings,
|
|
78
|
+
metadata: {},
|
|
79
|
+
synced_at: new Date(),
|
|
80
|
+
});
|
|
81
|
+
return new workflows_sdk_1.StepResponse({
|
|
82
|
+
product_id: input.product_id,
|
|
83
|
+
product_token,
|
|
84
|
+
removed: Boolean(product_token),
|
|
85
|
+
warnings,
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
exports.removeProductFromFaireWorkflow = (0, workflows_sdk_1.createWorkflow)(exports.REMOVE_PRODUCT_FROM_FAIRE, (input) => {
|
|
89
|
+
const result = removeFaireProductStep(input);
|
|
90
|
+
return new workflows_sdk_1.WorkflowResponse(result);
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3ZlLXByb2R1Y3QtZnJvbS1mYWlyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3MvcmVtb3ZlLXByb2R1Y3QtZnJvbS1mYWlyZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxRUFNMEM7QUFDMUMscURBQThFO0FBRTlFLHNEQUF5RDtBQUc1QyxRQUFBLHlCQUF5QixHQUFHLHNCQUFzQixDQUFBO0FBYS9ELDhFQUE4RTtBQUM5RSxFQUFFO0FBQ0YscUVBQXFFO0FBQ3JFLDZFQUE2RTtBQUM3RSw2RUFBNkU7QUFDN0UsNkVBQTZFO0FBRTdFLE1BQU0sc0JBQXNCLEdBQUcsSUFBQSwwQkFBVSxFQUN2QywyQkFBMkIsRUFDM0IsS0FBSyxFQUNILEtBQTZCLEVBQzdCLEVBQUUsU0FBUyxFQUFFLEVBQ3dCLEVBQUU7SUFDdkMsTUFBTSxPQUFPLEdBQXFCLFNBQVMsQ0FBQyxPQUFPLENBQUMsOEJBQWlCLENBQUMsQ0FBQTtJQUN0RSxNQUFNLFVBQVUsR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDLGlDQUF5QixDQUFDLElBQUksQ0FBUyxDQUFBO0lBRTVFLE1BQU0sT0FBTyxHQUFHLE1BQU0sT0FBTyxDQUFDLGdCQUFnQixFQUFFLENBQUE7SUFDaEQsTUFBTSxNQUFNLEdBQUcsT0FBTyxDQUFDLFNBQVMsQ0FBRSxPQUFPLENBQUMsU0FBZ0MsSUFBSSxPQUFPLENBQUMsQ0FBQTtJQUN0RixNQUFNLFFBQVEsR0FBYSxFQUFFLENBQUE7SUFFN0Isa0RBQWtEO0lBQ2xELElBQUksYUFBYSxHQUFrQixJQUFJLENBQUE7SUFDdkMsSUFBSSxDQUFDO1FBQ0gsTUFBTSxRQUFRLEdBQUcsTUFBTSxVQUFVLENBQUMsSUFBSSxDQUFDO1lBQ3JDLENBQUMsZUFBTyxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsVUFBVSxFQUFFLEtBQUssQ0FBQyxVQUFVLEVBQUU7WUFDbkQsQ0FBQyw4QkFBaUIsQ0FBQyxF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
|
package/package.json
CHANGED
package/src/admin/lib/api.ts
CHANGED
|
@@ -83,6 +83,8 @@ export const faireApi = {
|
|
|
83
83
|
getSync: (id: string) => sdk.client.fetch(`/admin/faire/syncs/${id}`),
|
|
84
84
|
retrySync: (id: string) =>
|
|
85
85
|
sdk.client.fetch(`/admin/faire/syncs/${id}`, { method: "POST" }),
|
|
86
|
+
deleteSync: (id: string) =>
|
|
87
|
+
sdk.client.fetch(`/admin/faire/syncs/${id}`, { method: "DELETE" }),
|
|
86
88
|
brand: () => sdk.client.fetch("/admin/faire/brand"),
|
|
87
89
|
taxonomy: (opts: { q?: string; limit?: number; ids?: string[] } = {}) => {
|
|
88
90
|
const query: Record<string, string> = {}
|
|
@@ -8,8 +8,9 @@ import {
|
|
|
8
8
|
StatusBadge,
|
|
9
9
|
Text,
|
|
10
10
|
Toaster,
|
|
11
|
+
usePrompt,
|
|
11
12
|
} from "@medusajs/ui"
|
|
12
|
-
import { EllipsisHorizontal, ArrowPath, ArrowUpRightOnBox } from "@medusajs/icons"
|
|
13
|
+
import { EllipsisHorizontal, ArrowPath, ArrowUpRightOnBox, Trash } from "@medusajs/icons"
|
|
13
14
|
import { useEffect, useState } from "react"
|
|
14
15
|
import { useNavigate, useParams } from "react-router-dom"
|
|
15
16
|
import { faireApi } from "../../../../lib/api"
|
|
@@ -29,9 +30,11 @@ const STATUS_COLORS: Record<string, "green" | "red" | "orange" | "grey"> = {
|
|
|
29
30
|
const FaireSyncDetailPage = () => {
|
|
30
31
|
const { id } = useParams<{ id: string }>()
|
|
31
32
|
const navigate = useNavigate()
|
|
33
|
+
const prompt = usePrompt()
|
|
32
34
|
const [record, setRecord] = useState<FaireSyncRecord | null>(null)
|
|
33
35
|
const [loading, setLoading] = useState(true)
|
|
34
36
|
const [retrying, setRetrying] = useState(false)
|
|
37
|
+
const [deleting, setDeleting] = useState(false)
|
|
35
38
|
const [error, setError] = useState<string | null>(null)
|
|
36
39
|
|
|
37
40
|
useEffect(() => {
|
|
@@ -61,6 +64,30 @@ const FaireSyncDetailPage = () => {
|
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
|
|
67
|
+
const handleDelete = async () => {
|
|
68
|
+
if (!record) return
|
|
69
|
+
const confirmed = await prompt({
|
|
70
|
+
title: "Remove from Faire?",
|
|
71
|
+
description:
|
|
72
|
+
"Faire has no hard-delete API, so this unpublishes the product (sets it back to draft, hidden from buyers) and unlinks it from the product. Re-sync to publish it again, or delete it permanently from the Faire brand portal.",
|
|
73
|
+
confirmText: "Remove",
|
|
74
|
+
cancelText: "Cancel",
|
|
75
|
+
})
|
|
76
|
+
if (!confirmed) return
|
|
77
|
+
setDeleting(true)
|
|
78
|
+
setError(null)
|
|
79
|
+
try {
|
|
80
|
+
await faireApi.deleteSync(record.id)
|
|
81
|
+
const updated = await faireApi.getSync(record.id).catch(() => null)
|
|
82
|
+
if (updated) setRecord((updated as any).sync || updated)
|
|
83
|
+
navigate(PARENT)
|
|
84
|
+
} catch (err: any) {
|
|
85
|
+
setError(err.message || "Remove failed")
|
|
86
|
+
} finally {
|
|
87
|
+
setDeleting(false)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
64
91
|
const warnings: string[] = record?.error_message
|
|
65
92
|
? record.error_message.split(" | ")
|
|
66
93
|
: []
|
|
@@ -80,7 +107,7 @@ const FaireSyncDetailPage = () => {
|
|
|
80
107
|
<div className="flex items-center gap-2">
|
|
81
108
|
<DropdownMenu>
|
|
82
109
|
<DropdownMenu.Trigger asChild>
|
|
83
|
-
<IconButton size="small" variant="transparent" disabled={retrying}>
|
|
110
|
+
<IconButton size="small" variant="transparent" disabled={retrying || deleting}>
|
|
84
111
|
<EllipsisHorizontal />
|
|
85
112
|
</IconButton>
|
|
86
113
|
</DropdownMenu.Trigger>
|
|
@@ -102,6 +129,15 @@ const FaireSyncDetailPage = () => {
|
|
|
102
129
|
Open on Faire
|
|
103
130
|
</DropdownMenu.Item>
|
|
104
131
|
)}
|
|
132
|
+
<DropdownMenu.Separator />
|
|
133
|
+
<DropdownMenu.Item
|
|
134
|
+
className="gap-x-2 text-ui-fg-error"
|
|
135
|
+
onClick={handleDelete}
|
|
136
|
+
disabled={deleting}
|
|
137
|
+
>
|
|
138
|
+
<Trash className="text-ui-fg-error" />
|
|
139
|
+
{deleting ? "Removing…" : "Remove from Faire"}
|
|
140
|
+
</DropdownMenu.Item>
|
|
105
141
|
</DropdownMenu.Content>
|
|
106
142
|
</DropdownMenu>
|
|
107
143
|
</div>
|
|
@@ -3,6 +3,7 @@ import { MedusaError } from "@medusajs/framework/utils"
|
|
|
3
3
|
import { FAIRE_SYNC_MODULE } from "../../../../../modules/faire-sync"
|
|
4
4
|
import FaireSyncService from "../../../../../modules/faire-sync/service"
|
|
5
5
|
import { syncProductToFaireWorkflow } from "../../../../../workflows/sync-product-to-faire"
|
|
6
|
+
import { removeProductFromFaireWorkflow } from "../../../../../workflows/remove-product-from-faire"
|
|
6
7
|
|
|
7
8
|
// GET /admin/faire/syncs/:id — fetch a single sync record
|
|
8
9
|
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
|
@@ -30,3 +31,21 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
|
|
30
31
|
|
|
31
32
|
res.json({ result })
|
|
32
33
|
}
|
|
34
|
+
|
|
35
|
+
// DELETE /admin/faire/syncs/:id — remove (unpublish) the Faire product for this
|
|
36
|
+
// record's product and clear the local link. Faire has no hard-delete API, so
|
|
37
|
+
// this sets the product back to DRAFT (hidden from buyers).
|
|
38
|
+
export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => {
|
|
39
|
+
const service: FaireSyncService = req.scope.resolve(FAIRE_SYNC_MODULE)
|
|
40
|
+
const { id } = req.params
|
|
41
|
+
const [record] = await service.listFaireSyncRecords({ id } as any, { take: 1 } as any)
|
|
42
|
+
if (!record) {
|
|
43
|
+
throw new MedusaError(MedusaError.Types.NOT_FOUND, "Sync record not found")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const { result } = await removeProductFromFaireWorkflow(req.scope).run({
|
|
47
|
+
input: { product_id: (record as any).product_id },
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
res.json({ result })
|
|
51
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createStep,
|
|
3
|
+
createWorkflow,
|
|
4
|
+
StepResponse,
|
|
5
|
+
WorkflowResponse,
|
|
6
|
+
WorkflowData,
|
|
7
|
+
} from "@medusajs/framework/workflows-sdk"
|
|
8
|
+
import { ContainerRegistrationKeys, Modules } from "@medusajs/framework/utils"
|
|
9
|
+
import type { Link } from "@medusajs/modules-sdk"
|
|
10
|
+
import { FAIRE_SYNC_MODULE } from "../modules/faire-sync"
|
|
11
|
+
import FaireSyncService from "../modules/faire-sync/service"
|
|
12
|
+
|
|
13
|
+
export const REMOVE_PRODUCT_FROM_FAIRE = "faire-remove-product"
|
|
14
|
+
|
|
15
|
+
export type RemoveProductFromFaireInput = {
|
|
16
|
+
product_id: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type RemoveResult = {
|
|
20
|
+
product_id: string
|
|
21
|
+
product_token: string | null
|
|
22
|
+
removed: boolean
|
|
23
|
+
warnings: string[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── Step: unpublish the Faire product + clean up the local link/record ─────
|
|
27
|
+
//
|
|
28
|
+
// Faire's public Brand API exposes no hard-delete for products (only
|
|
29
|
+
// POST/PUT/GET on /products), so "remove" means unpublish: set the product's
|
|
30
|
+
// lifecycle_state back to DRAFT via the update endpoint, which pulls it from
|
|
31
|
+
// buyers' view. A true hard-delete has to be done in the Faire brand portal.
|
|
32
|
+
|
|
33
|
+
const removeFaireProductStep = createStep(
|
|
34
|
+
"faire-remove-product-step",
|
|
35
|
+
async (
|
|
36
|
+
input: { product_id: string },
|
|
37
|
+
{ container }
|
|
38
|
+
): Promise<StepResponse<RemoveResult>> => {
|
|
39
|
+
const service: FaireSyncService = container.resolve(FAIRE_SYNC_MODULE)
|
|
40
|
+
const remoteLink = container.resolve(ContainerRegistrationKeys.LINK) as Link
|
|
41
|
+
|
|
42
|
+
const account = await service.ensureFreshToken()
|
|
43
|
+
const client = service.getClient((account.auth_mode as "oauth" | "apiKey") ?? "oauth")
|
|
44
|
+
const warnings: string[] = []
|
|
45
|
+
|
|
46
|
+
// Find the linked Faire product for this product.
|
|
47
|
+
let product_token: string | null = null
|
|
48
|
+
try {
|
|
49
|
+
const linkRows = await remoteLink.list({
|
|
50
|
+
[Modules.PRODUCT]: { product_id: input.product_id },
|
|
51
|
+
[FAIRE_SYNC_MODULE]: { faire_sync_account_id: account.id },
|
|
52
|
+
})
|
|
53
|
+
const row: any = (linkRows as any[])?.[0]
|
|
54
|
+
product_token = row?.faire_product_token ?? null
|
|
55
|
+
} catch {
|
|
56
|
+
// no link
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Unpublish (DRAFT) so it disappears from the Faire storefront. Tolerate a
|
|
60
|
+
// 404 — a product already gone on Faire still cleans up our side.
|
|
61
|
+
if (product_token) {
|
|
62
|
+
try {
|
|
63
|
+
await client.updateProduct(account.access_token, product_token, {
|
|
64
|
+
lifecycle_state: "DRAFT",
|
|
65
|
+
})
|
|
66
|
+
} catch (err: any) {
|
|
67
|
+
const msg = String(err?.message || err)
|
|
68
|
+
if (/404|not found/i.test(msg)) {
|
|
69
|
+
warnings.push("Product was already removed on Faire.")
|
|
70
|
+
} else {
|
|
71
|
+
warnings.push(`Faire unpublish failed: ${msg}`)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
warnings.push("No linked Faire product found for this product.")
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Drop the product↔account link so the product reads as un-synced.
|
|
79
|
+
try {
|
|
80
|
+
await remoteLink.dismiss([
|
|
81
|
+
{
|
|
82
|
+
[Modules.PRODUCT]: { product_id: input.product_id },
|
|
83
|
+
[FAIRE_SYNC_MODULE]: { faire_sync_account_id: account.id },
|
|
84
|
+
},
|
|
85
|
+
])
|
|
86
|
+
} catch {
|
|
87
|
+
// ignore dismiss errors
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Historical record of the removal.
|
|
91
|
+
await service.createSyncRecord({
|
|
92
|
+
product_id: input.product_id,
|
|
93
|
+
account_id: account.id,
|
|
94
|
+
product_token,
|
|
95
|
+
product_url: null,
|
|
96
|
+
product_state: "draft",
|
|
97
|
+
action: "delete",
|
|
98
|
+
status: "success",
|
|
99
|
+
published: false,
|
|
100
|
+
error_message: warnings.length ? warnings.join(" | ") : null,
|
|
101
|
+
warnings,
|
|
102
|
+
metadata: {},
|
|
103
|
+
synced_at: new Date(),
|
|
104
|
+
} as any)
|
|
105
|
+
|
|
106
|
+
return new StepResponse({
|
|
107
|
+
product_id: input.product_id,
|
|
108
|
+
product_token,
|
|
109
|
+
removed: Boolean(product_token),
|
|
110
|
+
warnings,
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
export const removeProductFromFaireWorkflow = createWorkflow(
|
|
116
|
+
REMOVE_PRODUCT_FROM_FAIRE,
|
|
117
|
+
(input: WorkflowData<RemoveProductFromFaireInput>) => {
|
|
118
|
+
const result = removeFaireProductStep(input)
|
|
119
|
+
return new WorkflowResponse(result)
|
|
120
|
+
}
|
|
121
|
+
)
|