@burdenoff/microfe-admin 2026.625.1 → 2026.626.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.
Files changed (70) hide show
  1. package/dist/admin/components/AccessDenied.js +2 -2
  2. package/dist/admin/components/AccessDenied.js.map +1 -1
  3. package/dist/admin/components/ActivityFeed.js +4 -4
  4. package/dist/admin/components/ActivityFeed.js.map +1 -1
  5. package/dist/admin/components/Charts.js.map +1 -1
  6. package/dist/admin/components/ConfirmDialog.js +1 -1
  7. package/dist/admin/components/ConfirmDialog.js.map +1 -1
  8. package/dist/admin/components/FormSheet.js +1 -1
  9. package/dist/admin/components/FormSheet.js.map +1 -1
  10. package/dist/admin/components/TenantSsoPanel.js +1 -1
  11. package/dist/admin/components/TenantSsoPanel.js.map +1 -1
  12. package/dist/admin/components/Toast.js +11 -11
  13. package/dist/admin/components/Toast.js.map +1 -1
  14. package/dist/admin/components/forms/SubscriptionForm.js +2 -2
  15. package/dist/admin/components/forms/SubscriptionForm.js.map +1 -1
  16. package/dist/admin/components/forms/TenantForm.js +1 -1
  17. package/dist/admin/components/forms/UserForm.js +1 -1
  18. package/dist/admin/components/ui.js +3 -3
  19. package/dist/admin/components/ui.js.map +1 -1
  20. package/dist/admin/constants/index.js +33 -24
  21. package/dist/admin/constants/index.js.map +1 -1
  22. package/dist/admin/pages/AuditLogsPage.js +92 -77
  23. package/dist/admin/pages/AuditLogsPage.js.map +1 -1
  24. package/dist/admin/pages/BillingPage.js +95 -51
  25. package/dist/admin/pages/BillingPage.js.map +1 -1
  26. package/dist/admin/pages/CommunicationsPage.js +97 -92
  27. package/dist/admin/pages/CommunicationsPage.js.map +1 -1
  28. package/dist/admin/pages/ContentPagesPage.js +274 -259
  29. package/dist/admin/pages/ContentPagesPage.js.map +1 -1
  30. package/dist/admin/pages/DashboardPage.js +113 -84
  31. package/dist/admin/pages/DashboardPage.js.map +1 -1
  32. package/dist/admin/pages/FeatureFlagsPage.js +93 -75
  33. package/dist/admin/pages/FeatureFlagsPage.js.map +1 -1
  34. package/dist/admin/pages/OrganizationsListPage.js +105 -87
  35. package/dist/admin/pages/OrganizationsListPage.js.map +1 -1
  36. package/dist/admin/pages/ProductsPage.js +138 -130
  37. package/dist/admin/pages/ProductsPage.js.map +1 -1
  38. package/dist/admin/pages/ReferralsPage.js +70 -52
  39. package/dist/admin/pages/ReferralsPage.js.map +1 -1
  40. package/dist/admin/pages/RolesPermissionsPage.js +136 -118
  41. package/dist/admin/pages/RolesPermissionsPage.js.map +1 -1
  42. package/dist/admin/pages/SREPage.js +34 -32
  43. package/dist/admin/pages/SREPage.js.map +1 -1
  44. package/dist/admin/pages/SandboxSettingsPage.js +360 -322
  45. package/dist/admin/pages/SandboxSettingsPage.js.map +1 -1
  46. package/dist/admin/pages/SettingsPage.js +63 -60
  47. package/dist/admin/pages/SettingsPage.js.map +1 -1
  48. package/dist/admin/pages/SyncFailuresPage.js +120 -118
  49. package/dist/admin/pages/SyncFailuresPage.js.map +1 -1
  50. package/dist/admin/pages/TenantDetailPage.js +261 -218
  51. package/dist/admin/pages/TenantDetailPage.js.map +1 -1
  52. package/dist/admin/pages/TenantFormRoutePage.js +65 -61
  53. package/dist/admin/pages/TenantFormRoutePage.js.map +1 -1
  54. package/dist/admin/pages/TenantsListPage.js +86 -71
  55. package/dist/admin/pages/TenantsListPage.js.map +1 -1
  56. package/dist/admin/pages/UsersListPage.js +128 -113
  57. package/dist/admin/pages/UsersListPage.js.map +1 -1
  58. package/dist/admin/pages/WaitlistPage.js +78 -63
  59. package/dist/admin/pages/WaitlistPage.js.map +1 -1
  60. package/dist/admin/pages/WorkflowsPage.js +107 -102
  61. package/dist/admin/pages/WorkflowsPage.js.map +1 -1
  62. package/dist/admin/pages/WorkspacesListPage.js +61 -49
  63. package/dist/admin/pages/WorkspacesListPage.js.map +1 -1
  64. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js +2 -2
  65. package/dist/node_modules/@hookform/resolvers/dist/resolvers.js.map +1 -1
  66. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js +35 -26
  67. package/dist/node_modules/@hookform/resolvers/zod/dist/zod.js.map +1 -1
  68. package/dist/node_modules/react-hook-form/dist/index.esm.js +609 -516
  69. package/dist/node_modules/react-hook-form/dist/index.esm.js.map +1 -1
  70. package/package.json +2 -2
@@ -8,14 +8,15 @@ import "../components/index.js";
8
8
  import { useCallback as l, useEffect as u, useMemo as d, useState as f } from "react";
9
9
  import { Check as p, ClipboardList as m, Search as h, Trash2 as g, X as _ } from "lucide-react";
10
10
  import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
11
- import { gql as x } from "@apollo/client";
12
- import { useGatewayClient as S } from "@burdenoff/fe-libs/shared/graphql";
11
+ import { EmphasisPanel as x, PagePurpose as ee } from "@burdenoff/fe-libs/ui";
12
+ import { gql as S } from "@apollo/client";
13
+ import { useGatewayClient as C } from "@burdenoff/fe-libs/shared/graphql";
13
14
  //#region src/admin/pages/WaitlistPage.tsx
