@burdenoff/microfe-store 2026.714.4 → 2026.714.5

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.
@@ -6,9 +6,9 @@ import { ArrowLeft as ee, Check as o, ChevronDown as s, ChevronUp as c, Clock as
6
6
  import { graphqlFetch as g } from "@burdenoff/fe-libs/shared/graphql";
7
7
  import { jsx as _, jsxs as v } from "react/jsx-runtime";
8
8
  import { useNavigate as se } from "react-router-dom";
9
- import { IllustratedEmptyState as ce, PagePurpose as le } from "@burdenoff/fe-libs/ui";
9
+ import { IllustratedEmptyState as y, PagePurpose as ce } from "@burdenoff/fe-libs/ui";
10
10
  //#region src/pages/AdminSubmissionsQueuePage.tsx
11
- var ue = "\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pendingReviewProducts(limit: $limit, offset: $offset) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n zipFileUrl\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n }\n", de = "\n query AdminProducts($limit: Int, $offset: Int, $status: ProductStatus) {\n products(filter: { status: $status }, limit: $limit, offset: $offset) {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n zipFileUrl\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n", fe = "\n mutation ApproveProduct($id: ID!, $notes: String) {\n approveProduct(id: $id, notes: $notes) {\n id\n status\n publishedAt\n }\n }\n", pe = "\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n", me = "\n query AdminPendingVersionApps($limit: Int, $offset: Int) {\n applicationsWithPendingVersions(limit: $limit, offset: $offset) {\n totalCount\n edges {\n node {\n id\n name\n slug\n iconUrl\n shortDescription\n description\n versions(first: 5) {\n edges {\n node {\n id\n version\n status\n releaseNotes\n packageUrl\n packageSize\n isLatest\n reviewNotes\n manifest\n createdAt\n }\n }\n }\n }\n }\n }\n }\n", he = "\n mutation AdminApproveApplicationVersion($id: ID!, $notes: String) {\n approveApplicationVersion(id: $id, notes: $notes) {\n id\n status\n }\n }\n", ge = "\n mutation AdminRejectApplicationVersion($id: ID!, $reason: String!) {\n rejectApplicationVersion(id: $id, reason: $reason) {\n id\n status\n }\n }\n", _e = [
11
+ var le = "\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pendingReviewProducts(limit: $limit, offset: $offset) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n zipFileUrl\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n }\n", ue = "\n query AdminProducts($limit: Int, $offset: Int, $status: ProductStatus) {\n products(filter: { status: $status }, limit: $limit, offset: $offset) {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n zipFileUrl\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n", de = "\n mutation ApproveProduct($id: ID!, $notes: String) {\n approveProduct(id: $id, notes: $notes) {\n id\n status\n publishedAt\n }\n }\n", fe = "\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n", pe = "\n query AdminAppPackageUrl($id: ID!) {\n application(id: $id) {\n id\n versions(first: 1) {\n edges {\n node {\n packageUrl\n }\n }\n }\n }\n }\n", me = "\n query AdminPendingVersionApps($limit: Int, $offset: Int) {\n applicationsWithPendingVersions(limit: $limit, offset: $offset) {\n totalCount\n edges {\n node {\n id\n name\n slug\n iconUrl\n shortDescription\n description\n versions(first: 5) {\n edges {\n node {\n id\n version\n status\n releaseNotes\n packageUrl\n packageSize\n isLatest\n reviewNotes\n manifest\n createdAt\n }\n }\n }\n }\n }\n }\n }\n", he = "\n mutation AdminApproveApplicationVersion($id: ID!, $notes: String) {\n approveApplicationVersion(id: $id, notes: $notes) {\n id\n status\n }\n }\n", ge = "\n mutation AdminRejectApplicationVersion($id: ID!, $reason: String!) {\n rejectApplicationVersion(id: $id, reason: $reason) {\n id\n status\n }\n }\n", _e = [
12
12
  {
13
13
  label: "Pending review",
14
14
  value: "PENDING_REVIEW"
@@ -29,16 +29,16 @@ var ue = "\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pend
29
29
  label: "All",
30
30
  value: "ALL"
31
31
  }
32
- ], y = {
32
+ ], b = {
33
33
  NODE: "bg-[var(--color-status-info-bg-subtle)] text-[var(--color-status-info-text)]",
34
34
  WORKFLOW: "bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]",
35
35
  INTEGRATION: "bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)]",
36
36
  VIBEMODULE: "bg-[var(--color-surface-accent-muted)] text-[var(--color-text-accent)]"
37
- }, b = {
37
+ }, x = {
38
38
  PENDING_REVIEW: "bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]",
39
39
  PUBLISHED: "bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)]",
40
40
  REJECTED: "bg-[var(--color-status-error-bg-subtle)] text-[var(--color-status-error-text)]"
41
- }, x = new Set([
41
+ }, S = new Set([
42
42
  "NODE",
43
43
  "WORKFLOW",
44
44
  "VIBEMODULE",
@@ -47,12 +47,12 @@ var ue = "\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pend
47
47
  "CONSOLE"
48
48
  ]);
49
49
  function ve(e) {
50
- return y[e] ?? "bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]";
50
+ return b[e] ?? "bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]";
51
51
  }
52
52
  function ye(e) {
53
- return b[e] ?? "bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]";
53
+ return x[e] ?? "bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]";
54
54
  }
55
- function S(e) {
55
+ function C(e) {
56
56
  return e ? new Date(e).toLocaleString(void 0, {
57
57
  month: "short",
58
58
  day: "numeric",
@@ -61,9 +61,9 @@ function S(e) {
61
61
  minute: "2-digit"
62
62
  }) : "—";
63
63
  }
64
- var C = () => {
65
- let y = se(), { authToken: b, basePath: C } = e(), [w, T] = a([]), [E, D] = a(0), [O, k] = a(!1), [A, j] = a(null), [M, N] = a(null), [P, be] = a("PENDING_REVIEW"), [xe, F] = a(null), [I, L] = a(""), [R, z] = a(null), [B, V] = a(null), [H, Se] = a([]), [U, Ce] = a(0), [W, G] = a(null), [K, q] = a(null), [J, Y] = a(""), [we, X] = a(null), Z = r(async () => {
66
- k(!0), j(null);
64
+ var w = () => {
65
+ let b = se(), { authToken: x, basePath: w } = e(), [T, E] = a([]), [D, O] = a(0), [k, A] = a(!1), [j, M] = a(null), [N, P] = a(null), [F, be] = a("PENDING_REVIEW"), [xe, I] = a(null), [L, R] = a(""), [z, B] = a(null), [V, H] = a(null), [U, Se] = a([]), [W, Ce] = a(0), [G, K] = a(null), [q, J] = a(null), [Y, X] = a(""), [we, Z] = a(null), [Te, Ee] = a({}), Q = r(async () => {
66
+ A(!0), M(null);
67
67
  try {
68
68
  let e = await g({
69
69
  gateway: "global",
@@ -72,17 +72,17 @@ var C = () => {
72
72
  limit: 50,
73
73
  offset: 0
74
74
  },
75
- authToken: b || void 0
75
+ authToken: x || void 0
76
76
  });
77
77
  if (e.errors?.length) throw Error(e.errors[0]?.message ?? "Failed to load version updates");
78
78
  Se((e.data?.applicationsWithPendingVersions.edges ?? []).map((e) => e.node)), Ce(e.data?.applicationsWithPendingVersions.totalCount ?? 0);
79
79
  } catch (e) {
80
- j(e instanceof Error ? e.message : "Failed to load version updates");
80
+ M(e instanceof Error ? e.message : "Failed to load version updates");
81
81
  } finally {
82
- k(!1);
82
+ A(!1);
83
83
  }
84
- }, [b]), Te = async (e, t, n) => {
85
- G(e), j(null);
84
+ }, [x]), De = async (e, t, n) => {
85
+ K(e), M(null);
86
86
  try {
87
87
  let t = await g({
88
88
  gateway: "global",
@@ -91,21 +91,21 @@ var C = () => {
91
91
  id: e,
92
92
  notes: n || null
93
93
  },
94
- authToken: b || void 0
94
+ authToken: x || void 0
95
95
  });
96
96
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Approval failed");
97
- await Z();
97
+ await Q();
98
98
  } catch (e) {
99
- j(e instanceof Error ? e.message : "Approval failed");
99
+ M(e instanceof Error ? e.message : "Approval failed");
100
100
  } finally {
101
- G(null);
101
+ K(null);
102
102
  }
103
- }, Ee = async (e, t, n) => {
103
+ }, Oe = async (e, t, n) => {
104
104
  if (!n.trim()) {
105
- j("Rejection reason is required");
105
+ M("Rejection reason is required");
106
106
  return;
107
107
  }
108
- G(e), j(null);
108
+ K(e), M(null);
109
109
  try {
110
110
  let t = await g({
111
111
  gateway: "global",
@@ -114,103 +114,122 @@ var C = () => {
114
114
  id: e,
115
115
  reason: n.trim()
116
116
  },
117
- authToken: b || void 0
117
+ authToken: x || void 0
118
118
  });
119
119
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Rejection failed");
120
- q(null), Y(""), X(null), await Z();
120
+ J(null), X(""), Z(null), await Q();
121
121
  } catch (e) {
122
- j(e instanceof Error ? e.message : "Rejection failed");
122
+ M(e instanceof Error ? e.message : "Rejection failed");
123
123
  } finally {
124
- G(null);
124
+ K(null);
125
125
  }
126
- }, Q = r(async (e) => {
126
+ }, $ = r(async (e) => {
127
127
  if (e === "VERSION_UPDATES") {
128
- await Z();
128
+ await Q();
129
129
  return;
130
130
  }
131
- k(!0), j(null);
131
+ A(!0), M(null);
132
132
  try {
133
133
  if (e === "PENDING_REVIEW") {
134
134
  let e = await g({
135
135
  gateway: "global",
136
- query: ue,
136
+ query: le,
137
137
  variables: {
138
138
  limit: 100,
139
139
  offset: 0
140
140
  },
141
- authToken: b || void 0
141
+ authToken: x || void 0
142
142
  });
143
143
  if (e.errors?.length) throw Error(e.errors[0]?.message ?? "Failed to load pending products");
144
- T(e.data?.pendingReviewProducts.products ?? []), D(e.data?.pendingReviewProducts.totalCount ?? 0);
144
+ E(e.data?.pendingReviewProducts.products ?? []), O(e.data?.pendingReviewProducts.totalCount ?? 0);
145
145
  } else {
146
146
  let t = await g({
147
147
  gateway: "global",
148
- query: de,
148
+ query: ue,
149
149
  variables: {
150
150
  limit: 100,
151
151
  offset: 0,
152
152
  ...e !== "ALL" && { status: e }
153
153
  },
154
- authToken: b || void 0
154
+ authToken: x || void 0
155
155
  });
156
156
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Failed to load products");
157
157
  let n = t.data?.products ?? [];
158
- T(n), D(n.length);
158
+ E(n), O(n.length);
159
159
  }
160
160
  } catch (e) {
161
- j(e instanceof Error ? e.message : "Failed to load products");
161
+ M(e instanceof Error ? e.message : "Failed to load products");
162
162
  } finally {
163
- k(!1);
163
+ A(!1);
164
164
  }
165
- }, [b, Z]);
165
+ }, [x, Q]);
166
166
  i(() => {
167
- Q(P);
168
- }, [Q, P]), i(() => {
169
- Z();
170
- }, [Z]);
171
- let De = async (e) => {
172
- N(e), j(null);
167
+ $(F);
168
+ }, [$, F]), i(() => {
169
+ Q();
170
+ }, [Q]), i(() => {
171
+ let e = T.filter((e) => S.has(e.type) && e.productId && !e.zipFileUrl);
172
+ e.length !== 0 && (async () => {
173
+ let t = await Promise.allSettled(e.map((e) => g({
174
+ gateway: "global",
175
+ query: pe,
176
+ variables: { id: e.productId },
177
+ authToken: x || void 0
178
+ })));
179
+ Ee((n) => {
180
+ let r = { ...n };
181
+ return t.forEach((t, n) => {
182
+ if (t.status === "fulfilled") {
183
+ let i = t.value.data?.application?.versions?.edges?.[0]?.node?.packageUrl, a = e[n].productId;
184
+ i && a && (r[a] = i);
185
+ }
186
+ }), r;
187
+ });
188
+ })();
189
+ }, [T, x]);
190
+ let ke = async (e) => {
191
+ P(e), M(null);
173
192
  try {
174
193
  let t = await g({
175
194
  gateway: "global",
176
- query: fe,
195
+ query: de,
177
196
  variables: {
178
197
  id: e,
179
198
  notes: null
180
199
  },
181
- authToken: b || void 0
200
+ authToken: x || void 0
182
201
  });
183
202
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Approval failed");
184
- await Q(P);
203
+ await $(F);
185
204
  } catch (e) {
186
- j(e instanceof Error ? e.message : "Approval failed");
205
+ M(e instanceof Error ? e.message : "Approval failed");
187
206
  } finally {
188
- N(null);
207
+ P(null);
189
208
  }
190
- }, Oe = async (e) => {
191
- if (!I.trim()) {
192
- j("Rejection reason is required");
209
+ }, Ae = async (e) => {
210
+ if (!L.trim()) {
211
+ M("Rejection reason is required");
193
212
  return;
194
213
  }
195
- N(e), j(null);
214
+ P(e), M(null);
196
215
  try {
197
216
  let t = await g({
198
217
  gateway: "global",
199
- query: pe,
218
+ query: fe,
200
219
  variables: {
201
220
  id: e,
202
- reason: I.trim()
221
+ reason: L.trim()
203
222
  },
204
- authToken: b || void 0
223
+ authToken: x || void 0
205
224
  });
206
225
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Rejection failed");
207
- F(null), L(""), await Q(P);
226
+ I(null), R(""), await $(F);
208
227
  } catch (e) {
209
- j(e instanceof Error ? e.message : "Rejection failed");
228
+ M(e instanceof Error ? e.message : "Rejection failed");
210
229
  } finally {
211
- N(null);
230
+ P(null);
212
231
  }
213
- }, $ = w.filter((e) => e.status === "PENDING_REVIEW").length;
232
+ }, je = T.filter((e) => e.status === "PENDING_REVIEW").length;
214
233
  return /* @__PURE__ */ v("div", {
215
234
  className: "min-h-screen bg-[var(--color-surface-default)]",
216
235
  children: [
@@ -225,7 +244,7 @@ var C = () => {
225
244
  children: [
226
245
  /* @__PURE__ */ v("button", {
227
246
  type: "button",
228
- onClick: () => y(`${C}/dashboard`),
247
+ onClick: () => b(`${w}/dashboard`),
229
248
  className: "inline-flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-sm text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors",
230
249
  children: [/* @__PURE__ */ _(ee, { className: "size-4 shrink-0" }), "Back"]
231
250
  }),
@@ -235,33 +254,33 @@ var C = () => {
235
254
  className: "text-lg font-semibold text-[var(--color-text-primary)] truncate",
236
255
  children: "Submission Queue"
237
256
  }),
238
- E > 0 && /* @__PURE__ */ _("span", {
257
+ D > 0 && /* @__PURE__ */ _("span", {
239
258
  className: "shrink-0 rounded-full bg-[var(--color-status-warning-bg-subtle)] px-2 py-0.5 text-xs font-semibold text-[var(--color-status-warning-text)]",
240
- children: E
259
+ children: D
241
260
  })
242
261
  ]
243
262
  }), /* @__PURE__ */ v("button", {
244
263
  type: "button",
245
- onClick: () => void Q(P),
264
+ onClick: () => void $(F),
246
265
  className: "shrink-0 inline-flex items-center gap-2 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-sm text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)] transition-colors disabled:opacity-50",
247
- disabled: O,
248
- children: [/* @__PURE__ */ _(m, { className: `size-4 ${O ? "animate-spin" : ""}` }), "Refresh"]
266
+ disabled: k,
267
+ children: [/* @__PURE__ */ _(m, { className: `size-4 ${k ? "animate-spin" : ""}` }), "Refresh"]
249
268
  })]
250
269
  }), /* @__PURE__ */ _("div", {
251
270
  className: "mt-3 flex gap-1.5 flex-wrap",
252
271
  children: _e.map((e) => /* @__PURE__ */ v("button", {
253
272
  type: "button",
254
273
  onClick: () => be(e.value),
255
- className: `rounded-full px-3.5 py-1 text-xs font-medium transition-colors ${P === e.value ? "bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]" : "bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)]"}`,
274
+ className: `rounded-full px-3.5 py-1 text-xs font-medium transition-colors ${F === e.value ? "bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]" : "bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)]"}`,
256
275
  children: [
257
276
  e.label,
258
- e.value === "PENDING_REVIEW" && $ > 0 && P !== "PENDING_REVIEW" && /* @__PURE__ */ _("span", {
277
+ e.value === "PENDING_REVIEW" && je > 0 && F !== "PENDING_REVIEW" && /* @__PURE__ */ _("span", {
259
278
  className: "ml-1.5 rounded-full bg-[var(--color-status-warning-text)] px-1.5 py-0.5 text-[10px] text-white",
260
- children: $
279
+ children: je
261
280
  }),
262
- e.value === "VERSION_UPDATES" && U > 0 && P !== "VERSION_UPDATES" && /* @__PURE__ */ _("span", {
281
+ e.value === "VERSION_UPDATES" && W > 0 && F !== "VERSION_UPDATES" && /* @__PURE__ */ _("span", {
263
282
  className: "ml-1.5 rounded-full bg-[var(--color-status-info-text)] px-1.5 py-0.5 text-[10px] text-white",
264
- children: U
283
+ children: W
265
284
  })
266
285
  ]
267
286
  }, e.value))
@@ -271,25 +290,25 @@ var C = () => {
271
290
  /* @__PURE__ */ v("main", {
272
291
  className: "mx-auto max-w-5xl space-y-4 p-6",
273
292
  children: [
274
- /* @__PURE__ */ _(le, {
293
+ /* @__PURE__ */ _(ce, {
275
294
  className: "mb-2",
276
295
  children: "Every publisher submission lands here for your review. Inspect the manifest, permissions, and package details — then approve to publish or reject with a clear reason the developer will see."
277
296
  }),
278
- A && /* @__PURE__ */ _("div", {
297
+ j && /* @__PURE__ */ _("div", {
279
298
  role: "alert",
280
299
  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)]",
281
- children: A
300
+ children: j
282
301
  }),
283
- P === "VERSION_UPDATES" ? O && H.length === 0 ? /* @__PURE__ */ _("div", {
302
+ F === "VERSION_UPDATES" ? k && U.length === 0 ? /* @__PURE__ */ _("div", {
284
303
  className: "flex items-center justify-center py-24",
285
304
  children: /* @__PURE__ */ _(m, { className: "size-6 animate-spin text-[var(--color-text-muted)]" })
286
- }) : H.length === 0 ? /* @__PURE__ */ _(ce, {
305
+ }) : U.length === 0 ? /* @__PURE__ */ _(y, {
287
306
  illustration: "empty-data",
288
307
  title: "No pending version updates",
289
308
  description: "All published apps are up to date. When a developer submits a new version, it will appear here for your review."
290
309
  }) : /* @__PURE__ */ _("ul", {
291
310
  className: "space-y-4",
292
- children: H.map((e) => {
311
+ children: U.map((e) => {
293
312
  let t = e.versions.edges.map((e) => e.node).filter((e) => e.status === "pending_review");
294
313
  return t.length === 0 ? null : /* @__PURE__ */ v("li", {
295
314
  className: "overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]",
@@ -357,7 +376,7 @@ var C = () => {
357
376
  }),
358
377
  /* @__PURE__ */ v("span", {
359
378
  className: "ml-auto flex items-center gap-1 text-xs text-[var(--color-text-secondary)]",
360
- children: [/* @__PURE__ */ _(l, { className: "size-3" }), S(t.createdAt)]
379
+ children: [/* @__PURE__ */ _(l, { className: "size-3" }), C(t.createdAt)]
361
380
  })
362
381
  ]
363
382
  }),
@@ -387,20 +406,20 @@ var C = () => {
387
406
  ]
388
407
  }), t.manifest && /* @__PURE__ */ v("button", {
389
408
  type: "button",
390
- onClick: () => z(R === t.id ? null : t.id),
409
+ onClick: () => B(z === t.id ? null : t.id),
391
410
  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)] hover:bg-[var(--color-surface-hover)] transition-colors",
392
411
  children: [
393
412
  /* @__PURE__ */ _(f, { className: "size-3.5" }),
394
- R === t.id ? "Hide manifest" : "View manifest",
395
- R === t.id ? /* @__PURE__ */ _(c, { className: "size-3" }) : /* @__PURE__ */ _(s, { className: "size-3" })
413
+ z === t.id ? "Hide manifest" : "View manifest",
414
+ z === t.id ? /* @__PURE__ */ _(c, { className: "size-3" }) : /* @__PURE__ */ _(s, { className: "size-3" })
396
415
  ]
397
416
  })]
398
417
  }),
399
- R === t.id && t.manifest && /* @__PURE__ */ _("pre", {
418
+ z === t.id && t.manifest && /* @__PURE__ */ _("pre", {
400
419
  className: "overflow-x-auto rounded-lg border border-[var(--color-border-seam)] bg-[var(--color-surface-default)] p-4 text-[11px] leading-relaxed text-[var(--color-text-primary)] max-h-48 overflow-y-auto",
401
420
  children: JSON.stringify(t.manifest, null, 2)
402
421
  }),
403
- K === t.id && we === e.id && /* @__PURE__ */ v("div", {
422
+ q === t.id && we === e.id && /* @__PURE__ */ v("div", {
404
423
  className: "rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)]/30 p-3 space-y-2",
405
424
  children: [
406
425
  /* @__PURE__ */ _("p", {
@@ -408,8 +427,8 @@ var C = () => {
408
427
  children: "Rejection reason (the developer will see this)"
409
428
  }),
410
429
  /* @__PURE__ */ _("textarea", {
411
- value: J,
412
- onChange: (e) => Y(e.target.value),
430
+ value: Y,
431
+ onChange: (e) => X(e.target.value),
413
432
  rows: 2,
414
433
  className: "w-full rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] resize-none",
415
434
  placeholder: "Be specific — what needs to change before this version can be approved…"
@@ -419,14 +438,14 @@ var C = () => {
419
438
  children: [/* @__PURE__ */ _("button", {
420
439
  type: "button",
421
440
  onClick: () => {
422
- q(null), Y(""), X(null);
441
+ J(null), X(""), Z(null);
423
442
  },
424
443
  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-primary)] hover:bg-[var(--color-surface-hover)] transition-colors",
425
444
  children: "Cancel"
426
445
  }), /* @__PURE__ */ _("button", {
427
446
  type: "button",
428
- onClick: () => void Ee(t.id, e.id, J),
429
- disabled: !J.trim() || W === t.id,
447
+ onClick: () => void Oe(t.id, e.id, Y),
448
+ disabled: !Y.trim() || G === t.id,
430
449
  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 transition-opacity",
431
450
  children: "Submit rejection"
432
451
  })]
@@ -437,16 +456,16 @@ var C = () => {
437
456
  className: "flex items-center gap-2",
438
457
  children: [/* @__PURE__ */ v("button", {
439
458
  type: "button",
440
- onClick: () => void Te(t.id, e.id, ""),
441
- disabled: W === t.id,
459
+ onClick: () => void De(t.id, e.id, ""),
460
+ disabled: G === t.id,
442
461
  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)] hover:opacity-90 disabled:opacity-50 transition-opacity",
443
- children: [W === t.id ? /* @__PURE__ */ _(m, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ _(o, { className: "size-3.5" }), "Approve version"]
444
- }), K !== t.id && /* @__PURE__ */ v("button", {
462
+ children: [G === t.id ? /* @__PURE__ */ _(m, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ _(o, { className: "size-3.5" }), "Approve version"]
463
+ }), q !== t.id && /* @__PURE__ */ v("button", {
445
464
  type: "button",
446
465
  onClick: () => {
447
- q(t.id), Y(""), X(e.id);
466
+ J(t.id), X(""), Z(e.id);
448
467
  },
449
- disabled: W === t.id,
468
+ disabled: G === t.id,
450
469
  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)] hover:bg-[var(--color-surface-danger)] hover:text-[var(--color-text-on-danger)] disabled:opacity-50 transition-colors",
451
470
  children: [/* @__PURE__ */ _(h, { className: "size-3.5" }), "Reject version"]
452
471
  })]
@@ -456,16 +475,16 @@ var C = () => {
456
475
  })]
457
476
  }, e.id);
458
477
  })
459
- }) : O && w.length === 0 ? /* @__PURE__ */ _("div", {
478
+ }) : k && T.length === 0 ? /* @__PURE__ */ _("div", {
460
479
  className: "flex items-center justify-center py-24",
461
480
  children: /* @__PURE__ */ _(m, { className: "size-6 animate-spin text-[var(--color-text-muted)]" })
462
- }) : w.length === 0 ? /* @__PURE__ */ _(ce, {
463
- illustration: P === "PENDING_REVIEW" ? "empty-notifications" : "empty-data",
464
- title: P === "PENDING_REVIEW" ? "Queue is clear" : "No products match this filter",
465
- description: P === "PENDING_REVIEW" ? "No submissions are awaiting review. New submissions will appear here as publishers send them in." : "Nothing matches the current filter. Try switching to a different status above."
481
+ }) : T.length === 0 ? /* @__PURE__ */ _(y, {
482
+ illustration: F === "PENDING_REVIEW" ? "empty-notifications" : "empty-data",
483
+ title: F === "PENDING_REVIEW" ? "Queue is clear" : "No products match this filter",
484
+ description: F === "PENDING_REVIEW" ? "No submissions are awaiting review. New submissions will appear here as publishers send them in." : "Nothing matches the current filter. Try switching to a different status above."
466
485
  }) : /* @__PURE__ */ _("ul", {
467
486
  className: "space-y-3",
468
- children: w.map((e) => /* @__PURE__ */ v("li", {
487
+ children: T.map((e) => /* @__PURE__ */ v("li", {
469
488
  className: "overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)] transition-[box-shadow,border-color] hover:border-[var(--color-border-strong)] hover:shadow-[var(--shadow-elevation-2)]",
470
489
  children: [
471
490
  /* @__PURE__ */ v("div", {
@@ -524,7 +543,7 @@ var C = () => {
524
543
  }),
525
544
  e.submittedAt && /* @__PURE__ */ v("span", {
526
545
  className: "flex items-center gap-1",
527
- children: [/* @__PURE__ */ _(l, { className: "size-3" }), S(e.submittedAt)]
546
+ children: [/* @__PURE__ */ _(l, { className: "size-3" }), C(e.submittedAt)]
528
547
  }),
529
548
  /* @__PURE__ */ v("span", {
530
549
  className: "flex items-center gap-1",
@@ -551,8 +570,8 @@ var C = () => {
551
570
  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)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors",
552
571
  children: [/* @__PURE__ */ _(d, { className: "size-3.5" }), "Devportal"]
553
572
  }),
554
- x.has(e.type) && (() => {
555
- let t = e.zipFileUrl ?? H.find((t) => t.id === e.productId)?.versions?.edges?.[0]?.node?.packageUrl ?? null;
573
+ S.has(e.type) && (() => {
574
+ let t = e.zipFileUrl ?? (e.productId ? Te[e.productId] ?? null : null) ?? U.find((t) => t.id === e.productId)?.versions?.edges?.[0]?.node?.packageUrl ?? null;
556
575
  return t ? /* @__PURE__ */ v("a", {
557
576
  href: t,
558
577
  download: !0,
@@ -562,20 +581,20 @@ var C = () => {
562
581
  children: [/* @__PURE__ */ _(u, { className: "size-3.5" }), "Download Package"]
563
582
  }) : null;
564
583
  })(),
565
- x.has(e.type) && /* @__PURE__ */ v("button", {
584
+ S.has(e.type) && /* @__PURE__ */ v("button", {
566
585
  type: "button",
567
- onClick: () => V(e),
586
+ onClick: () => H(e),
568
587
  className: "inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-accent)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-accent)] hover:bg-[var(--color-surface-accent-muted)] transition-colors",
569
588
  children: [/* @__PURE__ */ _(te, { className: "size-3.5" }), "Test in workspace"]
570
589
  }),
571
590
  /* @__PURE__ */ v("button", {
572
591
  type: "button",
573
- onClick: () => z(R === e.id ? null : e.id),
592
+ onClick: () => B(z === e.id ? null : e.id),
574
593
  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)] hover:bg-[var(--color-surface-hover)] transition-colors",
575
594
  children: [
576
595
  /* @__PURE__ */ _(f, { className: "size-3.5" }),
577
- R === e.id ? "Hide details" : "Inspect details",
578
- R === e.id ? /* @__PURE__ */ _(c, { className: "size-3" }) : /* @__PURE__ */ _(s, { className: "size-3" })
596
+ z === e.id ? "Hide details" : "Inspect details",
597
+ z === e.id ? /* @__PURE__ */ _(c, { className: "size-3" }) : /* @__PURE__ */ _(s, { className: "size-3" })
579
598
  ]
580
599
  })
581
600
  ]
@@ -583,22 +602,22 @@ var C = () => {
583
602
  className: "flex items-center gap-2",
584
603
  children: [e.status !== "PUBLISHED" && /* @__PURE__ */ v("button", {
585
604
  type: "button",
586
- onClick: () => void De(e.id),
587
- disabled: M === e.id,
605
+ onClick: () => void ke(e.id),
606
+ disabled: N === e.id,
588
607
  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)] hover:opacity-90 disabled:opacity-50 transition-opacity",
589
608
  children: [/* @__PURE__ */ _(o, { className: "size-3.5" }), "Approve"]
590
609
  }), e.status !== "REJECTED" && /* @__PURE__ */ v("button", {
591
610
  type: "button",
592
611
  onClick: () => {
593
- F(e.id), L("");
612
+ I(e.id), R("");
594
613
  },
595
- disabled: M === e.id,
614
+ disabled: N === e.id,
596
615
  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)] hover:bg-[var(--color-surface-danger)] hover:text-[var(--color-text-on-danger)] disabled:opacity-50 transition-colors",
597
616
  children: [/* @__PURE__ */ _(h, { className: "size-3.5" }), "Reject"]
598
617
  })]
599
618
  })]
600
619
  }),
601
- R === e.id && /* @__PURE__ */ v("div", {
620
+ z === e.id && /* @__PURE__ */ v("div", {
602
621
  className: "border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] p-5 space-y-5",
603
622
  children: [
604
623
  /* @__PURE__ */ v("div", {
@@ -699,8 +718,8 @@ var C = () => {
699
718
  /* @__PURE__ */ _("textarea", {
700
719
  id: `reject-reason-${e.id}`,
701
720
  "aria-label": "Rejection reason",
702
- value: I,
703
- onChange: (e) => L(e.target.value),
721
+ value: L,
722
+ onChange: (e) => R(e.target.value),
704
723
  rows: 3,
705
724
  required: !0,
706
725
  "aria-required": "true",
@@ -712,14 +731,14 @@ var C = () => {
712
731
  children: [/* @__PURE__ */ _("button", {
713
732
  type: "button",
714
733
  onClick: () => {
715
- F(null), L("");
734
+ I(null), R("");
716
735
  },
717
736
  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-primary)] hover:bg-[var(--color-surface-hover)] transition-colors",
718
737
  children: "Cancel"
719
738
  }), /* @__PURE__ */ _("button", {
720
739
  type: "button",
721
- onClick: () => void Oe(e.id),
722
- disabled: !I.trim() || M === e.id,
740
+ onClick: () => void Ae(e.id),
741
+ disabled: !L.trim() || N === e.id,
723
742
  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 transition-opacity",
724
743
  children: "Submit rejection"
725
744
  })]
@@ -731,20 +750,20 @@ var C = () => {
731
750
  })
732
751
  ]
733
752
  }),
734
- B && /* @__PURE__ */ _(n, {
753
+ V && /* @__PURE__ */ _(n, {
735
754
  isOpen: !0,
736
- onClose: () => V(null),
755
+ onClose: () => H(null),
737
756
  app: {
738
- id: B.id,
739
- name: B.name,
740
- slug: B.slug,
741
- icon: B.icon ?? void 0,
742
- type: B.type,
743
- permissions: B.requiredPermissions?.length ? B.requiredPermissions : void 0
757
+ id: V.id,
758
+ name: V.name,
759
+ slug: V.slug,
760
+ icon: V.icon ?? void 0,
761
+ type: V.type,
762
+ permissions: V.requiredPermissions?.length ? V.requiredPermissions : void 0
744
763
  },
745
764
  purchaseState: "free",
746
765
  onInstallSuccess: () => {
747
- V(null);
766
+ H(null);
748
767
  },
749
768
  onInstallError: () => {}
750
769
  })
@@ -752,6 +771,6 @@ var C = () => {
752
771
  });
753
772
  };
754
773
  //#endregion
755
- export { C as AdminSubmissionsQueuePage };
774
+ export { w as AdminSubmissionsQueuePage };
756
775
 
757
776
  //# sourceMappingURL=AdminSubmissionsQueuePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AdminSubmissionsQueuePage.js","names":[],"sources":["../../src/pages/AdminSubmissionsQueuePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport {\n ArrowLeft,\n Check,\n X,\n RefreshCw,\n PackageCheck,\n ChevronDown,\n ChevronUp,\n ExternalLink,\n FileJson,\n ShieldCheck,\n FlaskConical,\n Download,\n Clock,\n Tag,\n User,\n Hash,\n GitBranch,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router-dom';\nimport { useStore } from '../providers/StoreProvider';\nimport { formatPriceForModel } from '../utils';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { InstallAppModal } from '../components/InstallAppModal';\n\ntype PendingProduct = {\n id: string;\n slug: string;\n name: string;\n type: string;\n nature: string;\n status: string;\n pricingModel: string;\n price: number;\n currency: string;\n description?: string | null;\n submittedAt?: string | null;\n submittedBy?: string | null;\n category?: string | null;\n icon?: string | null;\n productId?: string | null;\n zipFileUrl?: string | null;\n manifest?: Record<string, unknown> | null;\n requiredPermissions?: string[] | null;\n publisher?: { id: string; name: string; email: string } | null;\n};\n\ntype ProductStatus = 'PENDING_REVIEW' | 'PUBLISHED' | 'REJECTED' | 'ALL' | 'VERSION_UPDATES';\n\ntype PendingVersion = {\n id: string;\n version: string;\n status: string;\n releaseNotes?: string | null;\n packageUrl?: string | null;\n packageSize?: number | null;\n isLatest?: boolean | null;\n reviewNotes?: string | null;\n manifest?: Record<string, unknown> | null;\n createdAt: string;\n};\n\ntype VersionUpdateApp = {\n id: string;\n name: string;\n slug: string;\n iconUrl?: string | null;\n shortDescription?: string | null;\n description?: string | null;\n versions: { edges: Array<{ node: PendingVersion }> };\n};\n\nconst PENDING_PRODUCTS_QUERY = /* GraphQL */ `\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pendingReviewProducts(limit: $limit, offset: $offset) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n zipFileUrl\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n }\n`;\n\nconst ALL_PRODUCTS_QUERY = /* GraphQL */ `\n query AdminProducts($limit: Int, $offset: Int, $status: ProductStatus) {\n products(filter: { status: $status }, limit: $limit, offset: $offset) {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n zipFileUrl\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n`;\n\nconst APPROVE_PRODUCT_MUTATION = /* GraphQL */ `\n mutation ApproveProduct($id: ID!, $notes: String) {\n approveProduct(id: $id, notes: $notes) {\n id\n status\n publishedAt\n }\n }\n`;\n\nconst REJECT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n`;\n\nconst VERSION_UPDATES_QUERY = /* GraphQL */ `\n query AdminPendingVersionApps($limit: Int, $offset: Int) {\n applicationsWithPendingVersions(limit: $limit, offset: $offset) {\n totalCount\n edges {\n node {\n id\n name\n slug\n iconUrl\n shortDescription\n description\n versions(first: 5) {\n edges {\n node {\n id\n version\n status\n releaseNotes\n packageUrl\n packageSize\n isLatest\n reviewNotes\n manifest\n createdAt\n }\n }\n }\n }\n }\n }\n }\n`;\n\nconst APPROVE_VERSION_MUTATION = /* GraphQL */ `\n mutation AdminApproveApplicationVersion($id: ID!, $notes: String) {\n approveApplicationVersion(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\nconst REJECT_VERSION_MUTATION = /* GraphQL */ `\n mutation AdminRejectApplicationVersion($id: ID!, $reason: String!) {\n rejectApplicationVersion(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n\nconst STATUS_FILTERS: Array<{ label: string; value: ProductStatus }> = [\n { label: 'Pending review', value: 'PENDING_REVIEW' },\n { label: 'Published', value: 'PUBLISHED' },\n { label: 'Rejected', value: 'REJECTED' },\n { label: 'Version Updates', value: 'VERSION_UPDATES' },\n { label: 'All', value: 'ALL' },\n];\n\nconst TYPE_COLORS: Record<string, string> = {\n NODE: 'bg-[var(--color-status-info-bg-subtle)] text-[var(--color-status-info-text)]',\n WORKFLOW: 'bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]',\n INTEGRATION: 'bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)]',\n VIBEMODULE: 'bg-[var(--color-surface-accent-muted)] text-[var(--color-text-accent)]',\n};\n\nconst STATUS_COLORS: Record<string, string> = {\n PENDING_REVIEW:\n 'bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]',\n PUBLISHED: 'bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)]',\n REJECTED: 'bg-[var(--color-status-error-bg-subtle)] text-[var(--color-status-error-text)]',\n};\n\nconst PACKAGE_TYPES = new Set(['NODE', 'WORKFLOW', 'VIBEMODULE', 'PARSER', 'DATASINK', 'CONSOLE']);\n\nfunction typeColor(type: string) {\n return TYPE_COLORS[type] ?? 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]';\n}\n\nfunction statusColor(status: string) {\n return (\n STATUS_COLORS[status] ?? 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]'\n );\n}\n\nfunction formatDate(iso?: string | null) {\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 AdminSubmissionsQueuePage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n const [products, setProducts] = useState<PendingProduct[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [actingId, setActingId] = useState<string | null>(null);\n const [statusFilter, setStatusFilter] = useState<ProductStatus>('PENDING_REVIEW');\n const [rejectingId, setRejectingId] = useState<string | null>(null);\n const [rejectReason, setRejectReason] = useState('');\n const [viewingId, setViewingId] = useState<string | null>(null);\n const [testingProduct, setTestingProduct] = useState<PendingProduct | null>(null);\n const [versionApps, setVersionApps] = useState<VersionUpdateApp[]>([]);\n const [versionUpdateCount, setVersionUpdateCount] = useState(0);\n const [versionActingId, setVersionActingId] = useState<string | null>(null);\n const [versionRejectingId, setVersionRejectingId] = useState<string | null>(null);\n const [versionRejectReason, setVersionRejectReason] = useState('');\n const [versionRejectAppId, setVersionRejectAppId] = useState<string | null>(null);\n\n const loadVersionApps = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n applicationsWithPendingVersions: {\n totalCount: number;\n edges: Array<{ node: VersionUpdateApp }>;\n };\n }>({\n gateway: 'global',\n query: VERSION_UPDATES_QUERY,\n variables: { limit: 50, offset: 0 },\n authToken: authToken || undefined,\n });\n if (result.errors?.length)\n throw new Error(result.errors[0]?.message ?? 'Failed to load version updates');\n const edges = result.data?.applicationsWithPendingVersions.edges ?? [];\n setVersionApps(edges.map((e) => e.node));\n setVersionUpdateCount(result.data?.applicationsWithPendingVersions.totalCount ?? 0);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load version updates');\n } finally {\n setLoading(false);\n }\n }, [authToken]);\n\n const approveVersion = async (versionId: string, appId: string, notes: string) => {\n setVersionActingId(versionId);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_VERSION_MUTATION,\n variables: { id: versionId, notes: notes || null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) throw new Error(result.errors[0]?.message ?? 'Approval failed');\n await loadVersionApps();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setVersionActingId(null);\n }\n };\n\n const rejectVersion = async (versionId: string, appId: string, reason: string) => {\n if (!reason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setVersionActingId(versionId);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_VERSION_MUTATION,\n variables: { id: versionId, reason: reason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n setVersionRejectingId(null);\n setVersionRejectReason('');\n setVersionRejectAppId(null);\n await loadVersionApps();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setVersionActingId(null);\n }\n };\n\n const loadProducts = useCallback(\n async (filter: ProductStatus) => {\n if (filter === 'VERSION_UPDATES') {\n await loadVersionApps();\n return;\n }\n setLoading(true);\n setError(null);\n try {\n if (filter === 'PENDING_REVIEW') {\n const result = await graphqlFetch<{\n pendingReviewProducts: { totalCount: number; products: PendingProduct[] };\n }>({\n gateway: 'global',\n query: PENDING_PRODUCTS_QUERY,\n variables: { limit: 100, offset: 0 },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load pending products');\n }\n setProducts(result.data?.pendingReviewProducts.products ?? []);\n setTotalCount(result.data?.pendingReviewProducts.totalCount ?? 0);\n } else {\n const result = await graphqlFetch<{ products: PendingProduct[] }>({\n gateway: 'global',\n query: ALL_PRODUCTS_QUERY,\n variables: {\n limit: 100,\n offset: 0,\n ...(filter !== 'ALL' && { status: filter }),\n },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load products');\n }\n const list = result.data?.products ?? [];\n setProducts(list);\n setTotalCount(list.length);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load products');\n } finally {\n setLoading(false);\n }\n },\n [authToken, loadVersionApps]\n );\n\n useEffect(() => {\n void loadProducts(statusFilter);\n }, [loadProducts, statusFilter]);\n\n // Keep version update count fresh for the badge even when not on that tab\n useEffect(() => {\n void loadVersionApps();\n }, [loadVersionApps]);\n\n const approve = async (id: string) => {\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_PRODUCT_MUTATION,\n variables: { id, notes: null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Approval failed');\n }\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setActingId(null);\n }\n };\n\n const reject = async (id: string) => {\n if (!rejectReason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_PRODUCT_MUTATION,\n variables: { id, reason: rejectReason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n }\n setRejectingId(null);\n setRejectReason('');\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setActingId(null);\n }\n };\n\n const pendingCount = products.filter((p) => p.status === 'PENDING_REVIEW').length;\n\n return (\n <div className=\"min-h-screen bg-[var(--color-surface-default)]\">\n {/* Header */}\n <header className=\"sticky top-0 z-10 border-b border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]\">\n <div className=\"mx-auto max-w-5xl px-6 py-4\">\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-sm text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <ArrowLeft className=\"size-4 shrink-0\" />\n Back\n </button>\n <div className=\"h-5 w-px bg-[var(--color-border-seam)]\" />\n <PackageCheck className=\"size-5 shrink-0 text-[var(--color-text-primary)]\" />\n <h1 className=\"text-lg font-semibold text-[var(--color-text-primary)] truncate\">\n Submission Queue\n </h1>\n {totalCount > 0 && (\n <span className=\"shrink-0 rounded-full bg-[var(--color-status-warning-bg-subtle)] px-2 py-0.5 text-xs font-semibold text-[var(--color-status-warning-text)]\">\n {totalCount}\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => void loadProducts(statusFilter)}\n className=\"shrink-0 inline-flex items-center gap-2 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-sm text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)] transition-colors disabled:opacity-50\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n\n {/* Status filter pills */}\n <div className=\"mt-3 flex gap-1.5 flex-wrap\">\n {STATUS_FILTERS.map((f) => (\n <button\n key={f.value}\n type=\"button\"\n onClick={() => setStatusFilter(f.value)}\n className={`rounded-full px-3.5 py-1 text-xs font-medium transition-colors ${\n statusFilter === f.value\n ? 'bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]'\n : 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)]'\n }`}\n >\n {f.label}\n {f.value === 'PENDING_REVIEW' &&\n pendingCount > 0 &&\n statusFilter !== 'PENDING_REVIEW' && (\n <span className=\"ml-1.5 rounded-full bg-[var(--color-status-warning-text)] px-1.5 py-0.5 text-[10px] text-white\">\n {pendingCount}\n </span>\n )}\n {f.value === 'VERSION_UPDATES' &&\n versionUpdateCount > 0 &&\n statusFilter !== 'VERSION_UPDATES' && (\n <span className=\"ml-1.5 rounded-full bg-[var(--color-status-info-text)] px-1.5 py-0.5 text-[10px] text-white\">\n {versionUpdateCount}\n </span>\n )}\n </button>\n ))}\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-5xl space-y-4 p-6\">\n <PagePurpose className=\"mb-2\">\n Every publisher submission lands here for your review. Inspect the manifest, permissions,\n and package details — then approve to publish or reject with a clear reason the developer\n will see.\n </PagePurpose>\n\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\n {statusFilter === 'VERSION_UPDATES' ? (\n loading && versionApps.length === 0 ? (\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 ) : versionApps.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No pending version updates\"\n description=\"All published apps are up to date. When a developer submits a new version, it will appear here for your review.\"\n />\n ) : (\n <ul className=\"space-y-4\">\n {versionApps.map((app) => {\n const pendingVersions = app.versions.edges\n .map((e) => e.node)\n .filter((v) => v.status === 'pending_review');\n if (pendingVersions.length === 0) return null;\n return (\n <li\n key={app.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 {/* App header */}\n <div className=\"flex items-center gap-4 p-5 border-b border-[var(--color-border-seam)]\">\n {app.iconUrl ? (\n <img\n src={app.iconUrl}\n alt=\"\"\n className=\"size-12 shrink-0 rounded-xl object-cover shadow-sm\"\n />\n ) : (\n <div className=\"flex size-12 shrink-0 items-center justify-center rounded-xl bg-[var(--color-surface-muted)] text-sm font-bold text-[var(--color-text-muted)] uppercase\">\n {app.name.slice(0, 2)}\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {app.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 {pendingVersions.length} version\n {pendingVersions.length !== 1 ? 's' : ''} pending\n </span>\n </div>\n {(app.shortDescription ?? app.description) && (\n <p className=\"mt-0.5 text-sm text-[var(--color-text-secondary)] line-clamp-1\">\n {app.shortDescription ?? app.description}\n </p>\n )}\n </div>\n <a\n href={`/devportal/apps/${app.id}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"shrink-0 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)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n Devportal\n </a>\n </div>\n\n {/* Pending versions */}\n <div className=\"divide-y divide-[var(--color-border-seam)]\">\n {pendingVersions.map((ver) => (\n <div key={ver.id} className=\"p-5 space-y-3\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <GitBranch className=\"size-4 text-[var(--color-text-secondary)] shrink-0\" />\n <span className=\"font-mono font-semibold text-sm text-[var(--color-text-primary)]\">\n {ver.version}\n </span>\n {ver.isLatest && (\n <span className=\"rounded-full bg-[var(--color-surface-muted)] px-2 py-0.5 text-[10px] text-[var(--color-text-secondary)]\">\n Latest\n </span>\n )}\n <span className=\"rounded-full bg-[var(--color-status-warning-bg-subtle)] px-2 py-0.5 text-[10px] font-medium text-[var(--color-status-warning-text)] uppercase\">\n New Version\n </span>\n <span className=\"ml-auto flex items-center gap-1 text-xs text-[var(--color-text-secondary)]\">\n <Clock className=\"size-3\" />\n {formatDate(ver.createdAt)}\n </span>\n </div>\n\n {ver.releaseNotes && (\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {ver.releaseNotes}\n </p>\n )}\n\n <div className=\"flex flex-wrap gap-2\">\n {ver.packageUrl && (\n <a\n href={ver.packageUrl}\n download\n target=\"_blank\"\n rel=\"noreferrer\"\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)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <Download className=\"size-3.5\" />\n Download Package\n {ver.packageSize != null && (\n <span className=\"text-[var(--color-text-muted)]\">\n ({ver.packageSize} bytes)\n </span>\n )}\n </a>\n )}\n {ver.manifest && (\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === ver.id ? null : ver.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)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <FileJson className=\"size-3.5\" />\n {viewingId === ver.id ? 'Hide manifest' : 'View manifest'}\n {viewingId === ver.id ? (\n <ChevronUp className=\"size-3\" />\n ) : (\n <ChevronDown className=\"size-3\" />\n )}\n </button>\n )}\n </div>\n\n {viewingId === ver.id && ver.manifest && (\n <pre className=\"overflow-x-auto rounded-lg border border-[var(--color-border-seam)] bg-[var(--color-surface-default)] p-4 text-[11px] leading-relaxed text-[var(--color-text-primary)] max-h-48 overflow-y-auto\">\n {JSON.stringify(ver.manifest, null, 2)}\n </pre>\n )}\n\n {/* Reject reason input */}\n {versionRejectingId === ver.id && versionRejectAppId === app.id && (\n <div className=\"rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)]/30 p-3 space-y-2\">\n <p className=\"text-xs font-semibold text-[var(--color-text-danger)]\">\n Rejection reason (the developer will see this)\n </p>\n <textarea\n value={versionRejectReason}\n onChange={(e) => setVersionRejectReason(e.target.value)}\n rows={2}\n className=\"w-full rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] resize-none\"\n placeholder=\"Be specific — what needs to change before this version can be approved…\"\n />\n <div className=\"flex gap-2 justify-end\">\n <button\n type=\"button\"\n onClick={() => {\n setVersionRejectingId(null);\n setVersionRejectReason('');\n setVersionRejectAppId(null);\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-primary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() =>\n void rejectVersion(ver.id, app.id, versionRejectReason)\n }\n disabled={\n !versionRejectReason.trim() || versionActingId === ver.id\n }\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 transition-opacity\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => void approveVersion(ver.id, app.id, '')}\n disabled={versionActingId === ver.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)] hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n {versionActingId === ver.id ? (\n <RefreshCw className=\"size-3.5 animate-spin\" />\n ) : (\n <Check className=\"size-3.5\" />\n )}\n Approve version\n </button>\n {versionRejectingId !== ver.id && (\n <button\n type=\"button\"\n onClick={() => {\n setVersionRejectingId(ver.id);\n setVersionRejectReason('');\n setVersionRejectAppId(app.id);\n }}\n disabled={versionActingId === ver.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)] hover:bg-[var(--color-surface-danger)] hover:text-[var(--color-text-on-danger)] disabled:opacity-50 transition-colors\"\n >\n <X className=\"size-3.5\" />\n Reject version\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n </li>\n );\n })}\n </ul>\n )\n ) : loading && products.length === 0 ? (\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 ) : products.length === 0 ? (\n <IllustratedEmptyState\n illustration={statusFilter === 'PENDING_REVIEW' ? 'empty-notifications' : 'empty-data'}\n title={\n statusFilter === 'PENDING_REVIEW' ? 'Queue is clear' : 'No products match this filter'\n }\n description={\n statusFilter === 'PENDING_REVIEW'\n ? 'No submissions are awaiting review. New submissions will appear here as publishers send them in.'\n : 'Nothing matches the current filter. Try switching to a different status above.'\n }\n />\n ) : (\n <ul className=\"space-y-3\">\n {products.map((p) => (\n <li\n key={p.id}\n className=\"overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)] transition-[box-shadow,border-color] hover:border-[var(--color-border-strong)] hover:shadow-[var(--shadow-elevation-2)]\"\n >\n {/* Card body */}\n <div className=\"flex items-start gap-4 p-5\">\n {/* App icon */}\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-14 shrink-0 rounded-xl object-cover shadow-sm\"\n />\n ) : (\n <div className=\"flex size-14 shrink-0 items-center justify-center rounded-xl bg-[var(--color-surface-muted)] text-sm font-bold text-[var(--color-text-muted)] uppercase\">\n {p.type.slice(0, 2)}\n </div>\n )}\n\n {/* Main info */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {p.name}\n </h3>\n <span\n className={`rounded-full px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide ${typeColor(p.type)}`}\n >\n {p.type}\n </span>\n <span\n className={`rounded-full px-2 py-0.5 text-[11px] font-medium ${statusColor(p.status)}`}\n >\n {p.status.replace('_', ' ')}\n </span>\n </div>\n\n <div className=\"mt-1 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\">{p.slug}</span>\n </span>\n {p.publisher && (\n <span className=\"flex items-center gap-1\">\n <User className=\"size-3\" />\n {p.publisher.name}\n {p.publisher.email && (\n <span className=\"text-[var(--color-text-muted)]\">\n ({p.publisher.email})\n </span>\n )}\n </span>\n )}\n {p.submittedAt && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n {formatDate(p.submittedAt)}\n </span>\n )}\n <span className=\"flex items-center gap-1\">\n <Tag className=\"size-3\" />\n {formatPriceForModel(p.price, p.currency, p.pricingModel)}\n </span>\n </div>\n\n {p.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-[var(--color-text-secondary)]\">\n {p.description}\n </p>\n )}\n </div>\n </div>\n\n {/* Action bar */}\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 <div className=\"flex flex-wrap items-center gap-2\">\n {/* View in devportal */}\n {p.productId && (\n <a\n href={`/devportal/apps/${p.productId}`}\n target=\"_blank\"\n rel=\"noreferrer\"\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)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n Devportal\n </a>\n )}\n\n {/* Package download for NODE/WORKFLOW — prefer zipFileUrl on the product,\n fall back to the version packageUrl already loaded in versionApps */}\n {PACKAGE_TYPES.has(p.type) &&\n (() => {\n const pkgUrl =\n p.zipFileUrl ??\n versionApps.find((va) => va.id === p.productId)?.versions?.edges?.[0]\n ?.node?.packageUrl ??\n null;\n if (!pkgUrl) return null;\n return (\n <a\n href={pkgUrl}\n download\n target=\"_blank\"\n rel=\"noreferrer\"\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)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <Download className=\"size-3.5\" />\n Download Package\n </a>\n );\n })()}\n\n {/* Test in workspace for NODE/WORKFLOW */}\n {PACKAGE_TYPES.has(p.type) && (\n <button\n type=\"button\"\n onClick={() => setTestingProduct(p)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-accent)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-accent)] hover:bg-[var(--color-surface-accent-muted)] transition-colors\"\n >\n <FlaskConical className=\"size-3.5\" />\n Test in workspace\n </button>\n )}\n\n {/* Show manifest / details toggle */}\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === p.id ? null : p.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)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <FileJson className=\"size-3.5\" />\n {viewingId === p.id ? 'Hide details' : 'Inspect details'}\n {viewingId === p.id ? (\n <ChevronUp className=\"size-3\" />\n ) : (\n <ChevronDown className=\"size-3\" />\n )}\n </button>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {p.status !== 'PUBLISHED' && (\n <button\n type=\"button\"\n onClick={() => void approve(p.id)}\n disabled={actingId === p.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)] hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n <Check className=\"size-3.5\" />\n Approve\n </button>\n )}\n {p.status !== 'REJECTED' && (\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(p.id);\n setRejectReason('');\n }}\n disabled={actingId === p.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)] hover:bg-[var(--color-surface-danger)] hover:text-[var(--color-text-on-danger)] disabled:opacity-50 transition-colors\"\n >\n <X className=\"size-3.5\" />\n Reject\n </button>\n )}\n </div>\n </div>\n\n {/* Details panel */}\n {viewingId === p.id && (\n <div className=\"border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] p-5 space-y-5\">\n {/* Metadata grid */}\n <div className=\"grid grid-cols-2 gap-x-8 gap-y-3 text-xs sm:grid-cols-3\">\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Store product ID\n </dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)] break-all\">\n {p.id}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">Slug</dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)]\">\n {p.slug}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Type / Nature\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {p.type} · {p.nature}\n </dd>\n </div>\n {p.category && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Category\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">{p.category}</dd>\n </div>\n )}\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">Pricing</dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {formatPriceForModel(p.price, p.currency, p.pricingModel)}{' '}\n <span className=\"text-[var(--color-text-muted)]\">({p.pricingModel})</span>\n </dd>\n </div>\n {p.productId && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Devportal app ID\n </dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)] break-all\">\n {p.productId}\n </dd>\n </div>\n )}\n {p.submittedBy && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Submitted by (actor)\n </dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)] break-all\">\n {p.submittedBy}\n </dd>\n </div>\n )}\n </div>\n\n {/* Required permissions */}\n {p.requiredPermissions && p.requiredPermissions.length > 0 && (\n <div>\n <h4 className=\"mb-2 flex items-center gap-1.5 text-xs font-semibold text-[var(--color-text-secondary)] uppercase tracking-wide\">\n <ShieldCheck className=\"size-3.5 text-[var(--color-status-success-text)]\" />\n Required Permissions\n </h4>\n <div className=\"flex flex-wrap gap-1.5\">\n {p.requiredPermissions.map((perm) => (\n <span\n key={perm}\n className=\"inline-flex items-center gap-1 rounded-md bg-[var(--color-surface-default)] border border-[var(--color-border-seam)] px-2 py-1 text-xs font-mono text-[var(--color-text-primary)]\"\n >\n {perm}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {/* Manifest JSON */}\n {p.manifest && (\n <div>\n <h4 className=\"mb-2 flex items-center gap-1.5 text-xs font-semibold text-[var(--color-text-secondary)] uppercase tracking-wide\">\n <FileJson className=\"size-3.5\" />\n Manifest\n </h4>\n <pre className=\"overflow-x-auto rounded-lg border border-[var(--color-border-seam)] bg-[var(--color-surface-default)] p-4 text-[11px] leading-relaxed text-[var(--color-text-primary)] max-h-72 overflow-y-auto scrollbar-thin\">\n {JSON.stringify(p.manifest, null, 2)}\n </pre>\n </div>\n )}\n </div>\n )}\n\n {/* Rejection form */}\n {rejectingId === p.id && (\n <div className=\"border-t border-[var(--color-border-seam)] bg-[var(--color-surface-danger-muted)]/30 px-5 py-4\">\n <p className=\"mb-2 text-xs font-semibold text-[var(--color-text-danger)]\">\n Reject — provide a reason (the publisher will see this)\n </p>\n <textarea\n id={`reject-reason-${p.id}`}\n aria-label=\"Rejection reason\"\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n rows={3}\n required\n aria-required=\"true\"\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 — e.g. 'Manifest is missing the identity.version field' or 'ZIP file is empty'…\"\n />\n <div className=\"mt-2 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(null);\n setRejectReason('');\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-primary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void reject(p.id)}\n disabled={!rejectReason.trim() || actingId === p.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 transition-opacity\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n </main>\n\n {/* Test in workspace modal */}\n {testingProduct && (\n <InstallAppModal\n isOpen={true}\n onClose={() => setTestingProduct(null)}\n app={{\n id: testingProduct.id,\n name: testingProduct.name,\n slug: testingProduct.slug,\n icon: testingProduct.icon ?? undefined,\n type: testingProduct.type,\n permissions: testingProduct.requiredPermissions?.length\n ? testingProduct.requiredPermissions\n : undefined,\n }}\n purchaseState=\"free\"\n onInstallSuccess={() => {\n setTestingProduct(null);\n }}\n onInstallError={() => {}}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminSubmissionsQueuePage.js","names":[],"sources":["../../src/pages/AdminSubmissionsQueuePage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport {\n ArrowLeft,\n Check,\n X,\n RefreshCw,\n PackageCheck,\n ChevronDown,\n ChevronUp,\n ExternalLink,\n FileJson,\n ShieldCheck,\n FlaskConical,\n Download,\n Clock,\n Tag,\n User,\n Hash,\n GitBranch,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { useNavigate } from 'react-router-dom';\nimport { useStore } from '../providers/StoreProvider';\nimport { formatPriceForModel } from '../utils';\nimport { PagePurpose, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { InstallAppModal } from '../components/InstallAppModal';\n\ntype PendingProduct = {\n id: string;\n slug: string;\n name: string;\n type: string;\n nature: string;\n status: string;\n pricingModel: string;\n price: number;\n currency: string;\n description?: string | null;\n submittedAt?: string | null;\n submittedBy?: string | null;\n category?: string | null;\n icon?: string | null;\n productId?: string | null;\n zipFileUrl?: string | null;\n manifest?: Record<string, unknown> | null;\n requiredPermissions?: string[] | null;\n publisher?: { id: string; name: string; email: string } | null;\n};\n\ntype ProductStatus = 'PENDING_REVIEW' | 'PUBLISHED' | 'REJECTED' | 'ALL' | 'VERSION_UPDATES';\n\ntype PendingVersion = {\n id: string;\n version: string;\n status: string;\n releaseNotes?: string | null;\n packageUrl?: string | null;\n packageSize?: number | null;\n isLatest?: boolean | null;\n reviewNotes?: string | null;\n manifest?: Record<string, unknown> | null;\n createdAt: string;\n};\n\ntype VersionUpdateApp = {\n id: string;\n name: string;\n slug: string;\n iconUrl?: string | null;\n shortDescription?: string | null;\n description?: string | null;\n versions: { edges: Array<{ node: PendingVersion }> };\n};\n\nconst PENDING_PRODUCTS_QUERY = /* GraphQL */ `\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pendingReviewProducts(limit: $limit, offset: $offset) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n zipFileUrl\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n }\n`;\n\nconst ALL_PRODUCTS_QUERY = /* GraphQL */ `\n query AdminProducts($limit: Int, $offset: Int, $status: ProductStatus) {\n products(filter: { status: $status }, limit: $limit, offset: $offset) {\n id\n slug\n name\n type\n nature\n status\n pricingModel\n price\n currency\n description\n submittedAt\n submittedBy\n category\n icon\n productId\n zipFileUrl\n manifest\n requiredPermissions\n publisher {\n id\n name\n email\n }\n }\n }\n`;\n\nconst APPROVE_PRODUCT_MUTATION = /* GraphQL */ `\n mutation ApproveProduct($id: ID!, $notes: String) {\n approveProduct(id: $id, notes: $notes) {\n id\n status\n publishedAt\n }\n }\n`;\n\nconst REJECT_PRODUCT_MUTATION = /* GraphQL */ `\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n rejectedReason\n }\n }\n`;\n\nconst APP_PACKAGE_URL_QUERY = /* GraphQL */ `\n query AdminAppPackageUrl($id: ID!) {\n application(id: $id) {\n id\n versions(first: 1) {\n edges {\n node {\n packageUrl\n }\n }\n }\n }\n }\n`;\n\nconst VERSION_UPDATES_QUERY = /* GraphQL */ `\n query AdminPendingVersionApps($limit: Int, $offset: Int) {\n applicationsWithPendingVersions(limit: $limit, offset: $offset) {\n totalCount\n edges {\n node {\n id\n name\n slug\n iconUrl\n shortDescription\n description\n versions(first: 5) {\n edges {\n node {\n id\n version\n status\n releaseNotes\n packageUrl\n packageSize\n isLatest\n reviewNotes\n manifest\n createdAt\n }\n }\n }\n }\n }\n }\n }\n`;\n\nconst APPROVE_VERSION_MUTATION = /* GraphQL */ `\n mutation AdminApproveApplicationVersion($id: ID!, $notes: String) {\n approveApplicationVersion(id: $id, notes: $notes) {\n id\n status\n }\n }\n`;\n\nconst REJECT_VERSION_MUTATION = /* GraphQL */ `\n mutation AdminRejectApplicationVersion($id: ID!, $reason: String!) {\n rejectApplicationVersion(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n\nconst STATUS_FILTERS: Array<{ label: string; value: ProductStatus }> = [\n { label: 'Pending review', value: 'PENDING_REVIEW' },\n { label: 'Published', value: 'PUBLISHED' },\n { label: 'Rejected', value: 'REJECTED' },\n { label: 'Version Updates', value: 'VERSION_UPDATES' },\n { label: 'All', value: 'ALL' },\n];\n\nconst TYPE_COLORS: Record<string, string> = {\n NODE: 'bg-[var(--color-status-info-bg-subtle)] text-[var(--color-status-info-text)]',\n WORKFLOW: 'bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]',\n INTEGRATION: 'bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)]',\n VIBEMODULE: 'bg-[var(--color-surface-accent-muted)] text-[var(--color-text-accent)]',\n};\n\nconst STATUS_COLORS: Record<string, string> = {\n PENDING_REVIEW:\n 'bg-[var(--color-status-warning-bg-subtle)] text-[var(--color-status-warning-text)]',\n PUBLISHED: 'bg-[var(--color-status-success-bg-subtle)] text-[var(--color-status-success-text)]',\n REJECTED: 'bg-[var(--color-status-error-bg-subtle)] text-[var(--color-status-error-text)]',\n};\n\nconst PACKAGE_TYPES = new Set(['NODE', 'WORKFLOW', 'VIBEMODULE', 'PARSER', 'DATASINK', 'CONSOLE']);\n\nfunction typeColor(type: string) {\n return TYPE_COLORS[type] ?? 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]';\n}\n\nfunction statusColor(status: string) {\n return (\n STATUS_COLORS[status] ?? 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]'\n );\n}\n\nfunction formatDate(iso?: string | null) {\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 AdminSubmissionsQueuePage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n const [products, setProducts] = useState<PendingProduct[]>([]);\n const [totalCount, setTotalCount] = useState(0);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [actingId, setActingId] = useState<string | null>(null);\n const [statusFilter, setStatusFilter] = useState<ProductStatus>('PENDING_REVIEW');\n const [rejectingId, setRejectingId] = useState<string | null>(null);\n const [rejectReason, setRejectReason] = useState('');\n const [viewingId, setViewingId] = useState<string | null>(null);\n const [testingProduct, setTestingProduct] = useState<PendingProduct | null>(null);\n const [versionApps, setVersionApps] = useState<VersionUpdateApp[]>([]);\n const [versionUpdateCount, setVersionUpdateCount] = useState(0);\n const [versionActingId, setVersionActingId] = useState<string | null>(null);\n const [versionRejectingId, setVersionRejectingId] = useState<string | null>(null);\n const [versionRejectReason, setVersionRejectReason] = useState('');\n const [versionRejectAppId, setVersionRejectAppId] = useState<string | null>(null);\n // productId → packageUrl for existing submissions where zipFileUrl was not populated\n const [appVersionUrls, setAppVersionUrls] = useState<Record<string, string>>({});\n\n const loadVersionApps = useCallback(async () => {\n setLoading(true);\n setError(null);\n try {\n const result = await graphqlFetch<{\n applicationsWithPendingVersions: {\n totalCount: number;\n edges: Array<{ node: VersionUpdateApp }>;\n };\n }>({\n gateway: 'global',\n query: VERSION_UPDATES_QUERY,\n variables: { limit: 50, offset: 0 },\n authToken: authToken || undefined,\n });\n if (result.errors?.length)\n throw new Error(result.errors[0]?.message ?? 'Failed to load version updates');\n const edges = result.data?.applicationsWithPendingVersions.edges ?? [];\n setVersionApps(edges.map((e) => e.node));\n setVersionUpdateCount(result.data?.applicationsWithPendingVersions.totalCount ?? 0);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load version updates');\n } finally {\n setLoading(false);\n }\n }, [authToken]);\n\n const approveVersion = async (versionId: string, appId: string, notes: string) => {\n setVersionActingId(versionId);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_VERSION_MUTATION,\n variables: { id: versionId, notes: notes || null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) throw new Error(result.errors[0]?.message ?? 'Approval failed');\n await loadVersionApps();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setVersionActingId(null);\n }\n };\n\n const rejectVersion = async (versionId: string, appId: string, reason: string) => {\n if (!reason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setVersionActingId(versionId);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_VERSION_MUTATION,\n variables: { id: versionId, reason: reason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n setVersionRejectingId(null);\n setVersionRejectReason('');\n setVersionRejectAppId(null);\n await loadVersionApps();\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setVersionActingId(null);\n }\n };\n\n const loadProducts = useCallback(\n async (filter: ProductStatus) => {\n if (filter === 'VERSION_UPDATES') {\n await loadVersionApps();\n return;\n }\n setLoading(true);\n setError(null);\n try {\n if (filter === 'PENDING_REVIEW') {\n const result = await graphqlFetch<{\n pendingReviewProducts: { totalCount: number; products: PendingProduct[] };\n }>({\n gateway: 'global',\n query: PENDING_PRODUCTS_QUERY,\n variables: { limit: 100, offset: 0 },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load pending products');\n }\n setProducts(result.data?.pendingReviewProducts.products ?? []);\n setTotalCount(result.data?.pendingReviewProducts.totalCount ?? 0);\n } else {\n const result = await graphqlFetch<{ products: PendingProduct[] }>({\n gateway: 'global',\n query: ALL_PRODUCTS_QUERY,\n variables: {\n limit: 100,\n offset: 0,\n ...(filter !== 'ALL' && { status: filter }),\n },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load products');\n }\n const list = result.data?.products ?? [];\n setProducts(list);\n setTotalCount(list.length);\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to load products');\n } finally {\n setLoading(false);\n }\n },\n [authToken, loadVersionApps]\n );\n\n useEffect(() => {\n void loadProducts(statusFilter);\n }, [loadProducts, statusFilter]);\n\n // Keep version update count fresh for the badge even when not on that tab\n useEffect(() => {\n void loadVersionApps();\n }, [loadVersionApps]);\n\n // For PACKAGE_TYPES products whose zipFileUrl was not populated at submission time,\n // fetch the latest version package URL directly from the devportal.\n useEffect(() => {\n const targets = products.filter(\n (p) => PACKAGE_TYPES.has(p.type) && p.productId && !p.zipFileUrl\n );\n if (targets.length === 0) return;\n void (async () => {\n const results = await Promise.allSettled(\n targets.map((p) =>\n graphqlFetch<{\n application: {\n id: string;\n versions: { edges: Array<{ node: { packageUrl?: string | null } }> };\n } | null;\n }>({\n gateway: 'global',\n query: APP_PACKAGE_URL_QUERY,\n variables: { id: p.productId },\n authToken: authToken || undefined,\n })\n )\n );\n setAppVersionUrls((prev) => {\n const next = { ...prev };\n results.forEach((r, i) => {\n if (r.status === 'fulfilled') {\n const url = r.value.data?.application?.versions?.edges?.[0]?.node?.packageUrl;\n const productId = targets[i].productId;\n if (url && productId) next[productId] = url;\n }\n });\n return next;\n });\n })();\n }, [products, authToken]);\n\n const approve = async (id: string) => {\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_PRODUCT_MUTATION,\n variables: { id, notes: null },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Approval failed');\n }\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Approval failed');\n } finally {\n setActingId(null);\n }\n };\n\n const reject = async (id: string) => {\n if (!rejectReason.trim()) {\n setError('Rejection reason is required');\n return;\n }\n setActingId(id);\n setError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_PRODUCT_MUTATION,\n variables: { id, reason: rejectReason.trim() },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Rejection failed');\n }\n setRejectingId(null);\n setRejectReason('');\n await loadProducts(statusFilter);\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setActingId(null);\n }\n };\n\n const pendingCount = products.filter((p) => p.status === 'PENDING_REVIEW').length;\n\n return (\n <div className=\"min-h-screen bg-[var(--color-surface-default)]\">\n {/* Header */}\n <header className=\"sticky top-0 z-10 border-b border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]\">\n <div className=\"mx-auto max-w-5xl px-6 py-4\">\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"flex items-center gap-3 min-w-0\">\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/dashboard`)}\n className=\"inline-flex items-center gap-1.5 rounded-md px-2.5 py-1.5 text-sm text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <ArrowLeft className=\"size-4 shrink-0\" />\n Back\n </button>\n <div className=\"h-5 w-px bg-[var(--color-border-seam)]\" />\n <PackageCheck className=\"size-5 shrink-0 text-[var(--color-text-primary)]\" />\n <h1 className=\"text-lg font-semibold text-[var(--color-text-primary)] truncate\">\n Submission Queue\n </h1>\n {totalCount > 0 && (\n <span className=\"shrink-0 rounded-full bg-[var(--color-status-warning-bg-subtle)] px-2 py-0.5 text-xs font-semibold text-[var(--color-status-warning-text)]\">\n {totalCount}\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => void loadProducts(statusFilter)}\n className=\"shrink-0 inline-flex items-center gap-2 rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-1.5 text-sm text-[var(--color-text-primary)] hover:bg-[var(--color-surface-hover)] transition-colors disabled:opacity-50\"\n disabled={loading}\n >\n <RefreshCw className={`size-4 ${loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n\n {/* Status filter pills */}\n <div className=\"mt-3 flex gap-1.5 flex-wrap\">\n {STATUS_FILTERS.map((f) => (\n <button\n key={f.value}\n type=\"button\"\n onClick={() => setStatusFilter(f.value)}\n className={`rounded-full px-3.5 py-1 text-xs font-medium transition-colors ${\n statusFilter === f.value\n ? 'bg-[var(--color-action-primary-bg)] text-[var(--color-action-primary-text)]'\n : 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)]'\n }`}\n >\n {f.label}\n {f.value === 'PENDING_REVIEW' &&\n pendingCount > 0 &&\n statusFilter !== 'PENDING_REVIEW' && (\n <span className=\"ml-1.5 rounded-full bg-[var(--color-status-warning-text)] px-1.5 py-0.5 text-[10px] text-white\">\n {pendingCount}\n </span>\n )}\n {f.value === 'VERSION_UPDATES' &&\n versionUpdateCount > 0 &&\n statusFilter !== 'VERSION_UPDATES' && (\n <span className=\"ml-1.5 rounded-full bg-[var(--color-status-info-text)] px-1.5 py-0.5 text-[10px] text-white\">\n {versionUpdateCount}\n </span>\n )}\n </button>\n ))}\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-5xl space-y-4 p-6\">\n <PagePurpose className=\"mb-2\">\n Every publisher submission lands here for your review. Inspect the manifest, permissions,\n and package details — then approve to publish or reject with a clear reason the developer\n will see.\n </PagePurpose>\n\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\n {statusFilter === 'VERSION_UPDATES' ? (\n loading && versionApps.length === 0 ? (\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 ) : versionApps.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title=\"No pending version updates\"\n description=\"All published apps are up to date. When a developer submits a new version, it will appear here for your review.\"\n />\n ) : (\n <ul className=\"space-y-4\">\n {versionApps.map((app) => {\n const pendingVersions = app.versions.edges\n .map((e) => e.node)\n .filter((v) => v.status === 'pending_review');\n if (pendingVersions.length === 0) return null;\n return (\n <li\n key={app.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 {/* App header */}\n <div className=\"flex items-center gap-4 p-5 border-b border-[var(--color-border-seam)]\">\n {app.iconUrl ? (\n <img\n src={app.iconUrl}\n alt=\"\"\n className=\"size-12 shrink-0 rounded-xl object-cover shadow-sm\"\n />\n ) : (\n <div className=\"flex size-12 shrink-0 items-center justify-center rounded-xl bg-[var(--color-surface-muted)] text-sm font-bold text-[var(--color-text-muted)] uppercase\">\n {app.name.slice(0, 2)}\n </div>\n )}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <h3 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {app.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 {pendingVersions.length} version\n {pendingVersions.length !== 1 ? 's' : ''} pending\n </span>\n </div>\n {(app.shortDescription ?? app.description) && (\n <p className=\"mt-0.5 text-sm text-[var(--color-text-secondary)] line-clamp-1\">\n {app.shortDescription ?? app.description}\n </p>\n )}\n </div>\n <a\n href={`/devportal/apps/${app.id}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"shrink-0 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)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n Devportal\n </a>\n </div>\n\n {/* Pending versions */}\n <div className=\"divide-y divide-[var(--color-border-seam)]\">\n {pendingVersions.map((ver) => (\n <div key={ver.id} className=\"p-5 space-y-3\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <GitBranch className=\"size-4 text-[var(--color-text-secondary)] shrink-0\" />\n <span className=\"font-mono font-semibold text-sm text-[var(--color-text-primary)]\">\n {ver.version}\n </span>\n {ver.isLatest && (\n <span className=\"rounded-full bg-[var(--color-surface-muted)] px-2 py-0.5 text-[10px] text-[var(--color-text-secondary)]\">\n Latest\n </span>\n )}\n <span className=\"rounded-full bg-[var(--color-status-warning-bg-subtle)] px-2 py-0.5 text-[10px] font-medium text-[var(--color-status-warning-text)] uppercase\">\n New Version\n </span>\n <span className=\"ml-auto flex items-center gap-1 text-xs text-[var(--color-text-secondary)]\">\n <Clock className=\"size-3\" />\n {formatDate(ver.createdAt)}\n </span>\n </div>\n\n {ver.releaseNotes && (\n <p className=\"text-sm text-[var(--color-text-secondary)]\">\n {ver.releaseNotes}\n </p>\n )}\n\n <div className=\"flex flex-wrap gap-2\">\n {ver.packageUrl && (\n <a\n href={ver.packageUrl}\n download\n target=\"_blank\"\n rel=\"noreferrer\"\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)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <Download className=\"size-3.5\" />\n Download Package\n {ver.packageSize != null && (\n <span className=\"text-[var(--color-text-muted)]\">\n ({ver.packageSize} bytes)\n </span>\n )}\n </a>\n )}\n {ver.manifest && (\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === ver.id ? null : ver.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)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <FileJson className=\"size-3.5\" />\n {viewingId === ver.id ? 'Hide manifest' : 'View manifest'}\n {viewingId === ver.id ? (\n <ChevronUp className=\"size-3\" />\n ) : (\n <ChevronDown className=\"size-3\" />\n )}\n </button>\n )}\n </div>\n\n {viewingId === ver.id && ver.manifest && (\n <pre className=\"overflow-x-auto rounded-lg border border-[var(--color-border-seam)] bg-[var(--color-surface-default)] p-4 text-[11px] leading-relaxed text-[var(--color-text-primary)] max-h-48 overflow-y-auto\">\n {JSON.stringify(ver.manifest, null, 2)}\n </pre>\n )}\n\n {/* Reject reason input */}\n {versionRejectingId === ver.id && versionRejectAppId === app.id && (\n <div className=\"rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)]/30 p-3 space-y-2\">\n <p className=\"text-xs font-semibold text-[var(--color-text-danger)]\">\n Rejection reason (the developer will see this)\n </p>\n <textarea\n value={versionRejectReason}\n onChange={(e) => setVersionRejectReason(e.target.value)}\n rows={2}\n className=\"w-full rounded-md border border-[var(--color-border-default)] bg-[var(--color-surface-default)] px-3 py-2 text-sm text-[var(--color-text-primary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)] resize-none\"\n placeholder=\"Be specific — what needs to change before this version can be approved…\"\n />\n <div className=\"flex gap-2 justify-end\">\n <button\n type=\"button\"\n onClick={() => {\n setVersionRejectingId(null);\n setVersionRejectReason('');\n setVersionRejectAppId(null);\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-primary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() =>\n void rejectVersion(ver.id, app.id, versionRejectReason)\n }\n disabled={\n !versionRejectReason.trim() || versionActingId === ver.id\n }\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 transition-opacity\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => void approveVersion(ver.id, app.id, '')}\n disabled={versionActingId === ver.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)] hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n {versionActingId === ver.id ? (\n <RefreshCw className=\"size-3.5 animate-spin\" />\n ) : (\n <Check className=\"size-3.5\" />\n )}\n Approve version\n </button>\n {versionRejectingId !== ver.id && (\n <button\n type=\"button\"\n onClick={() => {\n setVersionRejectingId(ver.id);\n setVersionRejectReason('');\n setVersionRejectAppId(app.id);\n }}\n disabled={versionActingId === ver.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)] hover:bg-[var(--color-surface-danger)] hover:text-[var(--color-text-on-danger)] disabled:opacity-50 transition-colors\"\n >\n <X className=\"size-3.5\" />\n Reject version\n </button>\n )}\n </div>\n </div>\n ))}\n </div>\n </li>\n );\n })}\n </ul>\n )\n ) : loading && products.length === 0 ? (\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 ) : products.length === 0 ? (\n <IllustratedEmptyState\n illustration={statusFilter === 'PENDING_REVIEW' ? 'empty-notifications' : 'empty-data'}\n title={\n statusFilter === 'PENDING_REVIEW' ? 'Queue is clear' : 'No products match this filter'\n }\n description={\n statusFilter === 'PENDING_REVIEW'\n ? 'No submissions are awaiting review. New submissions will appear here as publishers send them in.'\n : 'Nothing matches the current filter. Try switching to a different status above.'\n }\n />\n ) : (\n <ul className=\"space-y-3\">\n {products.map((p) => (\n <li\n key={p.id}\n className=\"overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)] transition-[box-shadow,border-color] hover:border-[var(--color-border-strong)] hover:shadow-[var(--shadow-elevation-2)]\"\n >\n {/* Card body */}\n <div className=\"flex items-start gap-4 p-5\">\n {/* App icon */}\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-14 shrink-0 rounded-xl object-cover shadow-sm\"\n />\n ) : (\n <div className=\"flex size-14 shrink-0 items-center justify-center rounded-xl bg-[var(--color-surface-muted)] text-sm font-bold text-[var(--color-text-muted)] uppercase\">\n {p.type.slice(0, 2)}\n </div>\n )}\n\n {/* Main info */}\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h3 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n {p.name}\n </h3>\n <span\n className={`rounded-full px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide ${typeColor(p.type)}`}\n >\n {p.type}\n </span>\n <span\n className={`rounded-full px-2 py-0.5 text-[11px] font-medium ${statusColor(p.status)}`}\n >\n {p.status.replace('_', ' ')}\n </span>\n </div>\n\n <div className=\"mt-1 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\">{p.slug}</span>\n </span>\n {p.publisher && (\n <span className=\"flex items-center gap-1\">\n <User className=\"size-3\" />\n {p.publisher.name}\n {p.publisher.email && (\n <span className=\"text-[var(--color-text-muted)]\">\n ({p.publisher.email})\n </span>\n )}\n </span>\n )}\n {p.submittedAt && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n {formatDate(p.submittedAt)}\n </span>\n )}\n <span className=\"flex items-center gap-1\">\n <Tag className=\"size-3\" />\n {formatPriceForModel(p.price, p.currency, p.pricingModel)}\n </span>\n </div>\n\n {p.description && (\n <p className=\"mt-2 line-clamp-2 text-sm text-[var(--color-text-secondary)]\">\n {p.description}\n </p>\n )}\n </div>\n </div>\n\n {/* Action bar */}\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 <div className=\"flex flex-wrap items-center gap-2\">\n {/* View in devportal */}\n {p.productId && (\n <a\n href={`/devportal/apps/${p.productId}`}\n target=\"_blank\"\n rel=\"noreferrer\"\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)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <ExternalLink className=\"size-3.5\" />\n Devportal\n </a>\n )}\n\n {/* Package download for NODE/WORKFLOW — prefer zipFileUrl on the product,\n then check appVersionUrls (fetched per-app for legacy submissions),\n then fall back to versionApps (apps with pending devportal versions) */}\n {PACKAGE_TYPES.has(p.type) &&\n (() => {\n const pkgUrl =\n p.zipFileUrl ??\n (p.productId ? (appVersionUrls[p.productId] ?? null) : null) ??\n versionApps.find((va) => va.id === p.productId)?.versions?.edges?.[0]\n ?.node?.packageUrl ??\n null;\n if (!pkgUrl) return null;\n return (\n <a\n href={pkgUrl}\n download\n target=\"_blank\"\n rel=\"noreferrer\"\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)] hover:bg-[var(--color-surface-hover)] hover:text-[var(--color-text-primary)] transition-colors\"\n >\n <Download className=\"size-3.5\" />\n Download Package\n </a>\n );\n })()}\n\n {/* Test in workspace for NODE/WORKFLOW */}\n {PACKAGE_TYPES.has(p.type) && (\n <button\n type=\"button\"\n onClick={() => setTestingProduct(p)}\n className=\"inline-flex items-center gap-1.5 rounded-md border border-[var(--color-border-accent)] bg-[var(--color-surface-default)] px-3 py-1.5 text-xs font-medium text-[var(--color-text-accent)] hover:bg-[var(--color-surface-accent-muted)] transition-colors\"\n >\n <FlaskConical className=\"size-3.5\" />\n Test in workspace\n </button>\n )}\n\n {/* Show manifest / details toggle */}\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === p.id ? null : p.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)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n <FileJson className=\"size-3.5\" />\n {viewingId === p.id ? 'Hide details' : 'Inspect details'}\n {viewingId === p.id ? (\n <ChevronUp className=\"size-3\" />\n ) : (\n <ChevronDown className=\"size-3\" />\n )}\n </button>\n </div>\n\n <div className=\"flex items-center gap-2\">\n {p.status !== 'PUBLISHED' && (\n <button\n type=\"button\"\n onClick={() => void approve(p.id)}\n disabled={actingId === p.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)] hover:opacity-90 disabled:opacity-50 transition-opacity\"\n >\n <Check className=\"size-3.5\" />\n Approve\n </button>\n )}\n {p.status !== 'REJECTED' && (\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(p.id);\n setRejectReason('');\n }}\n disabled={actingId === p.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)] hover:bg-[var(--color-surface-danger)] hover:text-[var(--color-text-on-danger)] disabled:opacity-50 transition-colors\"\n >\n <X className=\"size-3.5\" />\n Reject\n </button>\n )}\n </div>\n </div>\n\n {/* Details panel */}\n {viewingId === p.id && (\n <div className=\"border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] p-5 space-y-5\">\n {/* Metadata grid */}\n <div className=\"grid grid-cols-2 gap-x-8 gap-y-3 text-xs sm:grid-cols-3\">\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Store product ID\n </dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)] break-all\">\n {p.id}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">Slug</dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)]\">\n {p.slug}\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Type / Nature\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {p.type} · {p.nature}\n </dd>\n </div>\n {p.category && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Category\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">{p.category}</dd>\n </div>\n )}\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">Pricing</dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {formatPriceForModel(p.price, p.currency, p.pricingModel)}{' '}\n <span className=\"text-[var(--color-text-muted)]\">({p.pricingModel})</span>\n </dd>\n </div>\n {p.productId && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Devportal app ID\n </dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)] break-all\">\n {p.productId}\n </dd>\n </div>\n )}\n {p.submittedBy && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Submitted by (actor)\n </dt>\n <dd className=\"mt-0.5 font-mono text-[var(--color-text-primary)] break-all\">\n {p.submittedBy}\n </dd>\n </div>\n )}\n </div>\n\n {/* Required permissions */}\n {p.requiredPermissions && p.requiredPermissions.length > 0 && (\n <div>\n <h4 className=\"mb-2 flex items-center gap-1.5 text-xs font-semibold text-[var(--color-text-secondary)] uppercase tracking-wide\">\n <ShieldCheck className=\"size-3.5 text-[var(--color-status-success-text)]\" />\n Required Permissions\n </h4>\n <div className=\"flex flex-wrap gap-1.5\">\n {p.requiredPermissions.map((perm) => (\n <span\n key={perm}\n className=\"inline-flex items-center gap-1 rounded-md bg-[var(--color-surface-default)] border border-[var(--color-border-seam)] px-2 py-1 text-xs font-mono text-[var(--color-text-primary)]\"\n >\n {perm}\n </span>\n ))}\n </div>\n </div>\n )}\n\n {/* Manifest JSON */}\n {p.manifest && (\n <div>\n <h4 className=\"mb-2 flex items-center gap-1.5 text-xs font-semibold text-[var(--color-text-secondary)] uppercase tracking-wide\">\n <FileJson className=\"size-3.5\" />\n Manifest\n </h4>\n <pre className=\"overflow-x-auto rounded-lg border border-[var(--color-border-seam)] bg-[var(--color-surface-default)] p-4 text-[11px] leading-relaxed text-[var(--color-text-primary)] max-h-72 overflow-y-auto scrollbar-thin\">\n {JSON.stringify(p.manifest, null, 2)}\n </pre>\n </div>\n )}\n </div>\n )}\n\n {/* Rejection form */}\n {rejectingId === p.id && (\n <div className=\"border-t border-[var(--color-border-seam)] bg-[var(--color-surface-danger-muted)]/30 px-5 py-4\">\n <p className=\"mb-2 text-xs font-semibold text-[var(--color-text-danger)]\">\n Reject — provide a reason (the publisher will see this)\n </p>\n <textarea\n id={`reject-reason-${p.id}`}\n aria-label=\"Rejection reason\"\n value={rejectReason}\n onChange={(e) => setRejectReason(e.target.value)}\n rows={3}\n required\n aria-required=\"true\"\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 — e.g. 'Manifest is missing the identity.version field' or 'ZIP file is empty'…\"\n />\n <div className=\"mt-2 flex items-center justify-end gap-2\">\n <button\n type=\"button\"\n onClick={() => {\n setRejectingId(null);\n setRejectReason('');\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-primary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={() => void reject(p.id)}\n disabled={!rejectReason.trim() || actingId === p.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 transition-opacity\"\n >\n Submit rejection\n </button>\n </div>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n </main>\n\n {/* Test in workspace modal */}\n {testingProduct && (\n <InstallAppModal\n isOpen={true}\n onClose={() => setTestingProduct(null)}\n app={{\n id: testingProduct.id,\n name: testingProduct.name,\n slug: testingProduct.slug,\n icon: testingProduct.icon ?? undefined,\n type: testingProduct.type,\n permissions: testingProduct.requiredPermissions?.length\n ? testingProduct.requiredPermissions\n : undefined,\n }}\n purchaseState=\"free\"\n onInstallSuccess={() => {\n setTestingProduct(null);\n }}\n onInstallError={() => {}}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-store",
3
- "version": "2026.714.4",
3
+ "version": "2026.714.5",
4
4
  "description": "Store microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [