@burdenoff/microfe-store 2026.714.4 → 2026.714.6

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 id\n packageUrl\n }\n }\n }\n }\n }\n", me = "\n mutation RefreshApplicationVersionPackageUrl($id: ID!) {\n refreshApplicationVersionPackageUrl(id: $id)\n }\n", he = "\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", ge = "\n mutation AdminApproveApplicationVersion($id: ID!, $notes: String) {\n approveApplicationVersion(id: $id, notes: $notes) {\n id\n status\n }\n }\n", _e = "\n mutation AdminRejectApplicationVersion($id: ID!, $reason: String!) {\n rejectApplicationVersion(id: $id, reason: $reason) {\n id\n status\n }\n }\n", ve = [
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",
@@ -46,13 +46,13 @@ var ue = "\n query PendingReviewProducts($limit: Int, $offset: Int) {\n pend
46
46
  "DATASINK",
47
47
  "CONSOLE"
48
48
  ]);
49
- function ve(e) {
50
- return y[e] ?? "bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]";
51
- }
52
49
  function ye(e) {
53
50
  return b[e] ?? "bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]";
54
51
  }
55
- function S(e) {
52
+ function be(e) {
53
+ return x[e] ?? "bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]";
54
+ }
55
+ function C(e) {
56
56
  return e ? new Date(e).toLocaleString(void 0, {
57
57
  month: "short",
58
58
  day: "numeric",
@@ -61,156 +61,193 @@ 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, xe] = a("PENDING_REVIEW"), [Se, I] = a(null), [L, R] = a(""), [z, B] = a(null), [V, H] = a(null), [U, Ce] = a([]), [we, Te] = a(0), [W, G] = a(null), [Ee, K] = a(null), [q, J] = a(""), [De, Y] = a(null), [Oe, ke] = a({}), [X, Z] = a(null), Q = r(async () => {
66
+ A(!0), M(null);
67
67
  try {
68
68
  let e = await g({
69
69
  gateway: "global",
70
- query: me,
70
+ query: he,
71
71
  variables: {
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
- Se((e.data?.applicationsWithPendingVersions.edges ?? []).map((e) => e.node)), Ce(e.data?.applicationsWithPendingVersions.totalCount ?? 0);
78
+ Ce((e.data?.applicationsWithPendingVersions.edges ?? []).map((e) => e.node)), Te(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]), Ae = async (e, t, n) => {
85
+ G(e), M(null);
86
86
  try {
87
87
  let t = await g({
88
88
  gateway: "global",
89
- query: he,
89
+ query: ge,
90
90
  variables: {
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
101
  G(null);
102
102
  }
103
- }, Ee = async (e, t, n) => {
103
+ }, je = 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
+ G(e), M(null);
109
109
  try {
110
110
  let t = await g({
111
111
  gateway: "global",
112
- query: ge,
112
+ query: _e,
113
113
  variables: {
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
+ K(null), J(""), Y(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
124
  G(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
+ ke((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, a = e[n].productId;
184
+ i?.id && i.packageUrl && a && (r[a] = {
185
+ versionId: i.id,
186
+ packageUrl: i.packageUrl
187
+ });
188
+ }
189
+ }), r;
190
+ });
191
+ })();
192
+ }, [T, x]);
193
+ let Me = async (e) => {
194
+ Z(e);
195
+ try {
196
+ let t = (await g({
197
+ gateway: "global",
198
+ query: me,
199
+ variables: { id: e },
200
+ authToken: x || void 0
201
+ })).data?.refreshApplicationVersionPackageUrl;
202
+ t ? window.open(t, "_blank", "noreferrer") : M("Could not generate a download URL. S3 credentials may not be configured.");
203
+ } catch (e) {
204
+ M(e instanceof Error ? e.message : "Failed to refresh download URL");
205
+ } finally {
206
+ Z(null);
207
+ }
208
+ }, Ne = async (e) => {
209
+ P(e), M(null);
173
210
  try {
174
211
  let t = await g({
175
212
  gateway: "global",
176
- query: fe,
213
+ query: de,
177
214
  variables: {
178
215
  id: e,
179
216
  notes: null
180
217
  },
181
- authToken: b || void 0
218
+ authToken: x || void 0
182
219
  });
183
220
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Approval failed");
184
- await Q(P);
221
+ await $(F);
185
222
  } catch (e) {
186
- j(e instanceof Error ? e.message : "Approval failed");
223
+ M(e instanceof Error ? e.message : "Approval failed");
187
224
  } finally {
188
- N(null);
225
+ P(null);
189
226
  }
190
- }, Oe = async (e) => {
191
- if (!I.trim()) {
192
- j("Rejection reason is required");
227
+ }, Pe = async (e) => {
228
+ if (!L.trim()) {
229
+ M("Rejection reason is required");
193
230
  return;
194
231
  }
195
- N(e), j(null);
232
+ P(e), M(null);
196
233
  try {
197
234
  let t = await g({
198
235
  gateway: "global",
199
- query: pe,
236
+ query: fe,
200
237
  variables: {
201
238
  id: e,
202
- reason: I.trim()
239
+ reason: L.trim()
203
240
  },
204
- authToken: b || void 0
241
+ authToken: x || void 0
205
242
  });
206
243
  if (t.errors?.length) throw Error(t.errors[0]?.message ?? "Rejection failed");
207
- F(null), L(""), await Q(P);
244
+ I(null), R(""), await $(F);
208
245
  } catch (e) {
209
- j(e instanceof Error ? e.message : "Rejection failed");
246
+ M(e instanceof Error ? e.message : "Rejection failed");
210
247
  } finally {
211
- N(null);
248
+ P(null);
212
249
  }
213
- }, $ = w.filter((e) => e.status === "PENDING_REVIEW").length;
250
+ }, Fe = T.filter((e) => e.status === "PENDING_REVIEW").length;
214
251
  return /* @__PURE__ */ v("div", {
215
252
  className: "min-h-screen bg-[var(--color-surface-default)]",
216
253
  children: [
@@ -225,7 +262,7 @@ var C = () => {
225
262
  children: [
226
263
  /* @__PURE__ */ v("button", {
227
264
  type: "button",
228
- onClick: () => y(`${C}/dashboard`),
265
+ onClick: () => b(`${w}/dashboard`),
229
266
  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
267
  children: [/* @__PURE__ */ _(ee, { className: "size-4 shrink-0" }), "Back"]
231
268
  }),
@@ -235,33 +272,33 @@ var C = () => {
235
272
  className: "text-lg font-semibold text-[var(--color-text-primary)] truncate",
236
273
  children: "Submission Queue"
237
274
  }),
238
- E > 0 && /* @__PURE__ */ _("span", {
275
+ D > 0 && /* @__PURE__ */ _("span", {
239
276
  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
277
+ children: D
241
278
  })
242
279
  ]
243
280
  }), /* @__PURE__ */ v("button", {
244
281
  type: "button",
245
- onClick: () => void Q(P),
282
+ onClick: () => void $(F),
246
283
  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"]
284
+ disabled: k,
285
+ children: [/* @__PURE__ */ _(m, { className: `size-4 ${k ? "animate-spin" : ""}` }), "Refresh"]
249
286
  })]
250
287
  }), /* @__PURE__ */ _("div", {
251
288
  className: "mt-3 flex gap-1.5 flex-wrap",
252
- children: _e.map((e) => /* @__PURE__ */ v("button", {
289
+ children: ve.map((e) => /* @__PURE__ */ v("button", {
253
290
  type: "button",
254
- 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)]"}`,
291
+ onClick: () => xe(e.value),
292
+ 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
293
  children: [
257
294
  e.label,
258
- e.value === "PENDING_REVIEW" && $ > 0 && P !== "PENDING_REVIEW" && /* @__PURE__ */ _("span", {
295
+ e.value === "PENDING_REVIEW" && Fe > 0 && F !== "PENDING_REVIEW" && /* @__PURE__ */ _("span", {
259
296
  className: "ml-1.5 rounded-full bg-[var(--color-status-warning-text)] px-1.5 py-0.5 text-[10px] text-white",
260
- children: $
297
+ children: Fe
261
298
  }),
262
- e.value === "VERSION_UPDATES" && U > 0 && P !== "VERSION_UPDATES" && /* @__PURE__ */ _("span", {
299
+ e.value === "VERSION_UPDATES" && we > 0 && F !== "VERSION_UPDATES" && /* @__PURE__ */ _("span", {
263
300
  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
301
+ children: we
265
302
  })
266
303
  ]
267
304
  }, e.value))
@@ -271,25 +308,25 @@ var C = () => {
271
308
  /* @__PURE__ */ v("main", {
272
309
  className: "mx-auto max-w-5xl space-y-4 p-6",
273
310
  children: [
274
- /* @__PURE__ */ _(le, {
311
+ /* @__PURE__ */ _(ce, {
275
312
  className: "mb-2",
276
313
  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
314
  }),
278
- A && /* @__PURE__ */ _("div", {
315
+ j && /* @__PURE__ */ _("div", {
279
316
  role: "alert",
280
317
  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
318
+ children: j
282
319
  }),
283
- P === "VERSION_UPDATES" ? O && H.length === 0 ? /* @__PURE__ */ _("div", {
320
+ F === "VERSION_UPDATES" ? k && U.length === 0 ? /* @__PURE__ */ _("div", {
284
321
  className: "flex items-center justify-center py-24",
285
322
  children: /* @__PURE__ */ _(m, { className: "size-6 animate-spin text-[var(--color-text-muted)]" })
286
- }) : H.length === 0 ? /* @__PURE__ */ _(ce, {
323
+ }) : U.length === 0 ? /* @__PURE__ */ _(y, {
287
324
  illustration: "empty-data",
288
325
  title: "No pending version updates",
289
326
  description: "All published apps are up to date. When a developer submits a new version, it will appear here for your review."
290
327
  }) : /* @__PURE__ */ _("ul", {
291
328
  className: "space-y-4",
292
- children: H.map((e) => {
329
+ children: U.map((e) => {
293
330
  let t = e.versions.edges.map((e) => e.node).filter((e) => e.status === "pending_review");
294
331
  return t.length === 0 ? null : /* @__PURE__ */ v("li", {
295
332
  className: "overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]",
@@ -357,7 +394,7 @@ var C = () => {
357
394
  }),
358
395
  /* @__PURE__ */ v("span", {
359
396
  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)]
397
+ children: [/* @__PURE__ */ _(l, { className: "size-3" }), C(t.createdAt)]
361
398
  })
362
399
  ]
363
400
  }),
@@ -367,14 +404,13 @@ var C = () => {
367
404
  }),
368
405
  /* @__PURE__ */ v("div", {
369
406
  className: "flex flex-wrap gap-2",
370
- children: [t.packageUrl && /* @__PURE__ */ v("a", {
371
- href: t.packageUrl,
372
- download: !0,
373
- target: "_blank",
374
- rel: "noreferrer",
375
- 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",
407
+ children: [t.packageUrl && /* @__PURE__ */ v("button", {
408
+ type: "button",
409
+ onClick: () => void Me(t.id),
410
+ disabled: X === t.id,
411
+ 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 disabled:opacity-50",
376
412
  children: [
377
- /* @__PURE__ */ _(u, { className: "size-3.5" }),
413
+ X === t.id ? /* @__PURE__ */ _(m, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ _(u, { className: "size-3.5" }),
378
414
  "Download Package",
379
415
  t.packageSize != null && /* @__PURE__ */ v("span", {
380
416
  className: "text-[var(--color-text-muted)]",
@@ -387,20 +423,20 @@ var C = () => {
387
423
  ]
388
424
  }), t.manifest && /* @__PURE__ */ v("button", {
389
425
  type: "button",
390
- onClick: () => z(R === t.id ? null : t.id),
426
+ onClick: () => B(z === t.id ? null : t.id),
391
427
  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
428
  children: [
393
429
  /* @__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" })
430
+ z === t.id ? "Hide manifest" : "View manifest",
431
+ z === t.id ? /* @__PURE__ */ _(c, { className: "size-3" }) : /* @__PURE__ */ _(s, { className: "size-3" })
396
432
  ]
397
433
  })]
398
434
  }),
399
- R === t.id && t.manifest && /* @__PURE__ */ _("pre", {
435
+ z === t.id && t.manifest && /* @__PURE__ */ _("pre", {
400
436
  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
437
  children: JSON.stringify(t.manifest, null, 2)
402
438
  }),
403
- K === t.id && we === e.id && /* @__PURE__ */ v("div", {
439
+ Ee === t.id && De === e.id && /* @__PURE__ */ v("div", {
404
440
  className: "rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)]/30 p-3 space-y-2",
405
441
  children: [
406
442
  /* @__PURE__ */ _("p", {
@@ -408,8 +444,8 @@ var C = () => {
408
444
  children: "Rejection reason (the developer will see this)"
409
445
  }),
410
446
  /* @__PURE__ */ _("textarea", {
411
- value: J,
412
- onChange: (e) => Y(e.target.value),
447
+ value: q,
448
+ onChange: (e) => J(e.target.value),
413
449
  rows: 2,
414
450
  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
451
  placeholder: "Be specific — what needs to change before this version can be approved…"
@@ -419,14 +455,14 @@ var C = () => {
419
455
  children: [/* @__PURE__ */ _("button", {
420
456
  type: "button",
421
457
  onClick: () => {
422
- q(null), Y(""), X(null);
458
+ K(null), J(""), Y(null);
423
459
  },
424
460
  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
461
  children: "Cancel"
426
462
  }), /* @__PURE__ */ _("button", {
427
463
  type: "button",
428
- onClick: () => void Ee(t.id, e.id, J),
429
- disabled: !J.trim() || W === t.id,
464
+ onClick: () => void je(t.id, e.id, q),
465
+ disabled: !q.trim() || W === t.id,
430
466
  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
467
  children: "Submit rejection"
432
468
  })]
@@ -437,14 +473,14 @@ var C = () => {
437
473
  className: "flex items-center gap-2",
438
474
  children: [/* @__PURE__ */ v("button", {
439
475
  type: "button",
440
- onClick: () => void Te(t.id, e.id, ""),
476
+ onClick: () => void Ae(t.id, e.id, ""),
441
477
  disabled: W === t.id,
442
478
  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
479
  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", {
480
+ }), Ee !== t.id && /* @__PURE__ */ v("button", {
445
481
  type: "button",
446
482
  onClick: () => {
447
- q(t.id), Y(""), X(e.id);
483
+ K(t.id), J(""), Y(e.id);
448
484
  },
449
485
  disabled: W === t.id,
450
486
  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",
@@ -456,16 +492,16 @@ var C = () => {
456
492
  })]
457
493
  }, e.id);
458
494
  })
459
- }) : O && w.length === 0 ? /* @__PURE__ */ _("div", {
495
+ }) : k && T.length === 0 ? /* @__PURE__ */ _("div", {
460
496
  className: "flex items-center justify-center py-24",
461
497
  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."
498
+ }) : T.length === 0 ? /* @__PURE__ */ _(y, {
499
+ illustration: F === "PENDING_REVIEW" ? "empty-notifications" : "empty-data",
500
+ title: F === "PENDING_REVIEW" ? "Queue is clear" : "No products match this filter",
501
+ 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
502
  }) : /* @__PURE__ */ _("ul", {
467
503
  className: "space-y-3",
468
- children: w.map((e) => /* @__PURE__ */ v("li", {
504
+ children: T.map((e) => /* @__PURE__ */ v("li", {
469
505
  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
506
  children: [
471
507
  /* @__PURE__ */ v("div", {
@@ -488,11 +524,11 @@ var C = () => {
488
524
  children: e.name
489
525
  }),
490
526
  /* @__PURE__ */ _("span", {
491
- className: `rounded-full px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide ${ve(e.type)}`,
527
+ className: `rounded-full px-2 py-0.5 text-[11px] font-semibold uppercase tracking-wide ${ye(e.type)}`,
492
528
  children: e.type
493
529
  }),
494
530
  /* @__PURE__ */ _("span", {
495
- className: `rounded-full px-2 py-0.5 text-[11px] font-medium ${ye(e.status)}`,
531
+ className: `rounded-full px-2 py-0.5 text-[11px] font-medium ${be(e.status)}`,
496
532
  children: e.status.replace("_", " ")
497
533
  })
498
534
  ]
@@ -524,7 +560,7 @@ var C = () => {
524
560
  }),
525
561
  e.submittedAt && /* @__PURE__ */ v("span", {
526
562
  className: "flex items-center gap-1",
527
- children: [/* @__PURE__ */ _(l, { className: "size-3" }), S(e.submittedAt)]
563
+ children: [/* @__PURE__ */ _(l, { className: "size-3" }), C(e.submittedAt)]
528
564
  }),
529
565
  /* @__PURE__ */ v("span", {
530
566
  className: "flex items-center gap-1",
@@ -551,31 +587,39 @@ var C = () => {
551
587
  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
588
  children: [/* @__PURE__ */ _(d, { className: "size-3.5" }), "Devportal"]
553
589
  }),
554
- x.has(e.type) && (() => {
555
- let t = e.zipFileUrl ?? H.find((t) => t.id === e.productId)?.versions?.edges?.[0]?.node?.packageUrl ?? null;
556
- return t ? /* @__PURE__ */ v("a", {
557
- href: t,
590
+ S.has(e.type) && (() => {
591
+ let t = e.productId ? Oe[e.productId]?.versionId ?? U.find((t) => t.id === e.productId)?.versions?.edges?.[0]?.node?.id : void 0;
592
+ if (!t && !e.zipFileUrl) return null;
593
+ let n = X === t;
594
+ return t ? /* @__PURE__ */ v("button", {
595
+ type: "button",
596
+ onClick: () => void Me(t),
597
+ disabled: n,
598
+ 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 disabled:opacity-50",
599
+ children: [n ? /* @__PURE__ */ _(m, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ _(u, { className: "size-3.5" }), "Download Package"]
600
+ }) : /* @__PURE__ */ v("a", {
601
+ href: e.zipFileUrl,
558
602
  download: !0,
559
603
  target: "_blank",
560
604
  rel: "noreferrer",
561
605
  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",
562
606
  children: [/* @__PURE__ */ _(u, { className: "size-3.5" }), "Download Package"]
563
- }) : null;
607
+ });
564
608
  })(),
565
- x.has(e.type) && /* @__PURE__ */ v("button", {
609
+ S.has(e.type) && /* @__PURE__ */ v("button", {
566
610
  type: "button",
567
- onClick: () => V(e),
611
+ onClick: () => H(e),
568
612
  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
613
  children: [/* @__PURE__ */ _(te, { className: "size-3.5" }), "Test in workspace"]
570
614
  }),
571
615
  /* @__PURE__ */ v("button", {
572
616
  type: "button",
573
- onClick: () => z(R === e.id ? null : e.id),
617
+ onClick: () => B(z === e.id ? null : e.id),
574
618
  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
619
  children: [
576
620
  /* @__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" })
621
+ z === e.id ? "Hide details" : "Inspect details",
622
+ z === e.id ? /* @__PURE__ */ _(c, { className: "size-3" }) : /* @__PURE__ */ _(s, { className: "size-3" })
579
623
  ]
580
624
  })
581
625
  ]
@@ -583,22 +627,22 @@ var C = () => {
583
627
  className: "flex items-center gap-2",
584
628
  children: [e.status !== "PUBLISHED" && /* @__PURE__ */ v("button", {
585
629
  type: "button",
586
- onClick: () => void De(e.id),
587
- disabled: M === e.id,
630
+ onClick: () => void Ne(e.id),
631
+ disabled: N === e.id,
588
632
  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
633
  children: [/* @__PURE__ */ _(o, { className: "size-3.5" }), "Approve"]
590
634
  }), e.status !== "REJECTED" && /* @__PURE__ */ v("button", {
591
635
  type: "button",
592
636
  onClick: () => {
593
- F(e.id), L("");
637
+ I(e.id), R("");
594
638
  },
595
- disabled: M === e.id,
639
+ disabled: N === e.id,
596
640
  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
641
  children: [/* @__PURE__ */ _(h, { className: "size-3.5" }), "Reject"]
598
642
  })]
599
643
  })]
600
644
  }),
601
- R === e.id && /* @__PURE__ */ v("div", {
645
+ z === e.id && /* @__PURE__ */ v("div", {
602
646
  className: "border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] p-5 space-y-5",
603
647
  children: [
604
648
  /* @__PURE__ */ v("div", {
@@ -689,7 +733,7 @@ var C = () => {
689
733
  })] })
690
734
  ]
691
735
  }),
692
- xe === e.id && /* @__PURE__ */ v("div", {
736
+ Se === e.id && /* @__PURE__ */ v("div", {
693
737
  className: "border-t border-[var(--color-border-seam)] bg-[var(--color-surface-danger-muted)]/30 px-5 py-4",
694
738
  children: [
695
739
  /* @__PURE__ */ _("p", {
@@ -699,8 +743,8 @@ var C = () => {
699
743
  /* @__PURE__ */ _("textarea", {
700
744
  id: `reject-reason-${e.id}`,
701
745
  "aria-label": "Rejection reason",
702
- value: I,
703
- onChange: (e) => L(e.target.value),
746
+ value: L,
747
+ onChange: (e) => R(e.target.value),
704
748
  rows: 3,
705
749
  required: !0,
706
750
  "aria-required": "true",
@@ -712,14 +756,14 @@ var C = () => {
712
756
  children: [/* @__PURE__ */ _("button", {
713
757
  type: "button",
714
758
  onClick: () => {
715
- F(null), L("");
759
+ I(null), R("");
716
760
  },
717
761
  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
762
  children: "Cancel"
719
763
  }), /* @__PURE__ */ _("button", {
720
764
  type: "button",
721
- onClick: () => void Oe(e.id),
722
- disabled: !I.trim() || M === e.id,
765
+ onClick: () => void Pe(e.id),
766
+ disabled: !L.trim() || N === e.id,
723
767
  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
768
  children: "Submit rejection"
725
769
  })]
@@ -731,20 +775,20 @@ var C = () => {
731
775
  })
732
776
  ]
733
777
  }),
734
- B && /* @__PURE__ */ _(n, {
778
+ V && /* @__PURE__ */ _(n, {
735
779
  isOpen: !0,
736
- onClose: () => V(null),
780
+ onClose: () => H(null),
737
781
  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
782
+ id: V.id,
783
+ name: V.name,
784
+ slug: V.slug,
785
+ icon: V.icon ?? void 0,
786
+ type: V.type,
787
+ permissions: V.requiredPermissions?.length ? V.requiredPermissions : void 0
744
788
  },
745
789
  purchaseState: "free",
746
790
  onInstallSuccess: () => {
747
- V(null);
791
+ H(null);
748
792
  },
749
793
  onInstallError: () => {}
750
794
  })
@@ -752,6 +796,6 @@ var C = () => {
752
796
  });
753
797
  };
754
798
  //#endregion
755
- export { C as AdminSubmissionsQueuePage };
799
+ export { w as AdminSubmissionsQueuePage };
756
800
 
757
801
  //# 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 id\n packageUrl\n }\n }\n }\n }\n }\n`;\n\nconst REFRESH_PACKAGE_URL_MUTATION = /* GraphQL */ `\n mutation RefreshApplicationVersionPackageUrl($id: ID!) {\n refreshApplicationVersionPackageUrl(id: $id)\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 → { versionId, packageUrl } for legacy submissions where zipFileUrl was not populated\n const [appVersionInfo, setAppVersionInfo] = useState<\n Record<string, { versionId: string; packageUrl: string }>\n >({});\n const [downloadingVersionId, setDownloadingVersionId] = 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 // For PACKAGE_TYPES products whose zipFileUrl was not populated at submission time,\n // fetch the latest version ID + 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: { id: string; 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 setAppVersionInfo((prev) => {\n const next = { ...prev };\n results.forEach((r, i) => {\n if (r.status === 'fulfilled') {\n const node = r.value.data?.application?.versions?.edges?.[0]?.node;\n const productId = targets[i].productId;\n if (node?.id && node.packageUrl && productId) {\n next[productId] = { versionId: node.id, packageUrl: node.packageUrl };\n }\n }\n });\n return next;\n });\n })();\n }, [products, authToken]);\n\n const downloadPackage = async (versionId: string) => {\n setDownloadingVersionId(versionId);\n try {\n const result = await graphqlFetch<{ refreshApplicationVersionPackageUrl: string | null }>({\n gateway: 'global',\n query: REFRESH_PACKAGE_URL_MUTATION,\n variables: { id: versionId },\n authToken: authToken || undefined,\n });\n const freshUrl = result.data?.refreshApplicationVersionPackageUrl;\n if (freshUrl) {\n window.open(freshUrl, '_blank', 'noreferrer');\n } else {\n setError('Could not generate a download URL. S3 credentials may not be configured.');\n }\n } catch (err) {\n setError(err instanceof Error ? err.message : 'Failed to refresh download URL');\n } finally {\n setDownloadingVersionId(null);\n }\n };\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 <button\n type=\"button\"\n onClick={() => void downloadPackage(ver.id)}\n disabled={downloadingVersionId === 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 disabled:opacity-50\"\n >\n {downloadingVersionId === ver.id ? (\n <RefreshCw className=\"size-3.5 animate-spin\" />\n ) : (\n <Download className=\"size-3.5\" />\n )}\n Download Package\n {ver.packageSize != null && (\n <span className=\"text-[var(--color-text-muted)]\">\n ({ver.packageSize} bytes)\n </span>\n )}\n </button>\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. Always calls the refresh mutation\n to get a fresh presigned URL (stored URLs expire in 1h). */}\n {PACKAGE_TYPES.has(p.type) &&\n (() => {\n // Resolve the version ID to use with the refresh mutation\n const versionId = p.productId\n ? (appVersionInfo[p.productId]?.versionId ??\n versionApps.find((va) => va.id === p.productId)?.versions?.edges?.[0]\n ?.node?.id)\n : undefined;\n // If we have no version ID yet, check if we at least have zipFileUrl\n // (new submissions populated by the backend fix)\n if (!versionId && !p.zipFileUrl) return null;\n const isLoading = downloadingVersionId === versionId;\n if (versionId) {\n return (\n <button\n type=\"button\"\n onClick={() => void downloadPackage(versionId)}\n disabled={isLoading}\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 disabled:opacity-50\"\n >\n {isLoading ? (\n <RefreshCw className=\"size-3.5 animate-spin\" />\n ) : (\n <Download className=\"size-3.5\" />\n )}\n Download Package\n </button>\n );\n }\n // Fallback: direct link when we have zipFileUrl but no versionId yet\n return (\n <a\n href={p.zipFileUrl!}\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.6",
4
4
  "description": "Store microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [