@burdenoff/microfe-store 2026.1031.1 → 2026.1102.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,9 @@
1
1
  import { useStore as e } from "../providers/StoreProvider.js";
2
2
  import { formatPriceForModel as t } from "../utils/index.js";
3
- import { useCallback as n, useEffect as r, useState as i } from "react";
4
- import { useNavigate as a } from "react-router";
5
- import { ArrowRight as o, Check as s, CheckCircle as c, ChevronDown as l, ChevronUp as u, Clock as d, Container as f, ExternalLink as p, LayoutDashboard as m, MessageSquareWarning as h, PackageCheck as g, RefreshCw as _, Store as v, TrendingUp as y, Users as b, X as x, XCircle as S } from "lucide-react";
3
+ import { GOVERNANCE_STATS_QUERY as n, runGovernanceOperation as r } from "../utils/governance.js";
4
+ import { useCallback as i, useEffect as a, useState as o } from "react";
5
+ import { useNavigate as s } from "react-router";
6
+ import { ArrowRight as c, Check as l, CheckCircle as u, ChevronDown as d, ChevronUp as f, Clock as p, Container as m, ExternalLink as h, Gavel as ee, LayoutDashboard as te, MessageSquareWarning as g, PackageCheck as _, RefreshCw as v, Store as y, TrendingUp as ne, Users as b, X as x, XCircle as S } from "lucide-react";
6
7
  import { graphqlFetch as C } from "@burdenoff/fe-libs/shared/graphql";
7
8
  import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
8
9
  import { GlassCard as D, IllustratedEmptyState as O } from "@burdenoff/fe-libs/ui";
@@ -31,7 +32,7 @@ function F(e) {
31
32
  return r < 24 ? `${r}h ago` : `${Math.floor(r / 24)}d ago`;
32
33
  }
33
34
  var I = () => {
34
- let M = a(), { authToken: I, basePath: L } = e(), [R, z] = i({
35
+ let M = s(), { authToken: I, basePath: L } = e(), [R, z] = o({
35
36
  pendingCount: 0,
36
37
  totalStores: 0,
37
38
  recentPending: [],
@@ -39,7 +40,7 @@ var I = () => {
39
40
  recentRejected: [],
40
41
  loading: !0,
41
42
  error: null
42
- }), [B, V] = i(null), [H, U] = i(null), [W, G] = i(""), [K, q] = i(null), [J, Y] = i(null), X = n(async () => {
43
+ }), [B, V] = o(null), [H, U] = o(null), [W, G] = o(""), [K, q] = o(null), [J, Y] = o(null), X = i(async () => {
43
44
  z((e) => ({
44
45
  ...e,
45
46
  loading: !0,
@@ -69,10 +70,21 @@ var I = () => {
69
70
  }));
70
71
  }
71
72
  }, [I]);
72
- r(() => {
73
+ a(() => {
73
74
  X();
74
75
  }, [X]);
75
- let Z = async (e) => {
76
+ let [Z, Q] = o(null);
77
+ a(() => {
78
+ let e = !1;
79
+ return r(n, void 0, I, "Failed to load governance counters").then((t) => {
80
+ e || Q(t.storeGovernanceStats);
81
+ }).catch(() => {
82
+ e || Q(null);
83
+ }), () => {
84
+ e = !0;
85
+ };
86
+ }, [I, R.loading]);
87
+ let $ = Z ? Z.openListingReports + Z.openReviewReports + Z.pendingAppeals : null, re = async (e) => {
76
88
  V(e), q(null);
77
89
  try {
78
90
  let t = await C({
@@ -88,7 +100,7 @@ var I = () => {
88
100
  } finally {
89
101
  V(null);
90
102
  }
91
- }, Q = async (e) => {
103
+ }, ie = async (e) => {
92
104
  if (W.trim()) {
93
105
  V(e), q(null);
94
106
  try {
@@ -109,20 +121,29 @@ var I = () => {
109
121
  V(null);
110
122
  }
111
123
  }
112
- }, $ = [
124
+ }, ae = [
113
125
  {
114
126
  label: "Pending review",
115
127
  value: R.pendingCount,
116
- icon: g,
128
+ icon: _,
117
129
  href: `${L}/submissions`,
118
130
  accent: R.pendingCount > 0 ? "border-[var(--color-status-warning-text)]/30 bg-[var(--color-status-warning-bg-subtle)]/20" : "",
119
131
  valueClass: R.pendingCount > 0 ? "text-[var(--color-status-warning-text)]" : "text-[var(--color-text-primary)]",
120
- cta: "Review queue"
132
+ cta: Z && Z.overdueSubmissions > 0 ? `Review queue · ${Z.overdueSubmissions} overdue` : "Review queue"
133
+ },
134
+ {
135
+ label: "Governance",
136
+ value: $ ?? "—",
137
+ icon: ee,
138
+ href: `${L}/governance`,
139
+ accent: $ && $ > 0 ? "border-[var(--color-status-warning-text)]/30 bg-[var(--color-status-warning-bg-subtle)]/20" : "",
140
+ valueClass: $ && $ > 0 ? "text-[var(--color-status-warning-text)]" : "text-[var(--color-text-primary)]",
141
+ cta: "Reports, takedowns & appeals"
121
142
  },
122
143
  {
123
144
  label: "Stores in registry",
124
145
  value: R.totalStores,
125
- icon: v,
146
+ icon: y,
126
147
  href: `${L}/stores`,
127
148
  accent: "",
128
149
  valueClass: "text-[var(--color-text-primary)]",
@@ -131,7 +152,7 @@ var I = () => {
131
152
  {
132
153
  label: "Review moderation",
133
154
  value: "—",
134
- icon: h,
155
+ icon: g,
135
156
  href: `${L}/reviews`,
136
157
  accent: "",
137
158
  valueClass: "text-[var(--color-text-primary)]",
@@ -149,7 +170,7 @@ var I = () => {
149
170
  {
150
171
  label: "Sandbox build checks",
151
172
  value: "—",
152
- icon: f,
173
+ icon: m,
153
174
  href: `${L}/sandbox-builds`,
154
175
  accent: "",
155
176
  valueClass: "text-[var(--color-text-primary)]",
@@ -166,7 +187,7 @@ var I = () => {
166
187
  className: "flex items-center justify-between",
167
188
  children: [/* @__PURE__ */ E("div", {
168
189
  className: "flex items-center gap-2.5",
169
- children: [/* @__PURE__ */ T(m, { className: "size-5 text-[var(--color-text-primary)]" }), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("h1", {
190
+ children: [/* @__PURE__ */ T(te, { className: "size-5 text-[var(--color-text-primary)]" }), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("h1", {
170
191
  className: "text-lg font-semibold text-[var(--color-text-primary)]",
171
192
  children: "Store Admin Dashboard"
172
193
  }), /* @__PURE__ */ T("p", {
@@ -178,7 +199,7 @@ var I = () => {
178
199
  onClick: () => void X(),
179
200
  disabled: R.loading,
180
201
  className: "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",
181
- children: [/* @__PURE__ */ T(_, { className: `size-4 ${R.loading ? "animate-spin" : ""}` }), "Refresh"]
202
+ children: [/* @__PURE__ */ T(v, { className: `size-4 ${R.loading ? "animate-spin" : ""}` }), "Refresh"]
182
203
  })]
183
204
  })
184
205
  })
@@ -192,7 +213,7 @@ var I = () => {
192
213
  }),
193
214
  /* @__PURE__ */ T("section", { children: /* @__PURE__ */ T("div", {
194
215
  className: "grid grid-cols-2 gap-4 lg:grid-cols-4",
195
- children: $.map((e) => {
216
+ children: ae.map((e) => {
196
217
  let t = e.icon;
197
218
  return /* @__PURE__ */ E("button", {
198
219
  type: "button",
@@ -212,7 +233,7 @@ var I = () => {
212
233
  })]
213
234
  }), /* @__PURE__ */ E("div", {
214
235
  className: "mt-3 flex items-center gap-1 text-xs font-medium text-[var(--color-text-accent)] group-hover:underline",
215
- children: [e.cta, /* @__PURE__ */ T(o, { className: "size-3" })]
236
+ children: [e.cta, /* @__PURE__ */ T(c, { className: "size-3" })]
216
237
  })]
217
238
  }, e.label);
218
239
  })
@@ -233,7 +254,7 @@ var I = () => {
233
254
  type: "button",
234
255
  onClick: () => M(`${L}/submissions`),
235
256
  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",
236
- children: ["View all", /* @__PURE__ */ T(p, { className: "size-3.5" })]
257
+ children: ["View all", /* @__PURE__ */ T(h, { className: "size-3.5" })]
237
258
  })]
238
259
  }),
239
260
  K && /* @__PURE__ */ T("div", {
@@ -289,7 +310,7 @@ var I = () => {
289
310
  /* @__PURE__ */ T("span", { children: t(e.price, e.currency, e.pricingModel) }),
290
311
  e.submittedAt && /* @__PURE__ */ E(w, { children: [/* @__PURE__ */ T("span", { children: "·" }), /* @__PURE__ */ E("span", {
291
312
  className: "flex items-center gap-1",
292
- children: [/* @__PURE__ */ T(d, { className: "size-3" }), F(e.submittedAt)]
313
+ children: [/* @__PURE__ */ T(p, { className: "size-3" }), F(e.submittedAt)]
293
314
  })] })
294
315
  ]
295
316
  })]
@@ -301,14 +322,14 @@ var I = () => {
301
322
  type: "button",
302
323
  onClick: () => Y(J === e.id ? null : e.id),
303
324
  className: "inline-flex items-center gap-1 rounded-md border border-[var(--color-border-default)] px-2.5 py-1.5 text-xs text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] transition-colors",
304
- children: [J === e.id ? /* @__PURE__ */ T(u, { className: "size-3.5" }) : /* @__PURE__ */ T(l, { className: "size-3.5" }), "Details"]
325
+ children: [J === e.id ? /* @__PURE__ */ T(f, { className: "size-3.5" }) : /* @__PURE__ */ T(d, { className: "size-3.5" }), "Details"]
305
326
  }),
306
327
  /* @__PURE__ */ E("button", {
307
328
  type: "button",
308
- onClick: () => void Z(e.id),
329
+ onClick: () => void re(e.id),
309
330
  disabled: B === e.id,
310
331
  className: "inline-flex items-center gap-1 rounded-md bg-[var(--color-surface-success)] px-3 py-1.5 text-xs font-semibold text-[var(--color-text-on-success)] hover:opacity-90 disabled:opacity-50 transition-opacity",
311
- children: [/* @__PURE__ */ T(s, { className: "size-3.5" }), "Approve"]
332
+ children: [/* @__PURE__ */ T(l, { className: "size-3.5" }), "Approve"]
312
333
  }),
313
334
  /* @__PURE__ */ E("button", {
314
335
  type: "button",
@@ -394,7 +415,7 @@ var I = () => {
394
415
  children: [
395
416
  e.productId.slice(0, 16),
396
417
  "…",
397
- /* @__PURE__ */ T(p, { className: "size-3" })
418
+ /* @__PURE__ */ T(h, { className: "size-3" })
398
419
  ]
399
420
  })
400
421
  })] }),
@@ -438,7 +459,7 @@ var I = () => {
438
459
  children: "Cancel"
439
460
  }), /* @__PURE__ */ T("button", {
440
461
  type: "button",
441
- onClick: () => void Q(e.id),
462
+ onClick: () => void ie(e.id),
442
463
  disabled: !W.trim() || B === e.id,
443
464
  className: "rounded-md bg-[var(--color-surface-danger)] px-3 py-1 text-xs font-semibold text-[var(--color-text-on-danger)] hover:opacity-90 disabled:opacity-50 transition-opacity",
444
465
  children: "Submit rejection"
@@ -453,7 +474,7 @@ var I = () => {
453
474
  ] }),
454
475
  /* @__PURE__ */ E("section", { children: [/* @__PURE__ */ E("div", {
455
476
  className: "mb-4 flex items-center gap-2",
456
- children: [/* @__PURE__ */ T(y, { className: "size-4 text-[var(--color-text-secondary)]" }), /* @__PURE__ */ T("h2", {
477
+ children: [/* @__PURE__ */ T(ne, { className: "size-4 text-[var(--color-text-secondary)]" }), /* @__PURE__ */ T("h2", {
457
478
  className: "text-base font-semibold text-[var(--color-text-primary)]",
458
479
  children: "Recent Activity"
459
480
  })]
@@ -466,7 +487,7 @@ var I = () => {
466
487
  children: [
467
488
  /* @__PURE__ */ T("div", {
468
489
  className: "flex size-7 items-center justify-center rounded-md bg-[var(--color-status-success-bg-subtle)]",
469
- children: /* @__PURE__ */ T(c, { className: "size-4 text-[var(--color-status-success-text)]" })
490
+ children: /* @__PURE__ */ T(u, { className: "size-4 text-[var(--color-status-success-text)]" })
470
491
  }),
471
492
  /* @__PURE__ */ T("h3", {
472
493
  className: "text-sm font-semibold text-[var(--color-text-primary)]",
@@ -1 +1 @@
1
- {"version":3,"file":"AdminDashboardPage.js","names":[],"sources":["../../src/pages/AdminDashboardPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport {\n LayoutDashboard,\n PackageCheck,\n Store as StoreIcon,\n MessageSquareWarning,\n Users,\n Check,\n X,\n ExternalLink,\n CheckCircle,\n XCircle,\n RefreshCw,\n ChevronDown,\n ChevronUp,\n ArrowRight,\n Clock,\n TrendingUp,\n Container,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { GlassCard, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useNavigate } from 'react-router';\nimport { useStore } from '../providers/StoreProvider';\nimport { formatPriceForModel } from '../utils';\n\ntype RecentProduct = {\n id: string;\n slug: string;\n name: string;\n type: string;\n nature?: string | null;\n category?: string | null;\n icon?: string | null;\n price: number;\n currency: string;\n pricingModel: string;\n description?: string | null;\n submittedAt?: string | null;\n submittedBy?: string | null;\n productId?: string | null;\n publisher?: { name: string; email: string } | null;\n};\n\ntype ActivityProduct = {\n id: string;\n slug: string;\n name: string;\n icon?: string | null;\n type?: string;\n status: string;\n publishedAt?: string | null;\n reviewedAt?: string | null;\n reviewedBy?: string | null;\n rejectedReason?: string | null;\n};\n\ntype DashboardState = {\n pendingCount: number;\n totalStores: number;\n recentPending: RecentProduct[];\n recentPublished: ActivityProduct[];\n recentRejected: ActivityProduct[];\n loading: boolean;\n error: string | null;\n};\n\nconst DASHBOARD_QUERY = /* GraphQL */ `\n query AdminDashboardData {\n recentPending: pendingReviewProducts(limit: 5) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n category\n icon\n price\n currency\n pricingModel\n description\n submittedAt\n submittedBy\n productId\n publisher {\n name\n email\n }\n }\n }\n stores {\n id\n }\n recentPublished: products(filter: { status: PUBLISHED }, limit: 5) {\n id\n slug\n name\n icon\n type\n status\n publishedAt\n reviewedBy\n }\n recentRejected: products(filter: { status: REJECTED }, limit: 5) {\n id\n slug\n name\n icon\n type\n status\n reviewedAt\n rejectedReason\n }\n }\n`;\n\nconst APPROVE_MUTATION = /* GraphQL */ `\n mutation ApproveProduct($id: ID!) {\n approveProduct(id: $id) {\n id\n status\n }\n }\n`;\n\nconst REJECT_MUTATION = /* GraphQL */ `\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n\nconst DELIST_MUTATION = /* GraphQL */ `\n mutation DelistProduct($id: ID!, $reason: String) {\n delistProduct(id: $id, reason: $reason) {\n id\n status\n }\n }\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\nfunction typeColor(type?: string | null) {\n if (!type) return 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]';\n return TYPE_COLORS[type] ?? 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]';\n}\n\nfunction formatDate(iso?: string | null) {\n if (!iso) return '—';\n return new Date(iso).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n}\n\nfunction timeAgo(iso?: string | null) {\n if (!iso) return '—';\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 60) return `${mins}m ago`;\n const hrs = Math.floor(mins / 60);\n if (hrs < 24) return `${hrs}h ago`;\n const days = Math.floor(hrs / 24);\n return `${days}d ago`;\n}\n\nexport const AdminDashboardPage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n\n const [state, setState] = useState<DashboardState>({\n pendingCount: 0,\n totalStores: 0,\n recentPending: [],\n recentPublished: [],\n recentRejected: [],\n loading: true,\n error: null,\n });\n const [actingId, setActingId] = useState<string | null>(null);\n const [rejectingId, setRejectingId] = useState<string | null>(null);\n const [rejectReason, setRejectReason] = useState('');\n const [actionError, setActionError] = useState<string | null>(null);\n const [viewingId, setViewingId] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setState((prev) => ({ ...prev, loading: true, error: null }));\n try {\n const result = await graphqlFetch<{\n recentPending: { totalCount: number; products: RecentProduct[] };\n stores: Array<{ id: string }>;\n recentPublished: ActivityProduct[];\n recentRejected: ActivityProduct[];\n }>({\n gateway: 'global',\n query: DASHBOARD_QUERY,\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load dashboard');\n }\n setState({\n pendingCount: result.data?.recentPending.totalCount ?? 0,\n totalStores: result.data?.stores.length ?? 0,\n recentPending: result.data?.recentPending.products ?? [],\n recentPublished: result.data?.recentPublished ?? [],\n recentRejected: result.data?.recentRejected ?? [],\n loading: false,\n error: null,\n });\n } catch (err) {\n setState((prev) => ({\n ...prev,\n loading: false,\n error: err instanceof Error ? err.message : 'Failed to load dashboard',\n }));\n }\n }, [authToken]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const approve = async (id: string) => {\n setActingId(id);\n setActionError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_MUTATION,\n variables: { id },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Approval failed');\n }\n await load();\n } catch (err) {\n setActionError(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()) return;\n setActingId(id);\n setActionError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_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 load();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setActingId(null);\n }\n };\n\n const delist = async (id: string) => {\n setActingId(id);\n setActionError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: DELIST_MUTATION,\n variables: { id, reason: rejectReason.trim() || undefined },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Delist failed');\n }\n setRejectingId(null);\n setRejectReason('');\n await load();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Delist failed');\n } finally {\n setActingId(null);\n }\n };\n\n const kpiTiles = [\n {\n label: 'Pending review',\n value: state.pendingCount,\n icon: PackageCheck,\n href: `${basePath}/submissions`,\n accent:\n state.pendingCount > 0\n ? 'border-[var(--color-status-warning-text)]/30 bg-[var(--color-status-warning-bg-subtle)]/20'\n : '',\n valueClass:\n state.pendingCount > 0\n ? 'text-[var(--color-status-warning-text)]'\n : 'text-[var(--color-text-primary)]',\n cta: 'Review queue',\n },\n {\n label: 'Stores in registry',\n value: state.totalStores,\n icon: StoreIcon,\n href: `${basePath}/stores`,\n accent: '',\n valueClass: 'text-[var(--color-text-primary)]',\n cta: 'Manage stores',\n },\n {\n label: 'Review moderation',\n value: '—',\n icon: MessageSquareWarning,\n href: `${basePath}/reviews`,\n accent: '',\n valueClass: 'text-[var(--color-text-primary)]',\n cta: 'Moderate reviews',\n },\n {\n label: 'Publishers',\n value: '—',\n icon: Users,\n href: `${basePath}/dashboard`,\n accent: '',\n valueClass: 'text-[var(--color-text-primary)]',\n cta: 'View publishers',\n },\n {\n label: 'Sandbox build checks',\n value: '—',\n icon: Container,\n href: `${basePath}/sandbox-builds`,\n accent: '',\n valueClass: 'text-[var(--color-text-primary)]',\n cta: 'View build log',\n },\n ];\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-6xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2.5\">\n <LayoutDashboard className=\"size-5 text-[var(--color-text-primary)]\" />\n <div>\n <h1 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n Store Admin Dashboard\n </h1>\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n Marketplace control room — submissions, registry, moderation\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={state.loading}\n className=\"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 >\n <RefreshCw className={`size-4 ${state.loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-6xl space-y-8 p-6\">\n {state.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 {state.error}\n </div>\n )}\n\n {/* KPI tiles */}\n <section>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {kpiTiles.map((tile) => {\n const Icon = tile.icon;\n return (\n <button\n key={tile.label}\n type=\"button\"\n onClick={() => navigate(tile.href)}\n className={`group relative overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] p-5 text-left shadow-[var(--shadow-elevation-1)] transition-all hover:border-[var(--color-border-strong)] hover:shadow-[var(--shadow-elevation-2)] ${tile.accent}`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div>\n <p className=\"text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide\">\n {tile.label}\n </p>\n <p className={`mt-2 text-3xl font-bold tabular-nums ${tile.valueClass}`}>\n {state.loading ? (\n <span className=\"inline-block w-8 animate-pulse rounded bg-[var(--color-surface-muted)] h-8\" />\n ) : (\n tile.value\n )}\n </p>\n </div>\n <div className=\"rounded-lg bg-[var(--color-surface-muted)] p-2\">\n <Icon className=\"size-5 text-[var(--color-text-secondary)]\" />\n </div>\n </div>\n <div className=\"mt-3 flex items-center gap-1 text-xs font-medium text-[var(--color-text-accent)] group-hover:underline\">\n {tile.cta}\n <ArrowRight className=\"size-3\" />\n </div>\n </button>\n );\n })}\n </div>\n </section>\n\n {/* Pending submissions */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n Pending Submissions\n </h2>\n {state.pendingCount > 0 && (\n <span className=\"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 {state.pendingCount} waiting\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/submissions`)}\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 View all\n <ExternalLink className=\"size-3.5\" />\n </button>\n </div>\n\n {actionError && (\n <div\n role=\"alert\"\n className=\"mb-3 rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-4 py-2.5 text-sm text-[var(--color-text-danger)]\"\n >\n {actionError}\n </div>\n )}\n\n {state.loading ? (\n <div className=\"space-y-3\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-20 animate-pulse rounded-xl bg-[var(--color-surface-muted)]\"\n />\n ))}\n </div>\n ) : state.recentPending.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"All caught up\"\n description=\"No submissions awaiting review. New products will appear here as publishers send them in.\"\n />\n ) : (\n <div className=\"overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]\">\n <ul className=\"divide-y divide-[var(--color-border-seam)]\">\n {state.recentPending.map((p) => (\n <li key={p.id} className=\"overflow-hidden\">\n <div className=\"flex items-center gap-4 px-5 py-4\">\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-10 shrink-0 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-[var(--color-surface-muted)] text-xs font-bold text-[var(--color-text-muted)] uppercase\">\n {p.type.slice(0, 2)}\n </div>\n )}\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <p className=\"font-medium text-[var(--color-text-primary)] truncate\">\n {p.name}\n </p>\n <span\n className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide ${typeColor(p.type)}`}\n >\n {p.type}\n </span>\n </div>\n <div className=\"mt-0.5 flex items-center gap-3 flex-wrap text-xs text-[var(--color-text-secondary)]\">\n <span>{p.publisher?.name ?? 'Unknown publisher'}</span>\n <span>·</span>\n <span>{formatPriceForModel(p.price, p.currency, p.pricingModel)}</span>\n {p.submittedAt && (\n <>\n <span>·</span>\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n {timeAgo(p.submittedAt)}\n </span>\n </>\n )}\n </div>\n </div>\n\n <div className=\"shrink-0 flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === p.id ? null : p.id)}\n className=\"inline-flex items-center gap-1 rounded-md border border-[var(--color-border-default)] px-2.5 py-1.5 text-xs text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n {viewingId === p.id ? (\n <ChevronUp className=\"size-3.5\" />\n ) : (\n <ChevronDown className=\"size-3.5\" />\n )}\n Details\n </button>\n <button\n type=\"button\"\n onClick={() => void approve(p.id)}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md bg-[var(--color-surface-success)] px-3 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 <button\n type=\"button\"\n onClick={() => {\n setRejectingId(p.id);\n setRejectReason('');\n setActionError(null);\n }}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-3 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 </div>\n </div>\n\n {viewingId === p.id && (\n <div className=\"border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] px-5 py-4\">\n <dl 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)]\">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)]\">Type</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)]\">\n {p.category}\n </dd>\n </div>\n )}\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Pricing\n </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)]\">\n ({p.pricingModel})\n </span>\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Publisher\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {p.publisher?.name ?? '—'}\n {p.publisher?.email && (\n <span className=\"block text-[var(--color-text-muted)]\">\n {p.publisher.email}\n </span>\n )}\n </dd>\n </div>\n {p.productId && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Devportal app\n </dt>\n <dd className=\"mt-0.5\">\n <a\n href={`/devportal/apps/${p.productId}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 font-mono text-[var(--color-text-accent)] hover:underline\"\n >\n {p.productId.slice(0, 16)}…\n <ExternalLink className=\"size-3\" />\n </a>\n </dd>\n </div>\n )}\n {p.description && (\n <div className=\"col-span-2 sm:col-span-3\">\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Description\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {p.description}\n </dd>\n </div>\n )}\n </dl>\n </div>\n )}\n\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 Provide a rejection 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={2}\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=\"Explain why this submission is being rejected…\"\n />\n <div className=\"mt-2 flex 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 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 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 </div>\n )}\n </section>\n\n {/* Recent Activity */}\n <section>\n <div className=\"mb-4 flex items-center gap-2\">\n <TrendingUp className=\"size-4 text-[var(--color-text-secondary)]\" />\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n Recent Activity\n </h2>\n </div>\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n {/* Recently Approved */}\n <GlassCard className=\"overflow-hidden p-0\">\n <div className=\"flex items-center gap-2.5 border-b border-[var(--color-border-seam)] px-4 py-3\">\n <div className=\"flex size-7 items-center justify-center rounded-md bg-[var(--color-status-success-bg-subtle)]\">\n <CheckCircle className=\"size-4 text-[var(--color-status-success-text)]\" />\n </div>\n <h3 className=\"text-sm font-semibold text-[var(--color-text-primary)]\">\n Recently Approved\n </h3>\n <span className=\"ml-auto text-xs text-[var(--color-text-muted)]\">Last 5</span>\n </div>\n {state.loading ? (\n <div className=\"space-y-3 p-4\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-10 animate-pulse rounded-lg bg-[var(--color-surface-muted)]\"\n />\n ))}\n </div>\n ) : state.recentPublished.length === 0 ? (\n <p className=\"px-4 py-8 text-center text-sm text-[var(--color-text-muted)]\">\n No approved products yet.\n </p>\n ) : (\n <ul className=\"divide-y divide-[var(--color-border-seam)]\">\n {state.recentPublished.map((p) => (\n <li key={p.id} className=\"flex items-center gap-3 px-4 py-3\">\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-8 shrink-0 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-surface-muted)] text-xs font-bold text-[var(--color-text-muted)] uppercase\">\n {(p.type ?? '?').slice(0, 2)}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-[var(--color-text-primary)]\">\n {p.name}\n </p>\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n {p.reviewedBy ? `by ${p.reviewedBy} · ` : ''}\n {formatDate(p.publishedAt)}\n </p>\n </div>\n <span className=\"shrink-0 rounded-full bg-[var(--color-status-success-bg-subtle)] px-2.5 py-1 text-xs font-medium text-[var(--color-status-success-text)]\">\n Published\n </span>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n\n {/* Recently Rejected */}\n <GlassCard className=\"overflow-hidden p-0\">\n <div className=\"flex items-center gap-2.5 border-b border-[var(--color-border-seam)] px-4 py-3\">\n <div className=\"flex size-7 items-center justify-center rounded-md bg-[var(--color-status-error-bg-subtle)]\">\n <XCircle className=\"size-4 text-[var(--color-status-error-text)]\" />\n </div>\n <h3 className=\"text-sm font-semibold text-[var(--color-text-primary)]\">\n Recently Rejected\n </h3>\n <span className=\"ml-auto text-xs text-[var(--color-text-muted)]\">Last 5</span>\n </div>\n {state.loading ? (\n <div className=\"space-y-3 p-4\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-10 animate-pulse rounded-lg bg-[var(--color-surface-muted)]\"\n />\n ))}\n </div>\n ) : state.recentRejected.length === 0 ? (\n <p className=\"px-4 py-8 text-center text-sm text-[var(--color-text-muted)]\">\n No rejected products yet.\n </p>\n ) : (\n <ul className=\"divide-y divide-[var(--color-border-seam)]\">\n {state.recentRejected.map((p) => (\n <li key={p.id} className=\"flex items-center gap-3 px-4 py-3\">\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-8 shrink-0 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-surface-muted)] text-xs font-bold text-[var(--color-text-muted)] uppercase\">\n {(p.type ?? '?').slice(0, 2)}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-[var(--color-text-primary)]\">\n {p.name}\n </p>\n {p.rejectedReason && (\n <p\n className=\"truncate text-xs text-[var(--color-text-muted)]\"\n title={p.rejectedReason}\n >\n \"{p.rejectedReason}\"\n </p>\n )}\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n {formatDate(p.reviewedAt)}\n </p>\n </div>\n <span className=\"shrink-0 rounded-full bg-[var(--color-status-error-bg-subtle)] px-2.5 py-1 text-xs font-medium text-[var(--color-status-error-text)]\">\n Rejected\n </span>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n </div>\n </section>\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AdminDashboardPage.js","names":[],"sources":["../../src/pages/AdminDashboardPage.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useCallback, useEffect, useState } from 'react';\nimport {\n LayoutDashboard,\n PackageCheck,\n Store as StoreIcon,\n MessageSquareWarning,\n Users,\n Check,\n X,\n ExternalLink,\n CheckCircle,\n XCircle,\n RefreshCw,\n ChevronDown,\n ChevronUp,\n ArrowRight,\n Clock,\n TrendingUp,\n Container,\n Gavel,\n} from 'lucide-react';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport { GlassCard, IllustratedEmptyState } from '@burdenoff/fe-libs/ui';\nimport { useNavigate } from 'react-router';\nimport { useStore } from '../providers/StoreProvider';\nimport { formatPriceForModel } from '../utils';\nimport {\n GOVERNANCE_STATS_QUERY,\n runGovernanceOperation,\n type GovernanceStats,\n} from '../utils/governance';\n\ntype RecentProduct = {\n id: string;\n slug: string;\n name: string;\n type: string;\n nature?: string | null;\n category?: string | null;\n icon?: string | null;\n price: number;\n currency: string;\n pricingModel: string;\n description?: string | null;\n submittedAt?: string | null;\n submittedBy?: string | null;\n productId?: string | null;\n publisher?: { name: string; email: string } | null;\n};\n\ntype ActivityProduct = {\n id: string;\n slug: string;\n name: string;\n icon?: string | null;\n type?: string;\n status: string;\n publishedAt?: string | null;\n reviewedAt?: string | null;\n reviewedBy?: string | null;\n rejectedReason?: string | null;\n};\n\ntype DashboardState = {\n pendingCount: number;\n totalStores: number;\n recentPending: RecentProduct[];\n recentPublished: ActivityProduct[];\n recentRejected: ActivityProduct[];\n loading: boolean;\n error: string | null;\n};\n\nconst DASHBOARD_QUERY = /* GraphQL */ `\n query AdminDashboardData {\n recentPending: pendingReviewProducts(limit: 5) {\n totalCount\n products {\n id\n slug\n name\n type\n nature\n category\n icon\n price\n currency\n pricingModel\n description\n submittedAt\n submittedBy\n productId\n publisher {\n name\n email\n }\n }\n }\n stores {\n id\n }\n recentPublished: products(filter: { status: PUBLISHED }, limit: 5) {\n id\n slug\n name\n icon\n type\n status\n publishedAt\n reviewedBy\n }\n recentRejected: products(filter: { status: REJECTED }, limit: 5) {\n id\n slug\n name\n icon\n type\n status\n reviewedAt\n rejectedReason\n }\n }\n`;\n\nconst APPROVE_MUTATION = /* GraphQL */ `\n mutation ApproveProduct($id: ID!) {\n approveProduct(id: $id) {\n id\n status\n }\n }\n`;\n\nconst REJECT_MUTATION = /* GraphQL */ `\n mutation RejectProduct($id: ID!, $reason: String!) {\n rejectProduct(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n\nconst DELIST_MUTATION = /* GraphQL */ `\n mutation DelistProduct($id: ID!, $reason: String) {\n delistProduct(id: $id, reason: $reason) {\n id\n status\n }\n }\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\nfunction typeColor(type?: string | null) {\n if (!type) return 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]';\n return TYPE_COLORS[type] ?? 'bg-[var(--color-surface-muted)] text-[var(--color-text-secondary)]';\n}\n\nfunction formatDate(iso?: string | null) {\n if (!iso) return '—';\n return new Date(iso).toLocaleDateString(undefined, {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n });\n}\n\nfunction timeAgo(iso?: string | null) {\n if (!iso) return '—';\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60000);\n if (mins < 60) return `${mins}m ago`;\n const hrs = Math.floor(mins / 60);\n if (hrs < 24) return `${hrs}h ago`;\n const days = Math.floor(hrs / 24);\n return `${days}d ago`;\n}\n\nexport const AdminDashboardPage: FC = () => {\n const navigate = useNavigate();\n const { authToken, basePath } = useStore();\n\n const [state, setState] = useState<DashboardState>({\n pendingCount: 0,\n totalStores: 0,\n recentPending: [],\n recentPublished: [],\n recentRejected: [],\n loading: true,\n error: null,\n });\n const [actingId, setActingId] = useState<string | null>(null);\n const [rejectingId, setRejectingId] = useState<string | null>(null);\n const [rejectReason, setRejectReason] = useState('');\n const [actionError, setActionError] = useState<string | null>(null);\n const [viewingId, setViewingId] = useState<string | null>(null);\n\n const load = useCallback(async () => {\n setState((prev) => ({ ...prev, loading: true, error: null }));\n try {\n const result = await graphqlFetch<{\n recentPending: { totalCount: number; products: RecentProduct[] };\n stores: Array<{ id: string }>;\n recentPublished: ActivityProduct[];\n recentRejected: ActivityProduct[];\n }>({\n gateway: 'global',\n query: DASHBOARD_QUERY,\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Failed to load dashboard');\n }\n setState({\n pendingCount: result.data?.recentPending.totalCount ?? 0,\n totalStores: result.data?.stores.length ?? 0,\n recentPending: result.data?.recentPending.products ?? [],\n recentPublished: result.data?.recentPublished ?? [],\n recentRejected: result.data?.recentRejected ?? [],\n loading: false,\n error: null,\n });\n } catch (err) {\n setState((prev) => ({\n ...prev,\n loading: false,\n error: err instanceof Error ? err.message : 'Failed to load dashboard',\n }));\n }\n }, [authToken]);\n\n useEffect(() => {\n void load();\n }, [load]);\n\n // Review SLA and governance counters (BOFF-10635). Loaded separately so a\n // governance outage never blanks the rest of the dashboard.\n const [governance, setGovernance] = useState<GovernanceStats | null>(null);\n useEffect(() => {\n let cancelled = false;\n runGovernanceOperation<{ storeGovernanceStats: GovernanceStats }>(\n GOVERNANCE_STATS_QUERY,\n undefined,\n authToken,\n 'Failed to load governance counters'\n )\n .then((data) => {\n if (!cancelled) setGovernance(data.storeGovernanceStats);\n })\n .catch(() => {\n if (!cancelled) setGovernance(null);\n });\n return () => {\n cancelled = true;\n };\n }, [authToken, state.loading]);\n const governanceOpen = governance\n ? governance.openListingReports + governance.openReviewReports + governance.pendingAppeals\n : null;\n\n const approve = async (id: string) => {\n setActingId(id);\n setActionError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: APPROVE_MUTATION,\n variables: { id },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Approval failed');\n }\n await load();\n } catch (err) {\n setActionError(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()) return;\n setActingId(id);\n setActionError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: REJECT_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 load();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Rejection failed');\n } finally {\n setActingId(null);\n }\n };\n\n const delist = async (id: string) => {\n setActingId(id);\n setActionError(null);\n try {\n const result = await graphqlFetch({\n gateway: 'global',\n query: DELIST_MUTATION,\n variables: { id, reason: rejectReason.trim() || undefined },\n authToken: authToken || undefined,\n });\n if (result.errors?.length) {\n throw new Error(result.errors[0]?.message ?? 'Delist failed');\n }\n setRejectingId(null);\n setRejectReason('');\n await load();\n } catch (err) {\n setActionError(err instanceof Error ? err.message : 'Delist failed');\n } finally {\n setActingId(null);\n }\n };\n\n const kpiTiles = [\n {\n label: 'Pending review',\n value: state.pendingCount,\n icon: PackageCheck,\n href: `${basePath}/submissions`,\n accent:\n state.pendingCount > 0\n ? 'border-[var(--color-status-warning-text)]/30 bg-[var(--color-status-warning-bg-subtle)]/20'\n : '',\n valueClass:\n state.pendingCount > 0\n ? 'text-[var(--color-status-warning-text)]'\n : 'text-[var(--color-text-primary)]',\n cta:\n governance && governance.overdueSubmissions > 0\n ? `Review queue · ${governance.overdueSubmissions} overdue`\n : 'Review queue',\n },\n {\n label: 'Governance',\n value: governanceOpen ?? '—',\n icon: Gavel,\n href: `${basePath}/governance`,\n accent:\n governanceOpen && governanceOpen > 0\n ? 'border-[var(--color-status-warning-text)]/30 bg-[var(--color-status-warning-bg-subtle)]/20'\n : '',\n valueClass:\n governanceOpen && governanceOpen > 0\n ? 'text-[var(--color-status-warning-text)]'\n : 'text-[var(--color-text-primary)]',\n cta: 'Reports, takedowns & appeals',\n },\n {\n label: 'Stores in registry',\n value: state.totalStores,\n icon: StoreIcon,\n href: `${basePath}/stores`,\n accent: '',\n valueClass: 'text-[var(--color-text-primary)]',\n cta: 'Manage stores',\n },\n {\n label: 'Review moderation',\n value: '—',\n icon: MessageSquareWarning,\n href: `${basePath}/reviews`,\n accent: '',\n valueClass: 'text-[var(--color-text-primary)]',\n cta: 'Moderate reviews',\n },\n {\n label: 'Publishers',\n value: '—',\n icon: Users,\n href: `${basePath}/dashboard`,\n accent: '',\n valueClass: 'text-[var(--color-text-primary)]',\n cta: 'View publishers',\n },\n {\n label: 'Sandbox build checks',\n value: '—',\n icon: Container,\n href: `${basePath}/sandbox-builds`,\n accent: '',\n valueClass: 'text-[var(--color-text-primary)]',\n cta: 'View build log',\n },\n ];\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-6xl px-6 py-4\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2.5\">\n <LayoutDashboard className=\"size-5 text-[var(--color-text-primary)]\" />\n <div>\n <h1 className=\"text-lg font-semibold text-[var(--color-text-primary)]\">\n Store Admin Dashboard\n </h1>\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n Marketplace control room — submissions, registry, moderation\n </p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => void load()}\n disabled={state.loading}\n className=\"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 >\n <RefreshCw className={`size-4 ${state.loading ? 'animate-spin' : ''}`} />\n Refresh\n </button>\n </div>\n </div>\n </header>\n\n <main className=\"mx-auto max-w-6xl space-y-8 p-6\">\n {state.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 {state.error}\n </div>\n )}\n\n {/* KPI tiles */}\n <section>\n <div className=\"grid grid-cols-2 gap-4 lg:grid-cols-4\">\n {kpiTiles.map((tile) => {\n const Icon = tile.icon;\n return (\n <button\n key={tile.label}\n type=\"button\"\n onClick={() => navigate(tile.href)}\n className={`group relative overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] p-5 text-left shadow-[var(--shadow-elevation-1)] transition-all hover:border-[var(--color-border-strong)] hover:shadow-[var(--shadow-elevation-2)] ${tile.accent}`}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <div>\n <p className=\"text-xs font-medium text-[var(--color-text-secondary)] uppercase tracking-wide\">\n {tile.label}\n </p>\n <p className={`mt-2 text-3xl font-bold tabular-nums ${tile.valueClass}`}>\n {state.loading ? (\n <span className=\"inline-block w-8 animate-pulse rounded bg-[var(--color-surface-muted)] h-8\" />\n ) : (\n tile.value\n )}\n </p>\n </div>\n <div className=\"rounded-lg bg-[var(--color-surface-muted)] p-2\">\n <Icon className=\"size-5 text-[var(--color-text-secondary)]\" />\n </div>\n </div>\n <div className=\"mt-3 flex items-center gap-1 text-xs font-medium text-[var(--color-text-accent)] group-hover:underline\">\n {tile.cta}\n <ArrowRight className=\"size-3\" />\n </div>\n </button>\n );\n })}\n </div>\n </section>\n\n {/* Pending submissions */}\n <section>\n <div className=\"mb-4 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n Pending Submissions\n </h2>\n {state.pendingCount > 0 && (\n <span className=\"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 {state.pendingCount} waiting\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={() => navigate(`${basePath}/submissions`)}\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 View all\n <ExternalLink className=\"size-3.5\" />\n </button>\n </div>\n\n {actionError && (\n <div\n role=\"alert\"\n className=\"mb-3 rounded-lg border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-4 py-2.5 text-sm text-[var(--color-text-danger)]\"\n >\n {actionError}\n </div>\n )}\n\n {state.loading ? (\n <div className=\"space-y-3\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-20 animate-pulse rounded-xl bg-[var(--color-surface-muted)]\"\n />\n ))}\n </div>\n ) : state.recentPending.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title=\"All caught up\"\n description=\"No submissions awaiting review. New products will appear here as publishers send them in.\"\n />\n ) : (\n <div className=\"overflow-hidden rounded-xl border border-[var(--color-border-seam)] bg-[var(--color-surface-raised)] shadow-[var(--shadow-elevation-1)]\">\n <ul className=\"divide-y divide-[var(--color-border-seam)]\">\n {state.recentPending.map((p) => (\n <li key={p.id} className=\"overflow-hidden\">\n <div className=\"flex items-center gap-4 px-5 py-4\">\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-10 shrink-0 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-10 shrink-0 items-center justify-center rounded-lg bg-[var(--color-surface-muted)] text-xs font-bold text-[var(--color-text-muted)] uppercase\">\n {p.type.slice(0, 2)}\n </div>\n )}\n\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2 flex-wrap\">\n <p className=\"font-medium text-[var(--color-text-primary)] truncate\">\n {p.name}\n </p>\n <span\n className={`shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide ${typeColor(p.type)}`}\n >\n {p.type}\n </span>\n </div>\n <div className=\"mt-0.5 flex items-center gap-3 flex-wrap text-xs text-[var(--color-text-secondary)]\">\n <span>{p.publisher?.name ?? 'Unknown publisher'}</span>\n <span>·</span>\n <span>{formatPriceForModel(p.price, p.currency, p.pricingModel)}</span>\n {p.submittedAt && (\n <>\n <span>·</span>\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" />\n {timeAgo(p.submittedAt)}\n </span>\n </>\n )}\n </div>\n </div>\n\n <div className=\"shrink-0 flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setViewingId(viewingId === p.id ? null : p.id)}\n className=\"inline-flex items-center gap-1 rounded-md border border-[var(--color-border-default)] px-2.5 py-1.5 text-xs text-[var(--color-text-secondary)] hover:bg-[var(--color-surface-hover)] transition-colors\"\n >\n {viewingId === p.id ? (\n <ChevronUp className=\"size-3.5\" />\n ) : (\n <ChevronDown className=\"size-3.5\" />\n )}\n Details\n </button>\n <button\n type=\"button\"\n onClick={() => void approve(p.id)}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md bg-[var(--color-surface-success)] px-3 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 <button\n type=\"button\"\n onClick={() => {\n setRejectingId(p.id);\n setRejectReason('');\n setActionError(null);\n }}\n disabled={actingId === p.id}\n className=\"inline-flex items-center gap-1 rounded-md border border-[var(--color-border-danger)] bg-[var(--color-surface-danger-muted)] px-3 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 </div>\n </div>\n\n {viewingId === p.id && (\n <div className=\"border-t border-[var(--color-border-seam)] bg-[var(--color-surface-muted)] px-5 py-4\">\n <dl 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)]\">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)]\">Type</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)]\">\n {p.category}\n </dd>\n </div>\n )}\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Pricing\n </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)]\">\n ({p.pricingModel})\n </span>\n </dd>\n </div>\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Publisher\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {p.publisher?.name ?? '—'}\n {p.publisher?.email && (\n <span className=\"block text-[var(--color-text-muted)]\">\n {p.publisher.email}\n </span>\n )}\n </dd>\n </div>\n {p.productId && (\n <div>\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Devportal app\n </dt>\n <dd className=\"mt-0.5\">\n <a\n href={`/devportal/apps/${p.productId}`}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"inline-flex items-center gap-1 font-mono text-[var(--color-text-accent)] hover:underline\"\n >\n {p.productId.slice(0, 16)}…\n <ExternalLink className=\"size-3\" />\n </a>\n </dd>\n </div>\n )}\n {p.description && (\n <div className=\"col-span-2 sm:col-span-3\">\n <dt className=\"font-medium text-[var(--color-text-secondary)]\">\n Description\n </dt>\n <dd className=\"mt-0.5 text-[var(--color-text-primary)]\">\n {p.description}\n </dd>\n </div>\n )}\n </dl>\n </div>\n )}\n\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 Provide a rejection 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={2}\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=\"Explain why this submission is being rejected…\"\n />\n <div className=\"mt-2 flex 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 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 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 </div>\n )}\n </section>\n\n {/* Recent Activity */}\n <section>\n <div className=\"mb-4 flex items-center gap-2\">\n <TrendingUp className=\"size-4 text-[var(--color-text-secondary)]\" />\n <h2 className=\"text-base font-semibold text-[var(--color-text-primary)]\">\n Recent Activity\n </h2>\n </div>\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n {/* Recently Approved */}\n <GlassCard className=\"overflow-hidden p-0\">\n <div className=\"flex items-center gap-2.5 border-b border-[var(--color-border-seam)] px-4 py-3\">\n <div className=\"flex size-7 items-center justify-center rounded-md bg-[var(--color-status-success-bg-subtle)]\">\n <CheckCircle className=\"size-4 text-[var(--color-status-success-text)]\" />\n </div>\n <h3 className=\"text-sm font-semibold text-[var(--color-text-primary)]\">\n Recently Approved\n </h3>\n <span className=\"ml-auto text-xs text-[var(--color-text-muted)]\">Last 5</span>\n </div>\n {state.loading ? (\n <div className=\"space-y-3 p-4\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-10 animate-pulse rounded-lg bg-[var(--color-surface-muted)]\"\n />\n ))}\n </div>\n ) : state.recentPublished.length === 0 ? (\n <p className=\"px-4 py-8 text-center text-sm text-[var(--color-text-muted)]\">\n No approved products yet.\n </p>\n ) : (\n <ul className=\"divide-y divide-[var(--color-border-seam)]\">\n {state.recentPublished.map((p) => (\n <li key={p.id} className=\"flex items-center gap-3 px-4 py-3\">\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-8 shrink-0 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-surface-muted)] text-xs font-bold text-[var(--color-text-muted)] uppercase\">\n {(p.type ?? '?').slice(0, 2)}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-[var(--color-text-primary)]\">\n {p.name}\n </p>\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n {p.reviewedBy ? `by ${p.reviewedBy} · ` : ''}\n {formatDate(p.publishedAt)}\n </p>\n </div>\n <span className=\"shrink-0 rounded-full bg-[var(--color-status-success-bg-subtle)] px-2.5 py-1 text-xs font-medium text-[var(--color-status-success-text)]\">\n Published\n </span>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n\n {/* Recently Rejected */}\n <GlassCard className=\"overflow-hidden p-0\">\n <div className=\"flex items-center gap-2.5 border-b border-[var(--color-border-seam)] px-4 py-3\">\n <div className=\"flex size-7 items-center justify-center rounded-md bg-[var(--color-status-error-bg-subtle)]\">\n <XCircle className=\"size-4 text-[var(--color-status-error-text)]\" />\n </div>\n <h3 className=\"text-sm font-semibold text-[var(--color-text-primary)]\">\n Recently Rejected\n </h3>\n <span className=\"ml-auto text-xs text-[var(--color-text-muted)]\">Last 5</span>\n </div>\n {state.loading ? (\n <div className=\"space-y-3 p-4\">\n {[1, 2, 3].map((i) => (\n <div\n key={i}\n className=\"h-10 animate-pulse rounded-lg bg-[var(--color-surface-muted)]\"\n />\n ))}\n </div>\n ) : state.recentRejected.length === 0 ? (\n <p className=\"px-4 py-8 text-center text-sm text-[var(--color-text-muted)]\">\n No rejected products yet.\n </p>\n ) : (\n <ul className=\"divide-y divide-[var(--color-border-seam)]\">\n {state.recentRejected.map((p) => (\n <li key={p.id} className=\"flex items-center gap-3 px-4 py-3\">\n {p.icon ? (\n <img\n src={p.icon}\n alt=\"\"\n className=\"size-8 shrink-0 rounded-lg object-cover\"\n />\n ) : (\n <div className=\"flex size-8 shrink-0 items-center justify-center rounded-lg bg-[var(--color-surface-muted)] text-xs font-bold text-[var(--color-text-muted)] uppercase\">\n {(p.type ?? '?').slice(0, 2)}\n </div>\n )}\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-[var(--color-text-primary)]\">\n {p.name}\n </p>\n {p.rejectedReason && (\n <p\n className=\"truncate text-xs text-[var(--color-text-muted)]\"\n title={p.rejectedReason}\n >\n \"{p.rejectedReason}\"\n </p>\n )}\n <p className=\"text-xs text-[var(--color-text-secondary)]\">\n {formatDate(p.reviewedAt)}\n </p>\n </div>\n <span className=\"shrink-0 rounded-full bg-[var(--color-status-error-bg-subtle)] px-2.5 py-1 text-xs font-medium text-[var(--color-status-error-text)]\">\n Rejected\n </span>\n </li>\n ))}\n </ul>\n )}\n </GlassCard>\n </div>\n </section>\n </main>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const AdminGovernancePage: FC;