14
- var C = 20, w = {
15
+ var w = 20, T = {
15
16
  PENDING: "outline",
16
17
  APPROVED: "default",
17
18
  REJECTED: "destructive"
18
- }, T = x`
19
+ }, E = S`
19
20
  query GetWaitlist($filter: WaitlistFilterInput) {
20
21
  waitlist(filter: $filter) {
21
22
  id
@@ -31,70 +32,70 @@ var C = 20, w = {
31
32
  createdAt
32
33
  }
33
34
  }
34
- `, E = x`
35
+ `, D = S`
35
36
  mutation ApproveWaitlist($input: ApproveWaitlistInput!) {
36
37
  approveWaitlist(input: $input) {
37
38
  id
38
39
  status
39
40
  }
40
41
  }
41
- `, D = x`
42
+ `, O = S`
42
43
  mutation RejectWaitlist($input: RejectWaitlistInput!) {
43
44
  rejectWaitlist(input: $input) {
44
45
  id
45
46
  status
46
47
  }
47
48
  }
48
- `, O = x`
49
+ `, k = S`
49
50
  mutation RemoveFromWaitlist($id: ID!) {
50
51
  removeFromWaitlist(id: $id)
51
52
  }
52
53
  `;
53
- function k() {
54
- let { canViewWaitlist: x, canManageWaitlist: k, isLoading: A } = e(), j = S("global"), [M, N] = f(1), [P, F] = f(""), [I, L] = f(""), [R, z] = f(null), [B, V] = f(!1), [H, U] = f(null), [W, G] = f(0), K = d(() => {
54
+ function A() {
55
+ let { canViewWaitlist: S, canManageWaitlist: A, isLoading: j } = e(), M = C("global"), [N, P] = f(1), [F, I] = f(""), [L, R] = f(""), [z, B] = f(null), [V, H] = f(!1), [U, W] = f(null), [G, K] = f(0), q = d(() => {
55
56
  let e = {};
56
- return I && (e.status = I), Object.keys(e).length > 0 ? e : void 0;
57
- }, [I]), q = l(() => G((e) => e + 1), []);
57
+ return L && (e.status = L), Object.keys(e).length > 0 ? e : void 0;
58
+ }, [L]), J = l(() => K((e) => e + 1), []);
58
59
  u(() => {
59
- A || !x || (V(!0), j.query({
60
- query: T,
61
- variables: { filter: K },
60
+ j || !S || (H(!0), M.query({
61
+ query: E,
62
+ variables: { filter: q },
62
63
  fetchPolicy: "network-only"
63
64
  }).then((e) => {
64
65
  let t = e.data;
65
- z(t ?? null), U(null);
66
- }).catch((e) => U({ message: e.message })).finally(() => V(!1)));
66
+ B(t ?? null), W(null);
67
+ }).catch((e) => W({ message: e.message })).finally(() => H(!1)));
67
68
  }, [
69
+ M,
70
+ q,
68
71
  j,
69
- K,
70
- A,
71
- x,
72
- W
72
+ S,
73
+ G
73
74
  ]);
74
- let J = d(() => R?.waitlist ?? [], [R]), Y = d(() => {
75
- if (!P) return J;
76
- let e = P.toLowerCase();
77
- return J.filter((t) => t.email.toLowerCase().includes(e) || t.name && t.name.toLowerCase().includes(e) || t.company && t.company.toLowerCase().includes(e));
78
- }, [J, P]), X = Y.length, Z = Math.ceil(X / C), Q = Y.slice((M - 1) * C, M * C), $ = l(async (e) => {
79
- await j.mutate({
80
- mutation: E,
75
+ let Y = d(() => z?.waitlist ?? [], [z]), X = d(() => {
76
+ if (!F) return Y;
77
+ let e = F.toLowerCase();
78
+ return Y.filter((t) => t.email.toLowerCase().includes(e) || t.name && t.name.toLowerCase().includes(e) || t.company && t.company.toLowerCase().includes(e));
79
+ }, [Y, F]), Z = X.length, Q = Math.ceil(Z / w), $ = X.slice((N - 1) * w, N * w), te = d(() => Y.filter((e) => e.status === "PENDING").length, [Y]), ne = l(async (e) => {
80
+ await M.mutate({
81
+ mutation: D,
81
82
  variables: { input: { id: e } }
82
- }), q();
83
- }, [j, q]), ee = l(async (e) => {
83
+ }), J();
84
+ }, [M, J]), re = l(async (e) => {
84
85
  let t = window.prompt("Rejection reason (optional):");
85
- await j.mutate({
86
- mutation: D,
86
+ await M.mutate({
87
+ mutation: O,
87
88
  variables: { input: {
88
89
  id: e,
89
90
  reason: t
90
91
  } }
91
- }), q();
92
- }, [j, q]), te = l(async (e) => {
93
- window.confirm("Remove this waitlist entry?") && (await j.mutate({
94
- mutation: O,
92
+ }), J();
93
+ }, [M, J]), ie = l(async (e) => {
94
+ window.confirm("Remove this waitlist entry?") && (await M.mutate({
95
+ mutation: k,
95
96
  variables: { id: e }
96
- }), q());
97
- }, [j, q]), ne = [
97
+ }), J());
98
+ }, [M, J]), ae = [
98
99
  {
99
100
  key: "email",
100
101
  header: "Email",
@@ -123,7 +124,7 @@ function k() {
123
124
  header: "Status",
124
125
  width: "100px",
125
126
  render: (e) => /* @__PURE__ */ y(t, {
126
- variant: w[e.status] ?? "outline",
127
+ variant: T[e.status] ?? "outline",
127
128
  className: "text-xs capitalize",
128
129
  children: e.status.toLowerCase()
129
130
  })
@@ -134,24 +135,24 @@ function k() {
134
135
  width: "110px",
135
136
  render: (e) => a(e.createdAt)
136
137
  },
137
- ...k ? [{
138
+ ...A ? [{
138
139
  key: "actions",
139
140
  header: "Actions",
140
141
  width: "140px",
141
142
  render: (e) => /* @__PURE__ */ b("div", {
142
143
  className: "flex items-center gap-1",
143
144
  children: [e.status === "PENDING" && /* @__PURE__ */ b(v, { children: [/* @__PURE__ */ y("button", {
144
- onClick: () => $(e.id),
145
- className: "rounded p-1 text-green-500 hover:bg-green-500/10",
145
+ onClick: () => ne(e.id),
146
+ className: "rounded p-1 text-status-success-text hover:bg-status-success-bg",
146
147
  title: "Approve",
147
148
  children: /* @__PURE__ */ y(p, { className: "h-4 w-4" })
148
149
  }), /* @__PURE__ */ y("button", {
149
- onClick: () => ee(e.id),
150
- className: "rounded p-1 text-red-500 hover:bg-red-500/10",
150
+ onClick: () => re(e.id),
151
+ className: "rounded p-1 text-status-error-text hover:bg-status-error-bg",
151
152
  title: "Reject",
152
153
  children: /* @__PURE__ */ y(_, { className: "h-4 w-4" })
153
154
  })] }), /* @__PURE__ */ y("button", {
154
- onClick: () => te(e.id),
155
+ onClick: () => ie(e.id),
155
156
  className: "rounded p-1 text-text-secondary hover:bg-bg-sunken",
156
157
  title: "Remove",
157
158
  children: /* @__PURE__ */ y(g, { className: "h-4 w-4" })
@@ -160,33 +161,47 @@ function k() {
160
161
  }] : []
161
162
  ];
162
163
  return /* @__PURE__ */ y(c, {
163
- allowed: x,
164
- isLoading: A,
164
+ allowed: S,
165
+ isLoading: j,
165
166
  pageTitle: "Waitlist",
166
167
  denyMessage: "You don't have permission to view the waitlist.",
167
168
  children: /* @__PURE__ */ y(i, {
168
169
  title: "Waitlist Management",
169
170
  gradientWord: "Management",
170
- description: `Product waitlist entries${X ? ` \u00b7 ${X} total` : ""}`,
171
+ description: `Product waitlist entries${Z ? ` \u00b7 ${Z} total` : ""}`,
171
172
  children: /* @__PURE__ */ b("div", {
172
173
  className: "space-y-4",
173
174
  children: [
175
+ /* @__PURE__ */ y(ee, { children: "People who signed up for early access to a product land here before they have an account. Use this page to review who's waiting, then approve the ones you want to let in (which provisions their access) or reject the rest — clearing the pending queue keeps onboarding moving." }),
176
+ /* @__PURE__ */ b(x, {
177
+ className: "flex items-center gap-4 p-5",
178
+ children: [/* @__PURE__ */ y("div", {
179
+ className: "flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-action-primary-bg/15 text-action-primary-bg",
180
+ children: /* @__PURE__ */ y(m, { className: "h-6 w-6" })
181
+ }), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("div", {
182
+ className: "text-3xl font-bold tabular-nums text-text-primary",
183
+ children: te
184
+ }), /* @__PURE__ */ y("p", {
185
+ className: "text-sm text-text-secondary",
186
+ children: "pending entries awaiting review"
187
+ })] })]
188
+ }),
174
189
  /* @__PURE__ */ b("div", {
175
190
  className: "flex flex-wrap items-center gap-3",
176
191
  children: [/* @__PURE__ */ b("div", {
177
192
  className: "relative max-w-sm flex-1",
178
193
  children: [/* @__PURE__ */ y(h, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ y(r, {
179
194
  placeholder: "Search by email, name, or company...",
180
- value: P,
195
+ value: F,
181
196
  onChange: (e) => {
182
- N(1), F(e.target.value);
197
+ P(1), I(e.target.value);
183
198
  },
184
199
  className: "pl-9"
185
200
  })]
186
201
  }), /* @__PURE__ */ b("select", {
187
- value: I,
202
+ value: L,
188
203
  onChange: (e) => {
189
- N(1), L(e.target.value);
204
+ P(1), R(e.target.value);
190
205
  },
191
206
  className: "rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm",
192
207
  children: [
@@ -209,15 +224,15 @@ function k() {
209
224
  ]
210
225
  })]
211
226
  }),
212
- H && /* @__PURE__ */ b("div", {
227
+ U && /* @__PURE__ */ b("div", {
213
228
  className: "rounded-md border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text",
214
- children: ["Failed to load waitlist: ", H.message]
229
+ children: ["Failed to load waitlist: ", U.message]
215
230
  }),
216
231
  /* @__PURE__ */ y(o, {
217
- columns: ne,
218
- data: Q,
232
+ columns: ae,
233
+ data: $,
219
234
  keyExtractor: (e) => e.id,
220
- loading: B,
235
+ loading: V,
221
236
  emptyState: /* @__PURE__ */ y(n, {
222
237
  illustration: "empty-search",
223
238
  icon: /* @__PURE__ */ y(m, { className: "h-8 w-8" }),
@@ -225,12 +240,12 @@ function k() {
225
240
  description: "No entries match your current filters"
226
241
  })
227
242
  }),
228
- X > C && /* @__PURE__ */ y(s, {
229
- page: M,
230
- totalPages: Z,
231
- totalItems: X,
232
- pageSize: C,
233
- onPageChange: N
243
+ Z > w && /* @__PURE__ */ y(s, {
244
+ page: N,
245
+ totalPages: Q,
246
+ totalItems: Z,
247
+ pageSize: w,
248
+ onPageChange: P
234
249
  })
235
250
  ]
236
251
  })
@@ -238,6 +253,6 @@ function k() {
238
253
  });
239
254
  }
240
255
  //#endregion
241
- export { k as WaitlistPage };
256
+ export { A as WaitlistPage };
242
257
 
243
258
  //# sourceMappingURL=WaitlistPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WaitlistPage.js","names":[],"sources":["../../../src/admin/pages/WaitlistPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, useEffect } from 'react';\nimport { ClipboardList, Search, Check, X, Trash2 } from 'lucide-react';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n Input,\n DataTable,\n Pagination,\n EmptyState,\n Badge,\n PermissionGuard,\n} from '../components';\nimport { formatDate } from '../utils/formatters';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { gql } from '@apollo/client';\n\nconst PAGE_SIZE = 20;\n\ntype WaitlistStatus = 'PENDING' | 'APPROVED' | 'REJECTED';\n\nconst STATUS_VARIANT: Record<WaitlistStatus, 'default' | 'secondary' | 'outline' | 'destructive'> =\n {\n PENDING: 'outline',\n APPROVED: 'default',\n REJECTED: 'destructive',\n };\n\nconst GET_WAITLIST = gql`\n query GetWaitlist($filter: WaitlistFilterInput) {\n waitlist(filter: $filter) {\n id\n email\n name\n company\n platformProductId\n status\n reason\n rejectionReason\n processedAt\n processedBy\n createdAt\n }\n }\n`;\n\nconst APPROVE_WAITLIST = gql`\n mutation ApproveWaitlist($input: ApproveWaitlistInput!) {\n approveWaitlist(input: $input) {\n id\n status\n }\n }\n`;\n\nconst REJECT_WAITLIST = gql`\n mutation RejectWaitlist($input: RejectWaitlistInput!) {\n rejectWaitlist(input: $input) {\n id\n status\n }\n }\n`;\n\nconst REMOVE_WAITLIST = gql`\n mutation RemoveFromWaitlist($id: ID!) {\n removeFromWaitlist(id: $id)\n }\n`;\n\ntype WaitlistItem = {\n id: string;\n email: string;\n name: string | null;\n company: string | null;\n platformProductId: string;\n status: WaitlistStatus;\n reason: string | null;\n rejectionReason: string | null;\n processedAt: string | null;\n processedBy: string | null;\n createdAt: string;\n};\n\nexport function WaitlistPage() {\n const {\n canViewWaitlist,\n canManageWaitlist,\n isLoading: permissionsLoading,\n } = useAdminPermissions();\n const globalClient = useGatewayClient('global');\n\n const [page, setPage] = useState(1);\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<WaitlistStatus | ''>('');\n const [data, setData] = useState<{ waitlist?: WaitlistItem[] } | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<{ message: string } | null>(null);\n const [fetchCount, setFetchCount] = useState(0);\n\n const filter = useMemo(() => {\n const f: Record<string, unknown> = {};\n if (statusFilter) f.status = statusFilter;\n return Object.keys(f).length > 0 ? f : undefined;\n }, [statusFilter]);\n\n const refetch = useCallback(() => setFetchCount((c) => c + 1), []);\n\n useEffect(() => {\n if (permissionsLoading || !canViewWaitlist) return;\n setLoading(true);\n globalClient\n .query({ query: GET_WAITLIST, variables: { filter }, fetchPolicy: 'network-only' })\n .then((result) => {\n const d = result.data as { waitlist?: WaitlistItem[] } | undefined;\n setData(d ?? null);\n setError(null);\n })\n .catch((err: Error) => setError({ message: err.message }))\n .finally(() => setLoading(false));\n }, [globalClient, filter, permissionsLoading, canViewWaitlist, fetchCount]);\n\n const allEntries: WaitlistItem[] = useMemo(\n () => (data?.waitlist ?? []) as WaitlistItem[],\n [data]\n );\n\n // Client-side search filter (email, name, company)\n const filtered = useMemo(() => {\n if (!search) return allEntries;\n const q = search.toLowerCase();\n return allEntries.filter(\n (e) =>\n e.email.toLowerCase().includes(q) ||\n (e.name && e.name.toLowerCase().includes(q)) ||\n (e.company && e.company.toLowerCase().includes(q))\n );\n }, [allEntries, search]);\n\n const total = filtered.length;\n const totalPages = Math.ceil(total / PAGE_SIZE);\n const paged = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);\n\n const handleApprove = useCallback(\n async (id: string) => {\n await globalClient.mutate({ mutation: APPROVE_WAITLIST, variables: { input: { id } } });\n refetch();\n },\n [globalClient, refetch]\n );\n\n const handleReject = useCallback(\n async (id: string) => {\n const reason = window.prompt('Rejection reason (optional):');\n await globalClient.mutate({\n mutation: REJECT_WAITLIST,\n variables: { input: { id, reason } },\n });\n refetch();\n },\n [globalClient, refetch]\n );\n\n const handleRemove = useCallback(\n async (id: string) => {\n if (!window.confirm('Remove this waitlist entry?')) return;\n await globalClient.mutate({ mutation: REMOVE_WAITLIST, variables: { id } });\n refetch();\n },\n [globalClient, refetch]\n );\n\n const columns = [\n {\n key: 'email',\n header: 'Email',\n render: (e: WaitlistItem) => (\n <div className=\"min-w-0\">\n <div className=\"font-medium text-text-primary\">{e.email}</div>\n {e.name && <div className=\"text-xs text-text-secondary\">{e.name}</div>}\n </div>\n ),\n },\n {\n key: 'company',\n header: 'Company',\n width: '140px',\n render: (e: WaitlistItem) => (\n <span className=\"text-sm text-text-secondary\">{e.company || '-'}</span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n width: '100px',\n render: (e: WaitlistItem) => (\n <Badge variant={STATUS_VARIANT[e.status] ?? 'outline'} className=\"text-xs capitalize\">\n {e.status.toLowerCase()}\n </Badge>\n ),\n },\n {\n key: 'createdAt',\n header: 'Joined',\n width: '110px',\n render: (e: WaitlistItem) => formatDate(e.createdAt),\n },\n ...(canManageWaitlist\n ? [\n {\n key: 'actions',\n header: 'Actions',\n width: '140px',\n render: (e: WaitlistItem) => (\n <div className=\"flex items-center gap-1\">\n {e.status === 'PENDING' && (\n <>\n <button\n onClick={() => handleApprove(e.id)}\n className=\"rounded p-1 text-green-500 hover:bg-green-500/10\"\n title=\"Approve\"\n >\n <Check className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => handleReject(e.id)}\n className=\"rounded p-1 text-red-500 hover:bg-red-500/10\"\n title=\"Reject\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </>\n )}\n <button\n onClick={() => handleRemove(e.id)}\n className=\"rounded p-1 text-text-secondary hover:bg-bg-sunken\"\n title=\"Remove\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n ),\n },\n ]\n : []),\n ];\n\n return (\n <PermissionGuard\n allowed={canViewWaitlist}\n isLoading={permissionsLoading}\n pageTitle=\"Waitlist\"\n denyMessage=\"You don't have permission to view the waitlist.\"\n >\n <PageLayout\n title=\"Waitlist Management\"\n gradientWord=\"Management\"\n description={`Product waitlist entries${total ? ` \\u00b7 ${total} total` : ''}`}\n >\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search by email, name, or company...\"\n value={search}\n onChange={(e) => {\n setPage(1);\n setSearch(e.target.value);\n }}\n className=\"pl-9\"\n />\n </div>\n <select\n value={statusFilter}\n onChange={(e) => {\n setPage(1);\n setStatusFilter(e.target.value as WaitlistStatus | '');\n }}\n className=\"rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n >\n <option value=\"\">All statuses</option>\n <option value=\"PENDING\">Pending</option>\n <option value=\"APPROVED\">Approved</option>\n <option value=\"REJECTED\">Rejected</option>\n </select>\n </div>\n\n {error && (\n <div className=\"rounded-md border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n Failed to load waitlist: {error.message}\n </div>\n )}\n\n <DataTable\n columns={columns}\n data={paged}\n keyExtractor={(e) => e.id}\n loading={loading}\n emptyState={\n <EmptyState\n illustration=\"empty-search\"\n icon={<ClipboardList className=\"h-8 w-8\" />}\n title=\"No waitlist entries\"\n description=\"No entries match your current filters\"\n />\n }\n />\n\n {total > PAGE_SIZE && (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalItems={total}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n />\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAgBA,IAAM,IAAY,IAIZ,IACJ;CACE,SAAS;CACT,UAAU;CACV,UAAU;CACX,EAEG,IAAe,CAAG;;;;;;;;;;;;;;;;GAkBlB,IAAmB,CAAG;;;;;;;GAStB,IAAkB,CAAG;;;;;;;GASrB,IAAkB,CAAG;;;;;AAoB3B,SAAgB,IAAe;CAC7B,IAAM,EACJ,oBACA,sBACA,WAAW,MACT,GAAqB,EACnB,IAAe,EAAiB,SAAS,EAEzC,CAAC,GAAM,KAAW,EAAS,EAAE,EAC7B,CAAC,GAAQ,KAAa,EAAS,GAAG,EAClC,CAAC,GAAc,KAAmB,EAA8B,GAAG,EACnE,CAAC,GAAM,KAAW,EAA+C,KAAK,EACtE,CAAC,GAAS,KAAc,EAAS,GAAM,EACvC,CAAC,GAAO,KAAY,EAAqC,KAAK,EAC9D,CAAC,GAAY,KAAiB,EAAS,EAAE,EAEzC,IAAS,QAAc;EAC3B,IAAM,IAA6B,EAAE;AAErC,SADI,MAAc,EAAE,SAAS,IACtB,OAAO,KAAK,EAAE,CAAC,SAAS,IAAI,IAAI,KAAA;IACtC,CAAC,EAAa,CAAC,EAEZ,IAAU,QAAkB,GAAe,MAAM,IAAI,EAAE,EAAE,EAAE,CAAC;AAElE,SAAgB;AACV,OAAsB,CAAC,MAC3B,EAAW,GAAK,EAChB,EACG,MAAM;GAAE,OAAO;GAAc,WAAW,EAAE,WAAQ;GAAE,aAAa;GAAgB,CAAC,CAClF,MAAM,MAAW;GAChB,IAAM,IAAI,EAAO;AAEjB,GADA,EAAQ,KAAK,KAAK,EAClB,EAAS,KAAK;IACd,CACD,OAAO,MAAe,EAAS,EAAE,SAAS,EAAI,SAAS,CAAC,CAAC,CACzD,cAAc,EAAW,GAAM,CAAC;IAClC;EAAC;EAAc;EAAQ;EAAoB;EAAiB;EAAW,CAAC;CAE3E,IAAM,IAA6B,QAC1B,GAAM,YAAY,EAAE,EAC3B,CAAC,EAAK,CACP,EAGK,IAAW,QAAc;AAC7B,MAAI,CAAC,EAAQ,QAAO;EACpB,IAAM,IAAI,EAAO,aAAa;AAC9B,SAAO,EAAW,QACf,MACC,EAAE,MAAM,aAAa,CAAC,SAAS,EAAE,IAChC,EAAE,QAAQ,EAAE,KAAK,aAAa,CAAC,SAAS,EAAE,IAC1C,EAAE,WAAW,EAAE,QAAQ,aAAa,CAAC,SAAS,EAAE,CACpD;IACA,CAAC,GAAY,EAAO,CAAC,EAElB,IAAQ,EAAS,QACjB,IAAa,KAAK,KAAK,IAAQ,EAAU,EACzC,IAAQ,EAAS,OAAO,IAAO,KAAK,GAAW,IAAO,EAAU,EAEhE,IAAgB,EACpB,OAAO,MAAe;AAEpB,EADA,MAAM,EAAa,OAAO;GAAE,UAAU;GAAkB,WAAW,EAAE,OAAO,EAAE,OAAI,EAAE;GAAE,CAAC,EACvF,GAAS;IAEX,CAAC,GAAc,EAAQ,CACxB,EAEK,KAAe,EACnB,OAAO,MAAe;EACpB,IAAM,IAAS,OAAO,OAAO,+BAA+B;AAK5D,EAJA,MAAM,EAAa,OAAO;GACxB,UAAU;GACV,WAAW,EAAE,OAAO;IAAE;IAAI;IAAQ,EAAE;GACrC,CAAC,EACF,GAAS;IAEX,CAAC,GAAc,EAAQ,CACxB,EAEK,KAAe,EACnB,OAAO,MAAe;AACf,SAAO,QAAQ,8BAA8B,KAClD,MAAM,EAAa,OAAO;GAAE,UAAU;GAAiB,WAAW,EAAE,OAAI;GAAE,CAAC,EAC3E,GAAS;IAEX,CAAC,GAAc,EAAQ,CACxB,EAEK,KAAU;EACd;GACE,KAAK;GACL,QAAQ;GACR,SAAS,MACP,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAiC,EAAE;KAAY,CAAA,EAC7D,EAAE,QAAQ,kBAAC,OAAD;KAAK,WAAU;eAA+B,EAAE;KAAW,CAAA,CAClE;;GAET;EACD;GACE,KAAK;GACL,QAAQ;GACR,OAAO;GACP,SAAS,MACP,kBAAC,QAAD;IAAM,WAAU;cAA+B,EAAE,WAAW;IAAW,CAAA;GAE1E;EACD;GACE,KAAK;GACL,QAAQ;GACR,OAAO;GACP,SAAS,MACP,kBAAC,GAAD;IAAO,SAAS,EAAe,EAAE,WAAW;IAAW,WAAU;cAC9D,EAAE,OAAO,aAAa;IACjB,CAAA;GAEX;EACD;GACE,KAAK;GACL,QAAQ;GACR,OAAO;GACP,SAAS,MAAoB,EAAW,EAAE,UAAU;GACrD;EACD,GAAI,IACA,CACE;GACE,KAAK;GACL,QAAQ;GACR,OAAO;GACP,SAAS,MACP,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,EAAE,WAAW,aACZ,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,UAAD;KACE,eAAe,EAAc,EAAE,GAAG;KAClC,WAAU;KACV,OAAM;eAEN,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACtB,CAAA,EACT,kBAAC,UAAD;KACE,eAAe,GAAa,EAAE,GAAG;KACjC,WAAU;KACV,OAAM;eAEN,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;KAClB,CAAA,CACR,EAAA,CAAA,EAEL,kBAAC,UAAD;KACE,eAAe,GAAa,EAAE,GAAG;KACjC,WAAU;KACV,OAAM;eAEN,kBAAC,GAAD,EAAQ,WAAU,WAAY,CAAA;KACvB,CAAA,CACL;;GAET,CACF,GACD,EAAE;EACP;AAED,QACE,kBAAC,GAAD;EACE,SAAS;EACT,WAAW;EACX,WAAU;EACV,aAAY;YAEZ,kBAAC,GAAD;GACE,OAAM;GACN,cAAa;GACb,aAAa,2BAA2B,IAAQ,WAAW,EAAM,UAAU;aAE3E,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,GAAD;QACE,aAAY;QACZ,OAAO;QACP,WAAW,MAAM;AAEf,SADA,EAAQ,EAAE,EACV,EAAU,EAAE,OAAO,MAAM;;QAE3B,WAAU;QACV,CAAA,CACE;UACN,kBAAC,UAAD;OACE,OAAO;OACP,WAAW,MAAM;AAEf,QADA,EAAQ,EAAE,EACV,EAAgB,EAAE,OAAO,MAA6B;;OAExD,WAAU;iBANZ;QAQE,kBAAC,UAAD;SAAQ,OAAM;mBAAG;SAAqB,CAAA;QACtC,kBAAC,UAAD;SAAQ,OAAM;mBAAU;SAAgB,CAAA;QACxC,kBAAC,UAAD;SAAQ,OAAM;mBAAW;SAAiB,CAAA;QAC1C,kBAAC,UAAD;SAAQ,OAAM;mBAAW;SAAiB,CAAA;QACnC;SACL;;KAEL,KACC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CAA0H,6BAC9F,EAAM,QAC5B;;KAGR,kBAAC,GAAD;MACW;MACT,MAAM;MACN,eAAe,MAAM,EAAE;MACd;MACT,YACE,kBAAC,GAAD;OACE,cAAa;OACb,MAAM,kBAAC,GAAD,EAAe,WAAU,WAAY,CAAA;OAC3C,OAAM;OACN,aAAY;OACZ,CAAA;MAEJ,CAAA;KAED,IAAQ,KACP,kBAAC,GAAD;MACQ;MACM;MACZ,YAAY;MACZ,UAAU;MACV,cAAc;MACd,CAAA;KAEA;;GACK,CAAA;EACG,CAAA"}
1
+ {"version":3,"file":"WaitlistPage.js","names":[],"sources":["../../../src/admin/pages/WaitlistPage.tsx"],"sourcesContent":["import { useState, useMemo, useCallback, useEffect } from 'react';\nimport { ClipboardList, Search, Check, X, Trash2 } from 'lucide-react';\nimport { useAdminPermissions } from '../hooks/useAdminPermissions';\nimport {\n PageLayout,\n Input,\n DataTable,\n Pagination,\n EmptyState,\n Badge,\n PermissionGuard,\n} from '../components';\nimport { formatDate } from '../utils/formatters';\nimport { PagePurpose, EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { gql } from '@apollo/client';\n\nconst PAGE_SIZE = 20;\n\ntype WaitlistStatus = 'PENDING' | 'APPROVED' | 'REJECTED';\n\nconst STATUS_VARIANT: Record<WaitlistStatus, 'default' | 'secondary' | 'outline' | 'destructive'> =\n {\n PENDING: 'outline',\n APPROVED: 'default',\n REJECTED: 'destructive',\n };\n\nconst GET_WAITLIST = gql`\n query GetWaitlist($filter: WaitlistFilterInput) {\n waitlist(filter: $filter) {\n id\n email\n name\n company\n platformProductId\n status\n reason\n rejectionReason\n processedAt\n processedBy\n createdAt\n }\n }\n`;\n\nconst APPROVE_WAITLIST = gql`\n mutation ApproveWaitlist($input: ApproveWaitlistInput!) {\n approveWaitlist(input: $input) {\n id\n status\n }\n }\n`;\n\nconst REJECT_WAITLIST = gql`\n mutation RejectWaitlist($input: RejectWaitlistInput!) {\n rejectWaitlist(input: $input) {\n id\n status\n }\n }\n`;\n\nconst REMOVE_WAITLIST = gql`\n mutation RemoveFromWaitlist($id: ID!) {\n removeFromWaitlist(id: $id)\n }\n`;\n\ntype WaitlistItem = {\n id: string;\n email: string;\n name: string | null;\n company: string | null;\n platformProductId: string;\n status: WaitlistStatus;\n reason: string | null;\n rejectionReason: string | null;\n processedAt: string | null;\n processedBy: string | null;\n createdAt: string;\n};\n\nexport function WaitlistPage() {\n const {\n canViewWaitlist,\n canManageWaitlist,\n isLoading: permissionsLoading,\n } = useAdminPermissions();\n const globalClient = useGatewayClient('global');\n\n const [page, setPage] = useState(1);\n const [search, setSearch] = useState('');\n const [statusFilter, setStatusFilter] = useState<WaitlistStatus | ''>('');\n const [data, setData] = useState<{ waitlist?: WaitlistItem[] } | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<{ message: string } | null>(null);\n const [fetchCount, setFetchCount] = useState(0);\n\n const filter = useMemo(() => {\n const f: Record<string, unknown> = {};\n if (statusFilter) f.status = statusFilter;\n return Object.keys(f).length > 0 ? f : undefined;\n }, [statusFilter]);\n\n const refetch = useCallback(() => setFetchCount((c) => c + 1), []);\n\n useEffect(() => {\n if (permissionsLoading || !canViewWaitlist) return;\n setLoading(true);\n globalClient\n .query({ query: GET_WAITLIST, variables: { filter }, fetchPolicy: 'network-only' })\n .then((result) => {\n const d = result.data as { waitlist?: WaitlistItem[] } | undefined;\n setData(d ?? null);\n setError(null);\n })\n .catch((err: Error) => setError({ message: err.message }))\n .finally(() => setLoading(false));\n }, [globalClient, filter, permissionsLoading, canViewWaitlist, fetchCount]);\n\n const allEntries: WaitlistItem[] = useMemo(\n () => (data?.waitlist ?? []) as WaitlistItem[],\n [data]\n );\n\n // Client-side search filter (email, name, company)\n const filtered = useMemo(() => {\n if (!search) return allEntries;\n const q = search.toLowerCase();\n return allEntries.filter(\n (e) =>\n e.email.toLowerCase().includes(q) ||\n (e.name && e.name.toLowerCase().includes(q)) ||\n (e.company && e.company.toLowerCase().includes(q))\n );\n }, [allEntries, search]);\n\n const total = filtered.length;\n const totalPages = Math.ceil(total / PAGE_SIZE);\n const paged = filtered.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE);\n const pendingCount = useMemo(\n () => allEntries.filter((e) => e.status === 'PENDING').length,\n [allEntries]\n );\n\n const handleApprove = useCallback(\n async (id: string) => {\n await globalClient.mutate({ mutation: APPROVE_WAITLIST, variables: { input: { id } } });\n refetch();\n },\n [globalClient, refetch]\n );\n\n const handleReject = useCallback(\n async (id: string) => {\n const reason = window.prompt('Rejection reason (optional):');\n await globalClient.mutate({\n mutation: REJECT_WAITLIST,\n variables: { input: { id, reason } },\n });\n refetch();\n },\n [globalClient, refetch]\n );\n\n const handleRemove = useCallback(\n async (id: string) => {\n if (!window.confirm('Remove this waitlist entry?')) return;\n await globalClient.mutate({ mutation: REMOVE_WAITLIST, variables: { id } });\n refetch();\n },\n [globalClient, refetch]\n );\n\n const columns = [\n {\n key: 'email',\n header: 'Email',\n render: (e: WaitlistItem) => (\n <div className=\"min-w-0\">\n <div className=\"font-medium text-text-primary\">{e.email}</div>\n {e.name && <div className=\"text-xs text-text-secondary\">{e.name}</div>}\n </div>\n ),\n },\n {\n key: 'company',\n header: 'Company',\n width: '140px',\n render: (e: WaitlistItem) => (\n <span className=\"text-sm text-text-secondary\">{e.company || '-'}</span>\n ),\n },\n {\n key: 'status',\n header: 'Status',\n width: '100px',\n render: (e: WaitlistItem) => (\n <Badge variant={STATUS_VARIANT[e.status] ?? 'outline'} className=\"text-xs capitalize\">\n {e.status.toLowerCase()}\n </Badge>\n ),\n },\n {\n key: 'createdAt',\n header: 'Joined',\n width: '110px',\n render: (e: WaitlistItem) => formatDate(e.createdAt),\n },\n ...(canManageWaitlist\n ? [\n {\n key: 'actions',\n header: 'Actions',\n width: '140px',\n render: (e: WaitlistItem) => (\n <div className=\"flex items-center gap-1\">\n {e.status === 'PENDING' && (\n <>\n <button\n onClick={() => handleApprove(e.id)}\n className=\"rounded p-1 text-status-success-text hover:bg-status-success-bg\"\n title=\"Approve\"\n >\n <Check className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => handleReject(e.id)}\n className=\"rounded p-1 text-status-error-text hover:bg-status-error-bg\"\n title=\"Reject\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </>\n )}\n <button\n onClick={() => handleRemove(e.id)}\n className=\"rounded p-1 text-text-secondary hover:bg-bg-sunken\"\n title=\"Remove\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n ),\n },\n ]\n : []),\n ];\n\n return (\n <PermissionGuard\n allowed={canViewWaitlist}\n isLoading={permissionsLoading}\n pageTitle=\"Waitlist\"\n denyMessage=\"You don't have permission to view the waitlist.\"\n >\n <PageLayout\n title=\"Waitlist Management\"\n gradientWord=\"Management\"\n description={`Product waitlist entries${total ? ` \\u00b7 ${total} total` : ''}`}\n >\n <div className=\"space-y-4\">\n <PagePurpose>\n People who signed up for early access to a product land here before they have an\n account. Use this page to review who&apos;s waiting, then approve the ones you want to\n let in (which provisions their access) or reject the rest — clearing the pending queue\n keeps onboarding moving.\n </PagePurpose>\n\n {/* Emphasis: pending entries — the count that needs action. */}\n <EmphasisPanel className=\"flex items-center gap-4 p-5\">\n <div className=\"flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-action-primary-bg/15 text-action-primary-bg\">\n <ClipboardList className=\"h-6 w-6\" />\n </div>\n <div>\n <div className=\"text-3xl font-bold tabular-nums text-text-primary\">\n {pendingCount}\n </div>\n <p className=\"text-sm text-text-secondary\">pending entries awaiting review</p>\n </div>\n </EmphasisPanel>\n\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"relative max-w-sm flex-1\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input\n placeholder=\"Search by email, name, or company...\"\n value={search}\n onChange={(e) => {\n setPage(1);\n setSearch(e.target.value);\n }}\n className=\"pl-9\"\n />\n </div>\n <select\n value={statusFilter}\n onChange={(e) => {\n setPage(1);\n setStatusFilter(e.target.value as WaitlistStatus | '');\n }}\n className=\"rounded-md border border-border-default bg-bg-surface px-3 py-2 text-sm\"\n >\n <option value=\"\">All statuses</option>\n <option value=\"PENDING\">Pending</option>\n <option value=\"APPROVED\">Approved</option>\n <option value=\"REJECTED\">Rejected</option>\n </select>\n </div>\n\n {error && (\n <div className=\"rounded-md border border-status-error-border/50 bg-status-error-bg/10 p-4 text-sm text-status-error-text\">\n Failed to load waitlist: {error.message}\n </div>\n )}\n\n <DataTable\n columns={columns}\n data={paged}\n keyExtractor={(e) => e.id}\n loading={loading}\n emptyState={\n <EmptyState\n illustration=\"empty-search\"\n icon={<ClipboardList className=\"h-8 w-8\" />}\n title=\"No waitlist entries\"\n description=\"No entries match your current filters\"\n />\n }\n />\n\n {total > PAGE_SIZE && (\n <Pagination\n page={page}\n totalPages={totalPages}\n totalItems={total}\n pageSize={PAGE_SIZE}\n onPageChange={setPage}\n />\n )}\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -7,12 +7,13 @@ import "../components/index.js";
7
7
  import { useGetWorkflowSystemOverviewQuery as l, useListActiveWorkflowsQuery as u } from "../../generated/wspace-operations.js";
8
8
  import { Activity as d, AlertCircle as f, Clock as p, GitBranch as m, Play as h, Plus as g, Search as _ } from "lucide-react";
9
9
  import { jsx as v, jsxs as y } from "react/jsx-runtime";
10
+ import { PagePurpose as b } from "@burdenoff/fe-libs/ui";
10
11
  //#region src/admin/pages/WorkflowsPage.tsx
11
- function b() {
12
- let { data: b, loading: x } = l(), { data: S, loading: C } = u({ variables: {
12
+ function x() {
13
+ let { data: x, loading: S } = l(), { data: C, loading: w } = u({ variables: {
13
14
  limit: 10,
14
15
  offset: 0
15
- } }), w = b?.systemOverview, T = S?.activeWorkflows || [];
16
+ } }), T = x?.systemOverview, E = C?.activeWorkflows || [];
16
17
  return /* @__PURE__ */ v(i, {
17
18
  title: "Workflows",
18
19
  gradientWord: "Workflows",
@@ -20,110 +21,114 @@ function b() {
20
21
  actions: /* @__PURE__ */ y(t, { children: [/* @__PURE__ */ v(g, { className: "mr-2 h-4 w-4" }), "Create Workflow"] }),
21
22
  children: /* @__PURE__ */ y("div", {
22
23
  className: "space-y-8",
23
- children: [/* @__PURE__ */ y(s, { children: [
24
- /* @__PURE__ */ v(o, {
25
- title: "Total Workflows",
26
- hero: !0,
27
- value: w?.totalWorkflows ?? 0,
28
- loading: x,
29
- icon: /* @__PURE__ */ v(m, { className: "h-4 w-4" })
30
- }),
31
- /* @__PURE__ */ v(o, {
32
- title: "Active Runs",
33
- value: w?.activeWorkflows ?? 0,
34
- loading: x,
35
- icon: /* @__PURE__ */ v(h, { className: "h-4 w-4" }),
36
- trend: w?.activeWorkflows && w.activeWorkflows > 0 ? "up" : "neutral"
37
- }),
38
- /* @__PURE__ */ v(o, {
39
- title: "Failed (24h)",
40
- value: w?.failedWorkflows ?? 0,
41
- loading: x,
42
- icon: /* @__PURE__ */ v(f, { className: "h-4 w-4" }),
43
- priority: w?.failedWorkflows && w.failedWorkflows > 0 ? "high" : "normal"
44
- }),
45
- /* @__PURE__ */ v(o, {
46
- title: "Queued Jobs",
47
- value: w?.queuedJobs ?? 0,
48
- loading: x,
49
- icon: /* @__PURE__ */ v(p, { className: "h-4 w-4" })
50
- })
51
- ] }), /* @__PURE__ */ y("div", {
52
- className: "space-y-4",
53
- children: [/* @__PURE__ */ y("div", {
54
- className: "flex items-center justify-between",
55
- children: [/* @__PURE__ */ v("h2", {
56
- className: "text-lg font-semibold",
57
- children: "Active Workflow Runs"
58
- }), /* @__PURE__ */ y("div", {
59
- className: "relative max-w-sm flex-1 ml-4",
60
- children: [/* @__PURE__ */ v(_, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ v(r, {
61
- placeholder: "Search active runs...",
62
- className: "pl-9"
24
+ children: [
25
+ /* @__PURE__ */ v(b, { children: "Workflows are the automated, multi-step processes running across the platform. Use this page to see how many are defined, watch which runs are live right now, and quickly spot ones that have failed or are stuck in the queue so you can investigate." }),
26
+ /* @__PURE__ */ y(s, { children: [
27
+ /* @__PURE__ */ v(o, {
28
+ title: "Total Workflows",
29
+ hero: !0,
30
+ value: T?.totalWorkflows ?? 0,
31
+ loading: S,
32
+ icon: /* @__PURE__ */ v(m, { className: "h-4 w-4" })
33
+ }),
34
+ /* @__PURE__ */ v(o, {
35
+ title: "Active Runs",
36
+ value: T?.activeWorkflows ?? 0,
37
+ loading: S,
38
+ icon: /* @__PURE__ */ v(h, { className: "h-4 w-4" }),
39
+ trend: T?.activeWorkflows && T.activeWorkflows > 0 ? "up" : "neutral"
40
+ }),
41
+ /* @__PURE__ */ v(o, {
42
+ title: "Failed (24h)",
43
+ value: T?.failedWorkflows ?? 0,
44
+ loading: S,
45
+ icon: /* @__PURE__ */ v(f, { className: "h-4 w-4" }),
46
+ priority: T?.failedWorkflows && T.failedWorkflows > 0 ? "high" : "normal"
47
+ }),
48
+ /* @__PURE__ */ v(o, {
49
+ title: "Queued Jobs",
50
+ value: T?.queuedJobs ?? 0,
51
+ loading: S,
52
+ icon: /* @__PURE__ */ v(p, { className: "h-4 w-4" })
53
+ })
54
+ ] }),
55
+ /* @__PURE__ */ y("div", {
56
+ className: "space-y-4",
57
+ children: [/* @__PURE__ */ y("div", {
58
+ className: "flex items-center justify-between",
59
+ children: [/* @__PURE__ */ v("h2", {
60
+ className: "text-lg font-semibold",
61
+ children: "Active Workflow Runs"
62
+ }), /* @__PURE__ */ y("div", {
63
+ className: "relative max-w-sm flex-1 ml-4",
64
+ children: [/* @__PURE__ */ v(_, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary" }), /* @__PURE__ */ v(r, {
65
+ placeholder: "Search active runs...",
66
+ className: "pl-9"
67
+ })]
63
68
  })]
69
+ }), /* @__PURE__ */ v(c, {
70
+ columns: [
71
+ {
72
+ key: "workflowName",
73
+ header: "Workflow",
74
+ render: (e) => /* @__PURE__ */ y("div", {
75
+ className: "flex flex-col",
76
+ children: [/* @__PURE__ */ v("span", {
77
+ className: "font-medium text-text-primary",
78
+ children: e.workflowName
79
+ }), /* @__PURE__ */ y("span", {
80
+ className: "text-xs text-text-secondary",
81
+ children: ["v", e.workflowVersion]
82
+ })]
83
+ })
84
+ },
85
+ {
86
+ key: "runId",
87
+ header: "Run ID",
88
+ render: (e) => /* @__PURE__ */ y("code", {
89
+ className: "text-xs",
90
+ children: [e.runId.substring(0, 8), "..."]
91
+ })
92
+ },
93
+ {
94
+ key: "status",
95
+ header: "Status",
96
+ render: (t) => /* @__PURE__ */ v(e, {
97
+ variant: {
98
+ RUNNING: "success",
99
+ PENDING: "warning",
100
+ FAILED: "destructive",
101
+ COMPLETED: "default"
102
+ }[t.status] || "outline",
103
+ children: t.status
104
+ })
105
+ },
106
+ {
107
+ key: "currentNode",
108
+ header: "Current Node"
109
+ },
110
+ {
111
+ key: "createdAt",
112
+ header: "Started At",
113
+ render: (e) => a(e.createdAt)
114
+ }
115
+ ],
116
+ data: E,
117
+ keyExtractor: (e) => e.runId,
118
+ loading: w,
119
+ emptyState: /* @__PURE__ */ v(n, {
120
+ illustration: "empty-data",
121
+ icon: /* @__PURE__ */ v(d, { className: "h-8 w-8" }),
122
+ title: "No active workflows",
123
+ description: "There are currently no workflows running in the system"
124
+ })
64
125
  })]
65
- }), /* @__PURE__ */ v(c, {
66
- columns: [
67
- {
68
- key: "workflowName",
69
- header: "Workflow",
70
- render: (e) => /* @__PURE__ */ y("div", {
71
- className: "flex flex-col",
72
- children: [/* @__PURE__ */ v("span", {
73
- className: "font-medium text-text-primary",
74
- children: e.workflowName
75
- }), /* @__PURE__ */ y("span", {
76
- className: "text-xs text-text-secondary",
77
- children: ["v", e.workflowVersion]
78
- })]
79
- })
80
- },
81
- {
82
- key: "runId",
83
- header: "Run ID",
84
- render: (e) => /* @__PURE__ */ y("code", {
85
- className: "text-xs",
86
- children: [e.runId.substring(0, 8), "..."]
87
- })
88
- },
89
- {
90
- key: "status",
91
- header: "Status",
92
- render: (t) => /* @__PURE__ */ v(e, {
93
- variant: {
94
- RUNNING: "success",
95
- PENDING: "warning",
96
- FAILED: "destructive",
97
- COMPLETED: "default"
98
- }[t.status] || "outline",
99
- children: t.status
100
- })
101
- },
102
- {
103
- key: "currentNode",
104
- header: "Current Node"
105
- },
106
- {
107
- key: "createdAt",
108
- header: "Started At",
109
- render: (e) => a(e.createdAt)
110
- }
111
- ],
112
- data: T,
113
- keyExtractor: (e) => e.runId,
114
- loading: C,
115
- emptyState: /* @__PURE__ */ v(n, {
116
- illustration: "empty-data",
117
- icon: /* @__PURE__ */ v(d, { className: "h-8 w-8" }),
118
- title: "No active workflows",
119
- description: "There are currently no workflows running in the system"
120
- })
121
- })]
122
- })]
126
+ })
127
+ ]
123
128
  })
124
129
  });
125
130
  }
126
131
  //#endregion
127
- export { b as WorkflowsPage };
132
+ export { x as WorkflowsPage };
128
133
 
129
134
  //# sourceMappingURL=WorkflowsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkflowsPage.js","names":[],"sources":["../../../src/admin/pages/WorkflowsPage.tsx"],"sourcesContent":["import { GitBranch, Search, Plus, Activity, AlertCircle, Clock, Play } from 'lucide-react';\nimport {\n PageLayout,\n Input,\n Button,\n EmptyState,\n MetricCard,\n MetricsGrid,\n DataTable,\n Badge,\n} from '../components';\nimport {\n useGetWorkflowSystemOverviewQuery,\n useListActiveWorkflowsQuery,\n} from '../../generated/wspace-operations';\nimport { formatDateTime } from '../utils/formatters';\n\nexport function WorkflowsPage() {\n const { data: overviewData, loading: overviewLoading } = useGetWorkflowSystemOverviewQuery();\n\n const { data: listData, loading: listLoading } = useListActiveWorkflowsQuery({\n variables: { limit: 10, offset: 0 },\n });\n\n const overview = overviewData?.systemOverview;\n const activeRuns = listData?.activeWorkflows || [];\n\n const columns = [\n {\n key: 'workflowName',\n header: 'Workflow',\n render: (run: any) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium text-text-primary\">{run.workflowName}</span>\n <span className=\"text-xs text-text-secondary\">v{run.workflowVersion}</span>\n </div>\n ),\n },\n {\n key: 'runId',\n header: 'Run ID',\n render: (run: any) => <code className=\"text-xs\">{run.runId.substring(0, 8)}...</code>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (run: any) => {\n const variants: Record<\n string,\n 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline'\n > = {\n RUNNING: 'success',\n PENDING: 'warning',\n FAILED: 'destructive',\n COMPLETED: 'default',\n };\n return <Badge variant={variants[run.status] || 'outline'}>{run.status}</Badge>;\n },\n },\n {\n key: 'currentNode',\n header: 'Current Node',\n },\n {\n key: 'createdAt',\n header: 'Started At',\n render: (run: any) => formatDateTime(run.createdAt),\n },\n ];\n\n return (\n <PageLayout\n title=\"Workflows\"\n gradientWord=\"Workflows\"\n description=\"Manage and monitor platform workflows\"\n actions={\n <Button>\n <Plus className=\"mr-2 h-4 w-4\" />\n Create Workflow\n </Button>\n }\n >\n <div className=\"space-y-8\">\n <MetricsGrid>\n <MetricCard\n title=\"Total Workflows\"\n hero\n value={overview?.totalWorkflows ?? 0}\n loading={overviewLoading}\n icon={<GitBranch className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Active Runs\"\n value={overview?.activeWorkflows ?? 0}\n loading={overviewLoading}\n icon={<Play className=\"h-4 w-4\" />}\n trend={overview?.activeWorkflows && overview.activeWorkflows > 0 ? 'up' : 'neutral'}\n />\n <MetricCard\n title=\"Failed (24h)\"\n value={overview?.failedWorkflows ?? 0}\n loading={overviewLoading}\n icon={<AlertCircle className=\"h-4 w-4\" />}\n priority={overview?.failedWorkflows && overview.failedWorkflows > 0 ? 'high' : 'normal'}\n />\n <MetricCard\n title=\"Queued Jobs\"\n value={overview?.queuedJobs ?? 0}\n loading={overviewLoading}\n icon={<Clock className=\"h-4 w-4\" />}\n />\n </MetricsGrid>\n\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">Active Workflow Runs</h2>\n <div className=\"relative max-w-sm flex-1 ml-4\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input placeholder=\"Search active runs...\" className=\"pl-9\" />\n </div>\n </div>\n\n <DataTable\n columns={columns}\n data={activeRuns as any[]}\n keyExtractor={(run) => run.runId}\n loading={listLoading}\n emptyState={\n <EmptyState\n illustration=\"empty-data\"\n icon={<Activity className=\"h-8 w-8\" />}\n title=\"No active workflows\"\n description=\"There are currently no workflows running in the system\"\n />\n }\n />\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,IAAgB;CAC9B,IAAM,EAAE,MAAM,GAAc,SAAS,MAAoB,GAAmC,EAEtF,EAAE,MAAM,GAAU,SAAS,MAAgB,EAA4B,EAC3E,WAAW;EAAE,OAAO;EAAI,QAAQ;EAAG,EACpC,CAAC,EAEI,IAAW,GAAc,gBACzB,IAAa,GAAU,mBAAmB,EAAE;AA6ClD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,cAAa;EACb,aAAY;EACZ,SACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,kBAE1B,EAAA,CAAA;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD,EAAA,UAAA;IACE,kBAAC,GAAD;KACE,OAAM;KACN,MAAA;KACA,OAAO,GAAU,kBAAkB;KACnC,SAAS;KACT,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;KACvC,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,GAAU,mBAAmB;KACpC,SAAS;KACT,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;KAClC,OAAO,GAAU,mBAAmB,EAAS,kBAAkB,IAAI,OAAO;KAC1E,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,GAAU,mBAAmB;KACpC,SAAS;KACT,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;KACzC,UAAU,GAAU,mBAAmB,EAAS,kBAAkB,IAAI,SAAS;KAC/E,CAAA;IACF,kBAAC,GAAD;KACE,OAAM;KACN,OAAO,GAAU,cAAc;KAC/B,SAAS;KACT,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;KACnC,CAAA;IACU,EAAA,CAAA,EAEd,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAwB;MAAyB,CAAA,EAC/D,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,GAAD;OAAO,aAAY;OAAwB,WAAU;OAAS,CAAA,CAC1D;QACF;QAEN,kBAAC,GAAD;KACW,SAhGH;MACd;OACE,KAAK;OACL,QAAQ;OACR,SAAS,MACP,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAiC,EAAI;SAAoB,CAAA,EACzE,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAA8C,KAAE,EAAI,gBAAuB;WACvE;;OAET;MACD;OACE,KAAK;OACL,QAAQ;OACR,SAAS,MAAa,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAA2B,EAAI,MAAM,UAAU,GAAG,EAAE,EAAC,MAAU;;OACtF;MACD;OACE,KAAK;OACL,QAAQ;OACR,SAAS,MAUA,kBAAC,GAAD;QAAO,SANV;SACF,SAAS;SACT,SAAS;SACT,QAAQ;SACR,WAAW;SACZ,CAC+B,EAAI,WAAW;kBAAY,EAAI;QAAe,CAAA;OAEjF;MACD;OACE,KAAK;OACL,QAAQ;OACT;MACD;OACE,KAAK;OACL,QAAQ;OACR,SAAS,MAAa,EAAe,EAAI,UAAU;OACpD;MACF;KAwDS,MAAM;KACN,eAAe,MAAQ,EAAI;KAC3B,SAAS;KACT,YACE,kBAAC,GAAD;MACE,cAAa;MACb,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;MACtC,OAAM;MACN,aAAY;MACZ,CAAA;KAEJ,CAAA,CACE;MACF;;EACK,CAAA"}
1
+ {"version":3,"file":"WorkflowsPage.js","names":[],"sources":["../../../src/admin/pages/WorkflowsPage.tsx"],"sourcesContent":["import { GitBranch, Search, Plus, Activity, AlertCircle, Clock, Play } from 'lucide-react';\nimport {\n PageLayout,\n Input,\n Button,\n EmptyState,\n MetricCard,\n MetricsGrid,\n DataTable,\n Badge,\n} from '../components';\nimport {\n useGetWorkflowSystemOverviewQuery,\n useListActiveWorkflowsQuery,\n} from '../../generated/wspace-operations';\nimport { formatDateTime } from '../utils/formatters';\nimport { PagePurpose } from '@burdenoff/fe-libs/ui';\n\nexport function WorkflowsPage() {\n const { data: overviewData, loading: overviewLoading } = useGetWorkflowSystemOverviewQuery();\n\n const { data: listData, loading: listLoading } = useListActiveWorkflowsQuery({\n variables: { limit: 10, offset: 0 },\n });\n\n const overview = overviewData?.systemOverview;\n const activeRuns = listData?.activeWorkflows || [];\n\n const columns = [\n {\n key: 'workflowName',\n header: 'Workflow',\n render: (run: any) => (\n <div className=\"flex flex-col\">\n <span className=\"font-medium text-text-primary\">{run.workflowName}</span>\n <span className=\"text-xs text-text-secondary\">v{run.workflowVersion}</span>\n </div>\n ),\n },\n {\n key: 'runId',\n header: 'Run ID',\n render: (run: any) => <code className=\"text-xs\">{run.runId.substring(0, 8)}...</code>,\n },\n {\n key: 'status',\n header: 'Status',\n render: (run: any) => {\n const variants: Record<\n string,\n 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline'\n > = {\n RUNNING: 'success',\n PENDING: 'warning',\n FAILED: 'destructive',\n COMPLETED: 'default',\n };\n return <Badge variant={variants[run.status] || 'outline'}>{run.status}</Badge>;\n },\n },\n {\n key: 'currentNode',\n header: 'Current Node',\n },\n {\n key: 'createdAt',\n header: 'Started At',\n render: (run: any) => formatDateTime(run.createdAt),\n },\n ];\n\n return (\n <PageLayout\n title=\"Workflows\"\n gradientWord=\"Workflows\"\n description=\"Manage and monitor platform workflows\"\n actions={\n <Button>\n <Plus className=\"mr-2 h-4 w-4\" />\n Create Workflow\n </Button>\n }\n >\n <div className=\"space-y-8\">\n <PagePurpose>\n Workflows are the automated, multi-step processes running across the platform. Use this\n page to see how many are defined, watch which runs are live right now, and quickly spot\n ones that have failed or are stuck in the queue so you can investigate.\n </PagePurpose>\n\n <MetricsGrid>\n <MetricCard\n title=\"Total Workflows\"\n hero\n value={overview?.totalWorkflows ?? 0}\n loading={overviewLoading}\n icon={<GitBranch className=\"h-4 w-4\" />}\n />\n <MetricCard\n title=\"Active Runs\"\n value={overview?.activeWorkflows ?? 0}\n loading={overviewLoading}\n icon={<Play className=\"h-4 w-4\" />}\n trend={overview?.activeWorkflows && overview.activeWorkflows > 0 ? 'up' : 'neutral'}\n />\n <MetricCard\n title=\"Failed (24h)\"\n value={overview?.failedWorkflows ?? 0}\n loading={overviewLoading}\n icon={<AlertCircle className=\"h-4 w-4\" />}\n priority={overview?.failedWorkflows && overview.failedWorkflows > 0 ? 'high' : 'normal'}\n />\n <MetricCard\n title=\"Queued Jobs\"\n value={overview?.queuedJobs ?? 0}\n loading={overviewLoading}\n icon={<Clock className=\"h-4 w-4\" />}\n />\n </MetricsGrid>\n\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">Active Workflow Runs</h2>\n <div className=\"relative max-w-sm flex-1 ml-4\">\n <Search className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-secondary\" />\n <Input placeholder=\"Search active runs...\" className=\"pl-9\" />\n </div>\n </div>\n\n <DataTable\n columns={columns}\n data={activeRuns as any[]}\n keyExtractor={(run) => run.runId}\n loading={listLoading}\n emptyState={\n <EmptyState\n illustration=\"empty-data\"\n icon={<Activity className=\"h-8 w-8\" />}\n title=\"No active workflows\"\n description=\"There are currently no workflows running in the system\"\n />\n }\n />\n </div>\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;AAkBA,SAAgB,IAAgB;CAC9B,IAAM,EAAE,MAAM,GAAc,SAAS,MAAoB,GAAmC,EAEtF,EAAE,MAAM,GAAU,SAAS,MAAgB,EAA4B,EAC3E,WAAW;EAAE,OAAO;EAAI,QAAQ;EAAG,EACpC,CAAC,EAEI,IAAW,GAAc,gBACzB,IAAa,GAAU,mBAAmB,EAAE;AA6ClD,QACE,kBAAC,GAAD;EACE,OAAM;EACN,cAAa;EACb,aAAY;EACZ,SACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,kBAE1B,EAAA,CAAA;YAGX,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD,EAAA,UAAa,2PAIC,CAAA;IAEd,kBAAC,GAAD,EAAA,UAAA;KACE,kBAAC,GAAD;MACE,OAAM;MACN,MAAA;MACA,OAAO,GAAU,kBAAkB;MACnC,SAAS;MACT,MAAM,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA;MACvC,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,GAAU,mBAAmB;MACpC,SAAS;MACT,MAAM,kBAAC,GAAD,EAAM,WAAU,WAAY,CAAA;MAClC,OAAO,GAAU,mBAAmB,EAAS,kBAAkB,IAAI,OAAO;MAC1E,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,GAAU,mBAAmB;MACpC,SAAS;MACT,MAAM,kBAAC,GAAD,EAAa,WAAU,WAAY,CAAA;MACzC,UAAU,GAAU,mBAAmB,EAAS,kBAAkB,IAAI,SAAS;MAC/E,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,GAAU,cAAc;MAC/B,SAAS;MACT,MAAM,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;MACnC,CAAA;KACU,EAAA,CAAA;IAEd,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAwB;OAAyB,CAAA,EAC/D,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wEAAyE,CAAA,EAC3F,kBAAC,GAAD;QAAO,aAAY;QAAwB,WAAU;QAAS,CAAA,CAC1D;SACF;SAEN,kBAAC,GAAD;MACW,SAtGH;OACd;QACE,KAAK;QACL,QAAQ;QACR,SAAS,MACP,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAiC,EAAI;UAAoB,CAAA,EACzE,kBAAC,QAAD;UAAM,WAAU;oBAAhB,CAA8C,KAAE,EAAI,gBAAuB;YACvE;;QAET;OACD;QACE,KAAK;QACL,QAAQ;QACR,SAAS,MAAa,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CAA2B,EAAI,MAAM,UAAU,GAAG,EAAE,EAAC,MAAU;;QACtF;OACD;QACE,KAAK;QACL,QAAQ;QACR,SAAS,MAUA,kBAAC,GAAD;SAAO,SANV;UACF,SAAS;UACT,SAAS;UACT,QAAQ;UACR,WAAW;UACZ,CAC+B,EAAI,WAAW;mBAAY,EAAI;SAAe,CAAA;QAEjF;OACD;QACE,KAAK;QACL,QAAQ;QACT;OACD;QACE,KAAK;QACL,QAAQ;QACR,SAAS,MAAa,EAAe,EAAI,UAAU;QACpD;OACF;MA8DS,MAAM;MACN,eAAe,MAAQ,EAAI;MAC3B,SAAS;MACT,YACE,kBAAC,GAAD;OACE,cAAa;OACb,MAAM,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;OACtC,OAAM;OACN,aAAY;OACZ,CAAA;MAEJ,CAAA,CACE;;IACF;;EACK,CAAA"}