@burdenoff/microfe-store 2026.1014.1 → 2026.1031.1
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/dist/components/ExternalListingBadge.d.ts +15 -0
- package/dist/components/ExternalListingBadge.js +19 -0
- package/dist/components/ExternalListingBadge.js.map +1 -0
- package/dist/components/TemplateRevisionsQueue.d.ts +10 -0
- package/dist/components/TemplateRevisionsQueue.js +300 -0
- package/dist/components/TemplateRevisionsQueue.js.map +1 -0
- package/dist/components/WorkflowTemplatePreviewPanel.d.ts +50 -0
- package/dist/components/WorkflowTemplatePreviewPanel.js +221 -0
- package/dist/components/WorkflowTemplatePreviewPanel.js.map +1 -0
- package/dist/generated/global-operations.d.ts +14 -0
- package/dist/generated/global-operations.js +14 -0
- package/dist/generated/global-operations.js.map +1 -1
- package/dist/generated/global-types.d.ts +377 -2
- package/dist/generated/global-types.js.map +1 -1
- package/dist/generated/wspace-types.d.ts +20345 -404
- package/dist/hooks/useStoreGraphQL.d.ts +4 -0
- package/dist/hooks/useStoreGraphQL.js.map +1 -1
- package/dist/pages/AdminSubmissionsQueuePage.js +447 -334
- package/dist/pages/AdminSubmissionsQueuePage.js.map +1 -1
- package/dist/pages/MarketplaceHomePage.js +134 -127
- package/dist/pages/MarketplaceHomePage.js.map +1 -1
- package/dist/pages/ProductDetailPage.js +628 -601
- package/dist/pages/ProductDetailPage.js.map +1 -1
- package/dist/translations/en.d.ts +6 -0
- package/dist/translations/en.js +6 -0
- package/dist/translations/en.js.map +1 -1
- package/dist/utils/externalListing.d.ts +35 -0
- package/dist/utils/externalListing.js +34 -0
- package/dist/utils/externalListing.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { ExternalListingFields } from '../utils/externalListing';
|
|
3
|
+
|
|
4
|
+
interface ExternalListingBadgeProps {
|
|
5
|
+
product: ExternalListingFields;
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Small, non-interactive chip for catalogue cards: tells the shopper that the
|
|
10
|
+
* item is sold in another Burdenoff product rather than through the store's
|
|
11
|
+
* cart. Cards are buttons that open the product page, which carries the actual
|
|
12
|
+
* "View on <product>" link, so this chip must not be a link itself.
|
|
13
|
+
*/
|
|
14
|
+
export declare const ExternalListingBadge: FC<ExternalListingBadgeProps>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useTr as e } from "../shared/hooks/useTr.js";
|
|
2
|
+
import { getExternalListing as t } from "../utils/externalListing.js";
|
|
3
|
+
import { ExternalLink as n } from "lucide-react";
|
|
4
|
+
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
5
|
+
//#region src/components/ExternalListingBadge.tsx
|
|
6
|
+
var a = ({ product: a, className: o }) => {
|
|
7
|
+
let s = e(), c = t(a);
|
|
8
|
+
return c ? /* @__PURE__ */ i("span", {
|
|
9
|
+
className: `inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary ${o ?? ""}`,
|
|
10
|
+
children: [/* @__PURE__ */ r(n, {
|
|
11
|
+
className: "size-3",
|
|
12
|
+
"aria-hidden": "true"
|
|
13
|
+
}), s("pages.marketplace.soldOnProduct", "On {{product}}", { product: c.productName })]
|
|
14
|
+
}) : null;
|
|
15
|
+
};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { a as ExternalListingBadge };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=ExternalListingBadge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExternalListingBadge.js","names":[],"sources":["../../src/components/ExternalListingBadge.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { ExternalLink } from 'lucide-react';\nimport { useTr } from '../shared/hooks/useTr';\nimport { getExternalListing, type ExternalListingFields } from '../utils/externalListing';\n\ninterface ExternalListingBadgeProps {\n product: ExternalListingFields;\n className?: string;\n}\n\n/**\n * Small, non-interactive chip for catalogue cards: tells the shopper that the\n * item is sold in another Burdenoff product rather than through the store's\n * cart. Cards are buttons that open the product page, which carries the actual\n * \"View on <product>\" link, so this chip must not be a link itself.\n */\nexport const ExternalListingBadge: FC<ExternalListingBadgeProps> = ({ product, className }) => {\n const tr = useTr();\n const listing = getExternalListing(product);\n if (!listing) return null;\n return (\n <span\n className={`inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary ${className ?? ''}`}\n >\n <ExternalLink className=\"size-3\" aria-hidden=\"true\" />\n {tr('pages.marketplace.soldOnProduct', 'On {{product}}', {\n product: listing.productName,\n })}\n </span>\n );\n};\n"],"mappings":";;;;;AAgBA,IAAa,KAAuD,EAAE,YAAS,mBAAgB;CAC7F,IAAM,IAAK,GAAO,EACZ,IAAU,EAAmB,EAAQ;AAE3C,QADK,IAEH,kBAAC,QAAD;EACE,WAAW,gHAAgH,KAAa;YAD1I,CAGE,kBAAC,GAAD;GAAc,WAAU;GAAS,eAAY;GAAS,CAAA,EACrD,EAAG,mCAAmC,kBAAkB,EACvD,SAAS,EAAQ,aAClB,CAAC,CACG;MATY"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
export declare const TEMPLATE_REVISION_COUNT_QUERY = "\n query AdminPendingWorkflowTemplateRevisionCount {\n pendingWorkflowTemplateRevisions(limit: 1) {\n totalCount\n }\n }\n";
|
|
4
|
+
export declare const TemplateRevisionsQueue: FC<{
|
|
5
|
+
authToken?: string | null;
|
|
6
|
+
/** Called with the number of pending revisions after every load. */
|
|
7
|
+
onCountChange?: (count: number) => void;
|
|
8
|
+
/** Bumped by the page's Refresh button to reload the list. */
|
|
9
|
+
refreshKey?: number;
|
|
10
|
+
}>;
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { Markdown as e } from "../node_modules/react-markdown/lib/index.js";
|
|
2
|
+
import { WORKFLOW_TEMPLATE_PREVIEW_FIELDS as t, WorkflowTemplatePreviewPanel as n } from "./WorkflowTemplatePreviewPanel.js";
|
|
3
|
+
import { useCallback as r, useEffect as i, useRef as a, useState as o } from "react";
|
|
4
|
+
import { Check as s, ChevronDown as c, ChevronUp as l, Clock as u, GitBranch as d, Hash as f, RefreshCw as p, User as m, X as h } from "lucide-react";
|
|
5
|
+
import { graphqlFetch as g } from "@burdenoff/fe-libs/shared/graphql";
|
|
6
|
+
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
7
|
+
import { IllustratedEmptyState as y } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
//#region src/components/TemplateRevisionsQueue.tsx
|
|
9
|
+
var b = `
|
|
10
|
+
query AdminPendingWorkflowTemplateRevisions($limit: Int, $offset: Int) {
|
|
11
|
+
pendingWorkflowTemplateRevisions(limit: $limit, offset: $offset) {
|
|
12
|
+
totalCount
|
|
13
|
+
items {
|
|
14
|
+
id
|
|
15
|
+
productId
|
|
16
|
+
productSlug
|
|
17
|
+
status
|
|
18
|
+
version
|
|
19
|
+
revision
|
|
20
|
+
liveVersion
|
|
21
|
+
name
|
|
22
|
+
description
|
|
23
|
+
category
|
|
24
|
+
tags
|
|
25
|
+
authorName
|
|
26
|
+
publisherName
|
|
27
|
+
notes
|
|
28
|
+
submittedAt
|
|
29
|
+
submittedBy
|
|
30
|
+
rejectedReason
|
|
31
|
+
nodeCount
|
|
32
|
+
preview { ${t} }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
`, x = "\n query AdminPendingWorkflowTemplateRevisionCount {\n pendingWorkflowTemplateRevisions(limit: 1) {\n totalCount\n }\n }\n", S = "\n mutation AdminApproveWorkflowTemplateRevision($id: ID!, $notes: String) {\n approveWorkflowTemplateRevision(id: $id, notes: $notes) {\n id\n status\n version\n }\n }\n", C = "\n mutation AdminRejectWorkflowTemplateRevision($id: ID!, $reason: String!) {\n rejectWorkflowTemplateRevision(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n", w = 20;
|
|
37
|
+
function T(e) {
|
|
38
|
+
return e ? new Date(e).toLocaleString(void 0, {
|
|
39
|
+
month: "short",
|
|
40
|
+
day: "numeric",
|
|
41
|
+
year: "numeric",
|
|
42
|
+
hour: "2-digit",
|
|
43
|
+
minute: "2-digit"
|
|
44
|
+
}) : "—";
|
|
45
|
+
}
|
|
46
|
+
var E = ({ authToken: t, onCountChange: x, refreshKey: E = 0 }) => {
|
|
47
|
+
let [D, O] = o([]), [k, A] = o(!1), [j, M] = o(null), [N, P] = o(null), [F, I] = o(null), [L, R] = o(null), [z, B] = o(""), [V, H] = o(null), [U, W] = o(0), G = a(0), K = r(async (e = 0) => {
|
|
48
|
+
let n = ++G.current, r = () => n === G.current;
|
|
49
|
+
A(!0), M(null);
|
|
50
|
+
try {
|
|
51
|
+
let n = await g({
|
|
52
|
+
gateway: "global",
|
|
53
|
+
query: b,
|
|
54
|
+
variables: {
|
|
55
|
+
limit: w,
|
|
56
|
+
offset: e
|
|
57
|
+
},
|
|
58
|
+
authToken: t || void 0
|
|
59
|
+
});
|
|
60
|
+
if (!r()) return;
|
|
61
|
+
if (n.errors?.length) throw Error(n.errors[0]?.message ?? "Failed to load template revisions");
|
|
62
|
+
let i = n.data?.pendingWorkflowTemplateRevisions, a = i?.items ?? [];
|
|
63
|
+
O((t) => e > 0 ? [...t, ...a] : a), W(i?.totalCount ?? 0), x?.(i?.totalCount ?? 0);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
r() && M(e instanceof Error ? e.message : "Failed to load template revisions");
|
|
66
|
+
} finally {
|
|
67
|
+
r() && A(!1);
|
|
68
|
+
}
|
|
69
|
+
}, [t, x]);
|
|
70
|
+
i(() => {
|
|
71
|
+
K();
|
|
72
|
+
}, [K, E]);
|
|
73
|
+
let q = async (e) => {
|
|
74
|
+
P(e.id), M(null), H(null);
|
|
75
|
+
try {
|
|
76
|
+
let n = await g({
|
|
77
|
+
gateway: "global",
|
|
78
|
+
query: S,
|
|
79
|
+
variables: {
|
|
80
|
+
id: e.id,
|
|
81
|
+
notes: null
|
|
82
|
+
},
|
|
83
|
+
authToken: t || void 0
|
|
84
|
+
});
|
|
85
|
+
if (n.errors?.length) throw Error(n.errors[0]?.message ?? "Approval failed");
|
|
86
|
+
H(`${e.name} is now live at version ${n.data?.approveWorkflowTemplateRevision.version ?? e.version}.`), await K();
|
|
87
|
+
} catch (e) {
|
|
88
|
+
M(e instanceof Error ? e.message : "Approval failed");
|
|
89
|
+
} finally {
|
|
90
|
+
P(null);
|
|
91
|
+
}
|
|
92
|
+
}, J = async (e) => {
|
|
93
|
+
if (!z.trim()) {
|
|
94
|
+
M("Rejection reason is required");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
P(e.id), M(null), H(null);
|
|
98
|
+
try {
|
|
99
|
+
let n = await g({
|
|
100
|
+
gateway: "global",
|
|
101
|
+
query: C,
|
|
102
|
+
variables: {
|
|
103
|
+
id: e.id,
|
|
104
|
+
reason: z.trim()
|
|
105
|
+
},
|
|
106
|
+
authToken: t || void 0
|
|
107
|
+
});
|
|
108
|
+
if (n.errors?.length) throw Error(n.errors[0]?.message ?? "Rejection failed");
|
|
109
|
+
R(null), B(""), H(`Revision ${e.version} of ${e.name} was rejected; version ${e.liveVersion} stays live.`), await K();
|
|
110
|
+
} catch (e) {
|
|
111
|
+
M(e instanceof Error ? e.message : "Rejection failed");
|
|
112
|
+
} finally {
|
|
113
|
+
P(null);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
return k && D.length === 0 ? /* @__PURE__ */ _("div", {
|
|
117
|
+
className: "flex items-center justify-center py-24",
|
|
118
|
+
children: /* @__PURE__ */ _(p, { className: "size-6 animate-spin text-[var(--color-text-muted)]" })
|
|
119
|
+
}) : /* @__PURE__ */ v("div", {
|
|
120
|
+
className: "space-y-4",
|
|
121
|
+
children: [
|
|
122
|
+
j && /* @__PURE__ */ _("div", {
|
|
123
|
+
role: "alert",
|
|
124
|
+
className: "rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-4 py-3 text-sm text-[var(--color-text-danger)]",
|
|
125
|
+
children: j
|
|
126
|
+
}),
|
|
127
|
+
V && /* @__PURE__ */ _("div", {
|
|
128
|
+
role: "status",
|
|
129
|
+
className: "rounded-lg border border-[var(--color-border-seam)] bg-[var(--color-status-success-bg-subtle)] px-4 py-3 text-sm text-[var(--color-status-success-text)]",
|
|
130
|
+
children: V
|
|
131
|
+
}),
|
|
132
|
+
D.length === 0 ? /* @__PURE__ */ _(y, {
|
|
133
|
+
illustration: "empty-data",
|
|
134
|
+
title: "No template revisions waiting",
|
|
135
|
+
description: "When an author submits a new version of a published workflow template, it appears here. The current version stays listed until you approve the new one."
|
|
136
|
+
}) : /* @__PURE__ */ _("ul", {
|
|
137
|
+
className: "space-y-4",
|
|
138
|
+
"data-testid": "template-revisions",
|
|
139
|
+
children: D.map((t) => /* @__PURE__ */ v("li", {
|
|
140
|
+
"data-testid": `template-revision-${t.id}`,
|
|
141
|
+
className: "overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]",
|
|
142
|
+
children: [
|
|
143
|
+
/* @__PURE__ */ v("div", {
|
|
144
|
+
className: "space-y-2 p-5",
|
|
145
|
+
children: [
|
|
146
|
+
/* @__PURE__ */ v("div", {
|
|
147
|
+
className: "flex flex-wrap items-center gap-2",
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ _("h3", {
|
|
150
|
+
className: "text-base font-semibold text-[var(--color-text-primary)]",
|
|
151
|
+
children: t.name
|
|
152
|
+
}),
|
|
153
|
+
/* @__PURE__ */ v("span", {
|
|
154
|
+
className: "inline-flex items-center gap-1 rounded-full bg-[var(--color-status-info-bg-subtle)] px-2 py-0.5 text-[11px] font-semibold text-[var(--color-status-info-text)]",
|
|
155
|
+
children: [
|
|
156
|
+
/* @__PURE__ */ _(d, { className: "size-3" }),
|
|
157
|
+
t.liveVersion,
|
|
158
|
+
" live → ",
|
|
159
|
+
t.version,
|
|
160
|
+
" in review"
|
|
161
|
+
]
|
|
162
|
+
}),
|
|
163
|
+
/* @__PURE__ */ _("span", {
|
|
164
|
+
className: "rounded-full bg-[var(--color-status-warning-bg-subtle)] px-2 py-0.5 text-[10px] font-medium uppercase text-[var(--color-status-warning-text)]",
|
|
165
|
+
children: "Workflow template revision"
|
|
166
|
+
})
|
|
167
|
+
]
|
|
168
|
+
}),
|
|
169
|
+
/* @__PURE__ */ v("div", {
|
|
170
|
+
className: "flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-[var(--color-text-secondary)]",
|
|
171
|
+
children: [
|
|
172
|
+
/* @__PURE__ */ v("span", {
|
|
173
|
+
className: "flex items-center gap-1",
|
|
174
|
+
children: [/* @__PURE__ */ _(f, { className: "size-3" }), /* @__PURE__ */ _("span", {
|
|
175
|
+
className: "font-mono",
|
|
176
|
+
children: t.productSlug
|
|
177
|
+
})]
|
|
178
|
+
}),
|
|
179
|
+
/* @__PURE__ */ v("span", {
|
|
180
|
+
className: "flex items-center gap-1",
|
|
181
|
+
children: [/* @__PURE__ */ _(m, { className: "size-3" }), t.authorName ?? t.publisherName ?? t.submittedBy]
|
|
182
|
+
}),
|
|
183
|
+
/* @__PURE__ */ v("span", {
|
|
184
|
+
className: "flex items-center gap-1",
|
|
185
|
+
children: [/* @__PURE__ */ _(u, { className: "size-3" }), T(t.submittedAt)]
|
|
186
|
+
}),
|
|
187
|
+
/* @__PURE__ */ v("span", { children: [
|
|
188
|
+
t.category,
|
|
189
|
+
" · ",
|
|
190
|
+
t.nodeCount,
|
|
191
|
+
" node types"
|
|
192
|
+
] })
|
|
193
|
+
]
|
|
194
|
+
}),
|
|
195
|
+
/* @__PURE__ */ _("p", {
|
|
196
|
+
className: "text-sm text-[var(--color-text-secondary)]",
|
|
197
|
+
children: t.description
|
|
198
|
+
}),
|
|
199
|
+
t.notes && /* @__PURE__ */ v("div", {
|
|
200
|
+
className: "rounded-md border border-[var(--color-border-seam)] bg-[var(--color-surface-default)] px-3 py-2 text-xs text-[var(--color-text-secondary)]",
|
|
201
|
+
children: [/* @__PURE__ */ _("span", {
|
|
202
|
+
className: "font-semibold",
|
|
203
|
+
children: "Note from the author: "
|
|
204
|
+
}), /* @__PURE__ */ _(e, { children: t.notes })]
|
|
205
|
+
})
|
|
206
|
+
]
|
|
207
|
+
}),
|
|
208
|
+
/* @__PURE__ */ v("div", {
|
|
209
|
+
className: "flex flex-wrap items-center justify-between gap-2 border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] px-5 py-3",
|
|
210
|
+
children: [/* @__PURE__ */ v("button", {
|
|
211
|
+
type: "button",
|
|
212
|
+
onClick: () => I(F === t.id ? null : t.id),
|
|
213
|
+
className: "inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-surface-hover)]",
|
|
214
|
+
children: [F === t.id ? "Hide template contents" : "Preview template contents", F === t.id ? /* @__PURE__ */ _(l, { className: "size-3" }) : /* @__PURE__ */ _(c, { className: "size-3" })]
|
|
215
|
+
}), /* @__PURE__ */ v("div", {
|
|
216
|
+
className: "flex items-center gap-2",
|
|
217
|
+
children: [/* @__PURE__ */ v("button", {
|
|
218
|
+
type: "button",
|
|
219
|
+
onClick: () => void q(t),
|
|
220
|
+
disabled: N === t.id,
|
|
221
|
+
className: "inline-flex items-center gap-1.5 rounded-md bg-[var(--color-surface-success)] px-4 py-1.5 text-xs font-semibold text-[var(--color-text-on-success)] transition-opacity hover:opacity-90 disabled:opacity-50",
|
|
222
|
+
children: [/* @__PURE__ */ _(s, { className: "size-3.5" }), "Approve revision"]
|
|
223
|
+
}), /* @__PURE__ */ v("button", {
|
|
224
|
+
type: "button",
|
|
225
|
+
onClick: () => {
|
|
226
|
+
R(t.id), B("");
|
|
227
|
+
},
|
|
228
|
+
disabled: N === t.id,
|
|
229
|
+
className: "inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-4 py-1.5 text-xs font-semibold text-[var(--color-text-danger)] transition-colors hover:opacity-90 disabled:opacity-50",
|
|
230
|
+
children: [/* @__PURE__ */ _(h, { className: "size-3.5" }), "Reject revision"]
|
|
231
|
+
})]
|
|
232
|
+
})]
|
|
233
|
+
}),
|
|
234
|
+
F === t.id && /* @__PURE__ */ _("div", {
|
|
235
|
+
className: "border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] p-5",
|
|
236
|
+
children: t.preview ? /* @__PURE__ */ _(n, { preview: t.preview }) : /* @__PURE__ */ _("p", {
|
|
237
|
+
className: "text-xs text-[var(--color-text-muted)]",
|
|
238
|
+
children: "This revision has no workflow definition to preview."
|
|
239
|
+
})
|
|
240
|
+
}),
|
|
241
|
+
L === t.id && /* @__PURE__ */ v("div", {
|
|
242
|
+
className: "border-t border-[var(--color-border-seam)] px-5 py-4",
|
|
243
|
+
children: [
|
|
244
|
+
/* @__PURE__ */ v("p", {
|
|
245
|
+
className: "mb-2 text-xs font-semibold text-[var(--color-text-danger)]",
|
|
246
|
+
children: [
|
|
247
|
+
"Reject this revision. Version ",
|
|
248
|
+
t.liveVersion,
|
|
249
|
+
" stays live; the author sees your reason."
|
|
250
|
+
]
|
|
251
|
+
}),
|
|
252
|
+
/* @__PURE__ */ _("textarea", {
|
|
253
|
+
"aria-label": "Revision rejection reason",
|
|
254
|
+
value: z,
|
|
255
|
+
onChange: (e) => B(e.target.value),
|
|
256
|
+
rows: 3,
|
|
257
|
+
required: !0,
|
|
258
|
+
"aria-required": !0,
|
|
259
|
+
className: "w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] focus:border-[var(--color-border-accent)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] resize-none",
|
|
260
|
+
placeholder: "Be specific, for example 'The new HTTP node posts to a personal endpoint'"
|
|
261
|
+
}),
|
|
262
|
+
/* @__PURE__ */ v("div", {
|
|
263
|
+
className: "mt-2 flex items-center justify-end gap-2",
|
|
264
|
+
children: [/* @__PURE__ */ _("button", {
|
|
265
|
+
type: "button",
|
|
266
|
+
onClick: () => {
|
|
267
|
+
R(null), B("");
|
|
268
|
+
},
|
|
269
|
+
className: "rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)]",
|
|
270
|
+
children: "Cancel"
|
|
271
|
+
}), /* @__PURE__ */ _("button", {
|
|
272
|
+
type: "button",
|
|
273
|
+
onClick: () => void J(t),
|
|
274
|
+
disabled: !z.trim() || N === t.id,
|
|
275
|
+
className: "rounded-md bg-[var(--color-surface-danger)] px-3 py-1.5 text-xs font-semibold text-[var(--color-text-on-danger)] hover:opacity-90 disabled:opacity-50",
|
|
276
|
+
children: "Submit rejection"
|
|
277
|
+
})]
|
|
278
|
+
})
|
|
279
|
+
]
|
|
280
|
+
})
|
|
281
|
+
]
|
|
282
|
+
}, t.id))
|
|
283
|
+
}),
|
|
284
|
+
D.length > 0 && D.length < U && /* @__PURE__ */ _("div", {
|
|
285
|
+
className: "flex justify-center",
|
|
286
|
+
children: /* @__PURE__ */ v("button", {
|
|
287
|
+
type: "button",
|
|
288
|
+
onClick: () => void K(D.length),
|
|
289
|
+
disabled: k,
|
|
290
|
+
className: "inline-flex items-center gap-2 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-4 py-1.5 text-sm text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-surface-hover)] disabled:opacity-50",
|
|
291
|
+
children: [k && /* @__PURE__ */ _(p, { className: "size-4 animate-spin" }), "Load more"]
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
]
|
|
295
|
+
});
|
|
296
|
+
};
|
|
297
|
+
//#endregion
|
|
298
|
+
export { x as TEMPLATE_REVISION_COUNT_QUERY, E as TemplateRevisionsQueue };
|
|
299
|
+
|
|
300
|
+
//# sourceMappingURL=TemplateRevisionsQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TemplateRevisionsQueue.js","names":[],"sources":["../../src/components/TemplateRevisionsQueue.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n Check,\n ChevronDown,\n ChevronUp,\n Clock,\n GitBranch,\n Hash,\n RefreshCw,\n User,\n X,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport Markdown from 'react-markdown';\nimport {\n WORKFLOW_TEMPLATE_PREVIEW_FIELDS,\n WorkflowTemplatePreviewPanel,\n type WorkflowTemplatePreview,\n} from './WorkflowTemplatePreviewPanel';\n\n/**\n * Store admin queue of new versions of published community workflow templates\n * (BOFF-10463). The live version stays listed while a revision is reviewed;\n * approving swaps the revision in, rejecting sends the reason to the author.\n */\ntype TemplateRevision = {\n id: string;\n productId: string;\n productSlug: string;\n status: 'PENDING_REVIEW' | 'REJECTED';\n version: string;\n revision: number;\n liveVersion: string;\n name: string;\n description: string;\n category: string;\n tags: string[];\n authorName?: string | null;\n publisherName?: string | null;\n notes?: string | null;\n submittedAt: string;\n submittedBy: string;\n rejectedReason?: string | null;\n nodeCount: number;\n preview?: WorkflowTemplatePreview | null;\n};\n\nconst REVISIONS_QUERY = /* GraphQL */ `\n query AdminPendingWorkflowTemplateRevisions($limit: Int, $offset: Int) {\n pendingWorkflowTemplateRevisions(limit: $limit, offset: $offset) {\n totalCount\n items {\n id\n productId\n productSlug\n status\n version\n revision\n liveVersion\n name\n description\n category\n tags\n authorName\n publisherName\n notes\n submittedAt\n submittedBy\n rejectedReason\n nodeCount\n preview { ${WORKFLOW_TEMPLATE_PREVIEW_FIELDS} }\n }\n }\n }\n`;\n\nexport const TEMPLATE_REVISION_COUNT_QUERY = /* GraphQL */ `\n query AdminPendingWorkflowTemplateRevisionCount {\n pendingWorkflowTemplateRevisions(limit: 1) {\n totalCount\n }\n }\n`;\n\nconst APPROVE_REVISION_MUTATION = /* GraphQL */ `\n mutation AdminApproveWorkflowTemplateRevision($id: ID!, $notes: String) {\n approveWorkflowTemplateRevision(id: $id, notes: $notes) {\n id\n status\n version\n }\n }\n`;\n\nconst REJECT_REVISION_MUTATION = /* GraphQL */ `\n mutation AdminRejectWorkflowTemplateRevision($id: ID!, $reason: String!) {\n rejectWorkflowTemplateRevision(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n`;\n\n/**\n * Revisions per page. Each carries its full preview, and the gateway caps a\n * query's cost: 20 revisions cost about 1,100 of the 2,000 allowed (100 cost 5,600).\n */\nconst PAGE_SIZE = 20;\n\nfunction formatDate(iso?: string | null): string {\n if (!iso) return '—';\n return new Date(iso).toLocaleString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n hour: '2-digit',\n minute: '2-digit',\n });\n}\n\nexport const TemplateRevisionsQueue: FC<{\n authToken?: string | null;\n /** Called with the number of pending revisions after every load. */\n onCountChange?: (count: number) => void;\n /** Bumped by the page's Refresh button to reload the list. */\n refreshKey?: number;\n}> = ({ authToken, onCountChange, refreshKey = 0 }) => {\n const [revisions, setRevisions] = useState<TemplateRevision[]>([]);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [actingId, setActingId] = useState<string | null>(null);\n const [openId, setOpenId] = useState<string | null>(null);\n const [rejectingId, setRejectingId] = useState<string | null>(null);\n const [reason, setReason] = useState('');\n const [notice, setNotice] = useState<string | null>(null);\n const [totalCount, setTotalCount] = useState(0);\n // Each load takes a ticket; only the newest load may write state, so a slow\n // response (an earlier page, or a load overtaken by a refresh) is dropped.\n const latestLoad = useRef(0);\n\n const load = useCallback(\n async (offset = 0) => {\n const ticket = ++latestLoad.current;\n const isCurrent = () => ticket === latestLoad.current;\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n pendingWorkflowTemplateRevisions: { totalCount: number; items: TemplateRevision[] };\n }>({\n gateway: 'global',\n query: REVISIONS_QUERY,\n variables: { limit: PAGE_SIZE, offset },\n authToken: authToken || undefined,\n });\n if (!isCurrent()) return;\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load template revisions');\n }\n const data = result.data?.pendingWorkflowTemplateRevisions;\n const page = data?.items ?? [];\n setRevisions((prev) => (offset > 0 ? [...prev, ...page] : page));\n setTotalCount(data?.totalCount ?? 0);\n onCountChange?.(data?.totalCount ?? 0);\n } catch (err) {\n if (isCurrent()) {\n setError(err instanceof Error ? err.message : 'Failed to load template revisions');\n }\n } finally {\n if (isCurrent()) setLoading(false);\n }\n },\n [authToken, onCountChange]\n );\n\n useEffect(() => {\n void load();\n }, [load, refreshKey]);\n\n const approve = async (revision: TemplateRevision) => {\n setActingId(revision.id);\n setError(null);\n setNotice(null);\n try {\n const result = await graphqlFetch<{\n approveWorkflowTemplateRevision: { id: string; version: string };\n }>({\n gateway: 'global',\n query: APPROVE_REVISION_MUTATION,\n variables: { id: revision.id, notes: null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) throw new Error(result.errors[0]?.message ?? 'Approval failed');\n setNotice(\n `${revision.name} is now live at version ${\n result.data?.approveWorkflowTemplateRevision.version ?? revision.version\n }.`\n );\n await load();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setActingId(null);\n }\n };\n\n const reject = async (revision: TemplateRevision) => {\n if (!reason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setActingId(revision.id);\n setError(null);\n setNotice(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_REVISION_MUTATION,\n variables: { id: revision.id, reason: reason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n setRejectingId(null);\n setReason('');\n setNotice(\n `Revision ${revision.version} of ${revision.name} was rejected; version ${revision.liveVersion} stays live.`\n );\n await load();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setActingId(null);\n }\n };\n\n if (loading && revisions.length === 0) {\n return (\n <div className=\"flex items-center justify-center py-24\">\n <RefreshCw className=\"size-6 animate-spin text-[var(--color-text-muted)]\" />\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4\">\n {error && (\n <div\n role=\"alert\"\n className=\"rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-4 py-3 text-sm text-[var(--color-text-danger)]\"\n >\n {error}\n </div>\n )}\n {notice && (\n <div\n role=\"status\"\n className=\"rounded-lg border border-[var(--color-border-seam)] bg-[var(--color-status-success-bg-subtle)] px-4 py-3 text-sm text-[var(--color-status-success-text)]\"\n >\n {notice}\n </div>\n )}\n {revisions.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No template revisions waiting\"\n description=\"When an author submits a new version of a published workflow template, it appears here. The current version stays listed until you approve the new one.\"\n />\n ) : (\n <ul className=\"space-y-4\" data-testid=\"template-revisions\">\n {revisions.map((revision) => (\n <li\n key={revision.id}\n data-testid={`template-revision-${revision.id}`}\n className=\"overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]\"\n >\n <div className=\"space-y-2 p-5\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {revision.name}\n </h3>\n <span className=\"inline-flex items-center gap-1 rounded-full bg-[var(--color-status-info-bg-subtle)] px-2 py-0.5 text-[11px] font-semibold text-[var(--color-status-info-text)]\">\n <GitBranch className=\"size-3\" />\n {revision.liveVersion} live → {revision.version} in review\n </span>\n <span className=\"rounded-full bg-[var(--color-status-warning-bg-subtle)] px-2 py-0.5 text-[10px] font-medium uppercase text-[var(--color-status-warning-text)]\">\n Workflow template revision\n </span>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-[var(--color-text-secondary)]\">\n <span className=\"flex items-center gap-1\">\n <Hash className=\"size-3\" />\n <span className=\"font-mono\">{revision.productSlug}</span>\n </span>\n <span className=\"flex items-center gap-1\">\n <User className=\"size-3\" />\n {revision.authorName ?? revision.publisherName ?? revision.submittedBy}\n </span>\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n {formatDate(revision.submittedAt)}\n </span>\n <span>\n {revision.category} · {revision.nodeCount} node types\n </span>\n </div>\n <p className=\"text-sm text-[var(--color-text-secondary)]\">{revision.description}</p>\n {revision.notes && (\n <div className=\"rounded-md border border-[var(--color-border-seam)] bg-[var(--color-surface-default)] px-3 py-2 text-xs text-[var(--color-text-secondary)]\">\n <span className=\"font-semibold\">Note from the author: </span>\n <Markdown>{revision.notes}</Markdown>\n </div>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center justify-between gap-2 border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] px-5 py-3\">\n <button\n type=\"button\"\n onClick={() => setOpenId(openId === revision.id ? null : revision.id)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] transition-colors hover:bg-[var(--color-surface-hover)]\"\n >\n {openId === revision.id ? 'Hide template contents' : 'Preview template contents'}\n {openId === revision.id ? (\n <ChevronUp className=\"size-3\" />\n ) : (\n <ChevronDown className=\"size-3\" />\n )}\n </button>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => void approve(revision)}\n disabled={actingId === revision.id}\n className=\"inline-flex items-center gap-1.5 rounded-md bg-[var(--color-surface-success)] px-4 py-1.5 text-xs font-semibold text-[var(--color-text-on-success)] transition-opacity hover:opacity-90 disabled:opacity-50\"\n >\n <Check className=\"size-3.5\" />\n Approve revision\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(revision.id);\n setReason('');\n }}\n disabled={actingId === revision.id}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-4 py-1.5 text-xs font-semibold text-[var(--color-text-danger)] transition-colors hover:opacity-90 disabled:opacity-50\"\n >\n <X className=\"size-3.5\" />\n Reject revision\n </button>\n </div>\n </div>\n\n {openId === revision.id && (\n <div className=\"border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] p-5\">\n {revision.preview ? (\n <WorkflowTemplatePreviewPanel preview={revision.preview} />\n ) : (\n <p className=\"text-xs text-[var(--color-text-muted)]\">\n This revision has no workflow definition to preview.\n </p>\n )}\n </div>\n )}\n\n {rejectingId === revision.id && (\n <div className=\"border-t border-[var(--color-border-seam)] px-5 py-4\">\n <p className=\"mb-2 text-xs font-semibold text-[var(--color-text-danger)]\">\n Reject this revision. Version {revision.liveVersion} stays live; the author sees\n your reason.\n </p>\n <textarea\n aria-label=\"Revision rejection reason\"\n value={reason}\n onChange={(e) => setReason(e.target.value)}\n rows={3}\n required\n aria-required\n className=\"w-full rounded-lg border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] focus:border-[var(--color-border-accent)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] resize-none\"\n placeholder=\"Be specific, for example 'The new HTTP node posts to a personal endpoint'\"\n />\n <div className=\"mt-2 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(null);\n setReason('');\n }}\n className=\"rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)]\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void reject(revision)}\n disabled={!reason.trim() || actingId === revision.id}\n className=\"rounded-md bg-[var(--color-surface-danger)] px-3 py-1.5 text-xs font-semibold text-[var(--color-text-on-danger)] hover:opacity-90 disabled:opacity-50\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n {revisions.length > 0 && revisions.length < totalCount && (\n <div className=\"flex justify-center\">\n <button\n type=\"button\"\n onClick={() => void load(revisions.length)}\n disabled={loading}\n className=\"inline-flex items-center gap-2 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-4 py-1.5 text-sm text-[var(--color-text-primary)] transition-colors hover:bg-[var(--color-surface-hover)] disabled:opacity-50\"\n >\n {loading && <RefreshCw className=\"size-4 animate-spin\" />}\n Load more\n </button>\n </div>\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reviewer preview of a community workflow template (BOFF-10463): what the
|
|
5
|
+
* template does before a store admin approves it. Mirrors the store service's
|
|
6
|
+
* `WorkflowTemplatePreview` type.
|
|
7
|
+
*/
|
|
8
|
+
export type WorkflowTemplateFieldRef = {
|
|
9
|
+
nodeId?: string | null;
|
|
10
|
+
nodeType?: string | null;
|
|
11
|
+
nodeLabel?: string | null;
|
|
12
|
+
field: string;
|
|
13
|
+
};
|
|
14
|
+
export type WorkflowTemplatePreview = {
|
|
15
|
+
nodeTypes: Array<{
|
|
16
|
+
nodeType: string;
|
|
17
|
+
nodeVersion: string;
|
|
18
|
+
publishType: string;
|
|
19
|
+
displayName: string;
|
|
20
|
+
}>;
|
|
21
|
+
nodeCount: number;
|
|
22
|
+
edgeCount: number;
|
|
23
|
+
requiredInputs: Array<{
|
|
24
|
+
name: string;
|
|
25
|
+
type: string;
|
|
26
|
+
label: string;
|
|
27
|
+
required: boolean;
|
|
28
|
+
usedBy: string[];
|
|
29
|
+
}>;
|
|
30
|
+
requiredCredentials: Array<{
|
|
31
|
+
name: string;
|
|
32
|
+
kind: string;
|
|
33
|
+
nodeId?: string | null;
|
|
34
|
+
nodeType?: string | null;
|
|
35
|
+
nodeLabel?: string | null;
|
|
36
|
+
}>;
|
|
37
|
+
cleaningReport?: {
|
|
38
|
+
connectionsCleared: number;
|
|
39
|
+
secretsRemoved: number;
|
|
40
|
+
pinnedDataRemoved: boolean;
|
|
41
|
+
clearedConnections: WorkflowTemplateFieldRef[];
|
|
42
|
+
removedSecrets: WorkflowTemplateFieldRef[];
|
|
43
|
+
} | null;
|
|
44
|
+
definitionJson?: Record<string, unknown> | null;
|
|
45
|
+
};
|
|
46
|
+
/** GraphQL selection for a `WorkflowTemplatePreview` field. */
|
|
47
|
+
export declare const WORKFLOW_TEMPLATE_PREVIEW_FIELDS = "\n nodeTypes { nodeType nodeVersion publishType displayName }\n nodeCount\n edgeCount\n requiredInputs { name type label required usedBy }\n requiredCredentials { name kind nodeId nodeType nodeLabel }\n cleaningReport {\n connectionsCleared\n secretsRemoved\n pinnedDataRemoved\n clearedConnections { nodeId nodeType nodeLabel field }\n removedSecrets { nodeId nodeType nodeLabel field }\n }\n definitionJson\n";
|
|
48
|
+
export declare const WorkflowTemplatePreviewPanel: FC<{
|
|
49
|
+
preview: WorkflowTemplatePreview;
|
|
50
|
+
}>;
|