@burdenoff/microfe-devportal 2026.609.4 → 2026.611.2

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 (58) hide show
  1. package/dist/devportal/DevportalRoot.js +6 -1
  2. package/dist/devportal/DevportalRoot.js.map +1 -1
  3. package/dist/devportal/DevportalRoutes.js +169 -158
  4. package/dist/devportal/DevportalRoutes.js.map +1 -1
  5. package/dist/devportal/hooks/usePackageUpload.js +120 -0
  6. package/dist/devportal/hooks/usePackageUpload.js.map +1 -0
  7. package/dist/devportal/pages/AnalyticsDashboardPage.js +73 -62
  8. package/dist/devportal/pages/AnalyticsDashboardPage.js.map +1 -1
  9. package/dist/devportal/pages/AppLogsPage.js +89 -82
  10. package/dist/devportal/pages/AppLogsPage.js.map +1 -1
  11. package/dist/devportal/pages/ApplicationDetailPage.js +948 -367
  12. package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
  13. package/dist/devportal/pages/ApplicationVersionsPage.js +56 -45
  14. package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
  15. package/dist/devportal/pages/CreateApiKeyPage.js +30 -32
  16. package/dist/devportal/pages/CreateApiKeyPage.js.map +1 -1
  17. package/dist/devportal/pages/CreateApplicationPage.js +8 -3
  18. package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
  19. package/dist/devportal/pages/CreateOAuthClientPage.js +4 -4
  20. package/dist/devportal/pages/CreateOAuthClientPage.js.map +1 -1
  21. package/dist/devportal/pages/CreateOrganizationPage.js +238 -229
  22. package/dist/devportal/pages/CreateOrganizationPage.js.map +1 -1
  23. package/dist/devportal/pages/CreateWebhookPage.js +159 -148
  24. package/dist/devportal/pages/CreateWebhookPage.js.map +1 -1
  25. package/dist/devportal/pages/DelegationsPage.js +1 -1
  26. package/dist/devportal/pages/DelegationsPage.js.map +1 -1
  27. package/dist/devportal/pages/EditApplicationPage.js +25 -17
  28. package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
  29. package/dist/devportal/pages/ListingPreviewPage.js +159 -153
  30. package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
  31. package/dist/devportal/pages/OAuthClientDetailPage.js.map +1 -1
  32. package/dist/devportal/pages/OAuthClientsPage.js +2 -2
  33. package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
  34. package/dist/devportal/pages/OrganizationDetailPage.js +158 -153
  35. package/dist/devportal/pages/OrganizationDetailPage.js.map +1 -1
  36. package/dist/devportal/pages/OrganizationMembersPage.js +223 -212
  37. package/dist/devportal/pages/OrganizationMembersPage.js.map +1 -1
  38. package/dist/devportal/pages/PublishPage.js +172 -165
  39. package/dist/devportal/pages/PublishPage.js.map +1 -1
  40. package/dist/devportal/pages/ResourcesPage.js.map +1 -1
  41. package/dist/devportal/pages/RevenueDashboardPage.js +461 -0
  42. package/dist/devportal/pages/RevenueDashboardPage.js.map +1 -0
  43. package/dist/devportal/pages/SetupPage.js +186 -181
  44. package/dist/devportal/pages/SetupPage.js.map +1 -1
  45. package/dist/devportal/pages/StoreSubmissionPage.js +76 -97
  46. package/dist/devportal/pages/StoreSubmissionPage.js.map +1 -1
  47. package/dist/devportal/pages/WebhookDetailPage.js +172 -154
  48. package/dist/devportal/pages/WebhookDetailPage.js.map +1 -1
  49. package/dist/devportal/pages/WebhooksPage.js +218 -200
  50. package/dist/devportal/pages/WebhooksPage.js.map +1 -1
  51. package/dist/devportal/providers/DevportalProvider.js +16 -13
  52. package/dist/devportal/providers/DevportalProvider.js.map +1 -1
  53. package/dist/devportal/types/index.js.map +1 -1
  54. package/dist/generated/global-operations.js +334 -223
  55. package/dist/generated/global-operations.js.map +1 -1
  56. package/package.json +1 -1
  57. package/dist/devportal/pages/RevenueRedirectPage.js +0 -14
  58. package/dist/devportal/pages/RevenueRedirectPage.js.map +0 -1
@@ -2,39 +2,50 @@ import { EmptyState as e } from "../components/EmptyState.js";
2
2
  import { PageLayout as t } from "../components/PageLayout.js";
3
3
  import { StatusBadge as n } from "../components/StatusBadge.js";
4
4
  import { SelectionCheckbox as r } from "../components/SelectionCheckbox.js";
5
- import { DataTable as i } from "../components/DataTable.js";
6
- import { InputField as a, TextareaField as o } from "../components/FormField.js";
7
- import { ConfirmDialog as s } from "../components/ConfirmDialog.js";
8
- import { useCreateApplicationVersionMutation as c, useDeprecateApplicationVersionMutation as ee, useGetApplicationVersionsQuery as te, usePublishApplicationVersionMutation as ne } from "../../generated/global-operations.js";
9
- import { PermissionGuard as l } from "../components/PermissionGuard.js";
5
+ import { DataTable as ee } from "../components/DataTable.js";
6
+ import { InputField as te, TextareaField as i } from "../components/FormField.js";
7
+ import { ConfirmDialog as a } from "../components/ConfirmDialog.js";
8
+ import { useCreateApplicationVersionMutation as ne, useDeprecateApplicationVersionMutation as re, useGetApplicationQuery as o, useGetApplicationVersionsQuery as ie, usePublishApplicationVersionMutation as s } from "../../generated/global-operations.js";
9
+ import { PermissionGuard as ae } from "../components/PermissionGuard.js";
10
10
  import "../components/index.js";
11
- import { DEVPORTAL_PERMISSIONS as u } from "../constants/permissions.js";
12
- import { useDevportalPermission as d } from "../hooks/usePermission.js";
11
+ import { DEVPORTAL_PERMISSIONS as c } from "../constants/permissions.js";
12
+ import { useDevportalPermission as l } from "../hooks/usePermission.js";
13
13
  import "../hooks/index.js";
14
- import re from "../../node_modules/date-fns/format.js";
15
- import { useMemo as f, useState as p } from "react";
16
- import { useI18n as ie } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
17
- import { AlertCircle as m, Archive as h, CheckCircle as g, Clock as ae, Loader2 as oe, Package as _, Plus as v } from "lucide-react";
18
- import { useParams as se } from "react-router-dom";
14
+ import u from "../../node_modules/date-fns/format.js";
15
+ import { useEffect as d, useMemo as f, useState as p } from "react";
16
+ import { useI18n as m } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
17
+ import { AlertCircle as h, Archive as oe, CheckCircle as g, Clock as se, Loader2 as ce, Package as _, Plus as v } from "lucide-react";
18
+ import { useParams as le } from "react-router-dom";
19
19
  import { jsx as y, jsxs as b } from "react/jsx-runtime";
20
20
  //#region src/devportal/pages/ApplicationVersionsPage.tsx
21
21
  var x = () => {
22
- let { t: x } = ie(), S = (e, t) => {
22
+ let { t: x } = m(), S = (e, t) => {
23
23
  let n = x(e);
24
24
  return n === e ? t : n;
25
- }, { allowed: C } = d(u.APP_VERSION_CREATE), { allowed: w } = d(u.APP_VERSION_PUBLISH), { allowed: T } = d(u.APP_VERSION_DEPRECATE), { appId: E } = se(), [D, O] = p(!1), [k, A] = p({
25
+ }, { allowed: C } = l(c.APP_VERSION_CREATE), { allowed: w } = l(c.APP_VERSION_PUBLISH), { allowed: T } = l(c.APP_VERSION_DEPRECATE), { appId: E } = le(), [D, O] = p(!1), [k, A] = p({
26
26
  version: "",
27
27
  releaseNotes: "",
28
28
  changelog: ""
29
- }), [j, M] = p(null), [N, P] = p(null), [F, I] = p(/* @__PURE__ */ new Set()), [L, R] = p(null), { data: z, loading: B, error: V } = te({
29
+ }), [j, M] = p(null), [N, P] = p(null), [F, I] = p(/* @__PURE__ */ new Set()), [L, R] = p(null), { data: z } = o({
30
+ variables: { id: E ?? "" },
31
+ skip: !E,
32
+ fetchPolicy: "cache-first"
33
+ });
34
+ d(() => {
35
+ let e = z?.application?.name;
36
+ if (!E || !e) return;
37
+ let t = window.__breadcrumbRegistry;
38
+ return t?.register(E, e), () => t?.unregister(E);
39
+ }, [E, z?.application?.name]);
40
+ let { data: B, loading: V, error: H } = ie({
30
41
  variables: { applicationId: E },
31
42
  skip: !E,
32
43
  fetchPolicy: "cache-and-network"
33
- }), [H, { loading: U }] = c({ refetchQueries: ["GetApplicationVersions"] }), [W] = ne({ refetchQueries: ["GetApplicationVersions"] }), [G] = ee({ refetchQueries: ["GetApplicationVersions"] }), K = f(() => z?.application?.versions?.edges?.map((e) => e.node) ?? [], [z?.application?.versions]), q = f(() => K.filter((e) => F.has(e.id)), [K, F]), J = f(() => q.filter((e) => e.status === "DRAFT"), [q]), Y = f(() => q.filter((e) => e.status === "PUBLISHED" && !e.isLatest), [q]), X = K.length > 0 && K.every((e) => F.has(e.id)), Z = K.some((e) => F.has(e.id)) && !X, ce = async () => {
44
+ }), [U, { loading: ue }] = ne({ refetchQueries: ["GetApplicationVersions"] }), [W] = s({ refetchQueries: ["GetApplicationVersions"] }), [G] = re({ refetchQueries: ["GetApplicationVersions"] }), K = f(() => B?.application?.versions?.edges?.map((e) => e.node) ?? [], [B?.application?.versions]), q = f(() => K.filter((e) => F.has(e.id)), [K, F]), J = f(() => q.filter((e) => e.status === "DRAFT"), [q]), Y = f(() => q.filter((e) => e.status === "PUBLISHED" && !e.isLatest), [q]), X = K.length > 0 && K.every((e) => F.has(e.id)), Z = K.some((e) => F.has(e.id)) && !X, de = async () => {
34
45
  if (!(!E || !k.version.trim())) {
35
46
  M(null);
36
47
  try {
37
- await H({ variables: {
48
+ await U({ variables: {
38
49
  applicationId: E,
39
50
  input: {
40
51
  version: k.version,
@@ -50,33 +61,33 @@ var x = () => {
50
61
  M(e instanceof Error ? e.message : S("devportal.versionCreateFailed", "Failed to create version"));
51
62
  }
52
63
  }
53
- }, le = async (e) => {
64
+ }, fe = async (e) => {
54
65
  M(null), P(null);
55
66
  try {
56
67
  await W({ variables: { id: e } }), P(S("devportal.versionPublishedSuccess", "Version published successfully"));
57
68
  } catch (e) {
58
69
  M(e instanceof Error ? e.message : S("devportal.versionPublishFailed", "Failed to publish version"));
59
70
  }
60
- }, ue = async (e) => {
71
+ }, pe = async (e) => {
61
72
  M(null), P(null);
62
73
  try {
63
74
  await G({ variables: { id: e } }), P(S("devportal.versionDeprecatedSuccess", "Version deprecated successfully"));
64
75
  } catch (e) {
65
76
  M(e instanceof Error ? e.message : S("devportal.versionDeprecateFailed", "Failed to deprecate version"));
66
77
  }
67
- }, de = (e) => e.status === "PUBLISHED" && e.isLatest ? /* @__PURE__ */ y(g, {
78
+ }, me = (e) => e.status === "PUBLISHED" && e.isLatest ? /* @__PURE__ */ y(g, {
68
79
  size: 16,
69
80
  className: "text-status-success-text"
70
- }) : e.status === "DRAFT" ? /* @__PURE__ */ y(ae, {
81
+ }) : e.status === "DRAFT" ? /* @__PURE__ */ y(se, {
71
82
  size: 16,
72
83
  className: "text-status-warning-text"
73
- }) : e.status === "DEPRECATED" ? /* @__PURE__ */ y(h, {
84
+ }) : e.status === "DEPRECATED" ? /* @__PURE__ */ y(oe, {
74
85
  size: 16,
75
86
  className: "text-text-secondary"
76
87
  }) : /* @__PURE__ */ y(_, {
77
88
  size: 16,
78
89
  className: "text-accent-blue"
79
- }), fe = (e, t) => {
90
+ }), he = (e, t) => {
80
91
  I((n) => {
81
92
  let r = new Set(n);
82
93
  return t ? r.add(e) : r.delete(e), r;
@@ -89,30 +100,30 @@ var x = () => {
89
100
  }), n;
90
101
  });
91
102
  }, $ = () => I(/* @__PURE__ */ new Set());
92
- return B && !z ? /* @__PURE__ */ y(t, {
103
+ return V && !B ? /* @__PURE__ */ y(t, {
93
104
  title: S("devportal.versionManagement", "Version Management"),
94
105
  description: S("devportal.versionManagementDescription", "Manage your application versions"),
95
106
  backLink: `/apps/${E}`,
96
107
  backLabel: S("devportal.backToApplication", "Back to Application"),
97
108
  children: /* @__PURE__ */ y("div", {
98
109
  className: "flex items-center justify-center py-16",
99
- children: /* @__PURE__ */ y(oe, { className: "h-8 w-8 animate-spin text-muted-foreground" })
110
+ children: /* @__PURE__ */ y(ce, { className: "h-8 w-8 animate-spin text-muted-foreground" })
100
111
  })
101
- }) : V && !z ? /* @__PURE__ */ y(t, {
112
+ }) : H && !B ? /* @__PURE__ */ y(t, {
102
113
  title: S("devportal.versionManagement", "Version Management"),
103
114
  description: S("devportal.versionManagementDescription", "Manage your application versions"),
104
115
  backLink: `/apps/${E}`,
105
116
  backLabel: S("devportal.backToApplication", "Back to Application"),
106
117
  children: /* @__PURE__ */ y(e, {
107
- icon: /* @__PURE__ */ y(m, {
118
+ icon: /* @__PURE__ */ y(h, {
108
119
  size: 32,
109
120
  className: "text-status-error-text"
110
121
  }),
111
122
  title: S("devportal.failedToLoadVersions", "Failed to load versions"),
112
- description: V.message || S("devportal.versionsLoadError", "An error occurred while loading versions")
123
+ description: H.message || S("devportal.versionsLoadError", "An error occurred while loading versions")
113
124
  })
114
- }) : /* @__PURE__ */ y(l, {
115
- permission: u.APP_READ,
125
+ }) : /* @__PURE__ */ y(ae, {
126
+ permission: c.APP_READ,
116
127
  children: /* @__PURE__ */ b(t, {
117
128
  title: S("devportal.versionManagement", "Version Management"),
118
129
  description: S("devportal.versionManagementDescription", "Manage your application versions"),
@@ -129,7 +140,7 @@ var x = () => {
129
140
  j && /* @__PURE__ */ b("div", {
130
141
  className: "rounded-lg border border-border-subtle bg-status-error-bg-subtle p-3 flex items-center gap-2 text-sm text-status-error-text",
131
142
  children: [
132
- /* @__PURE__ */ y(m, {
143
+ /* @__PURE__ */ y(h, {
133
144
  size: 16,
134
145
  className: "flex-shrink-0"
135
146
  }),
@@ -203,7 +214,7 @@ var x = () => {
203
214
  })]
204
215
  })
205
216
  }),
206
- K.length > 0 ? /* @__PURE__ */ y(i, {
217
+ K.length > 0 ? /* @__PURE__ */ y(ee, {
207
218
  data: K,
208
219
  keyField: "id",
209
220
  columns: [
@@ -220,7 +231,7 @@ var x = () => {
220
231
  onClick: (e) => e.stopPropagation(),
221
232
  children: /* @__PURE__ */ y(r, {
222
233
  checked: F.has(e.id),
223
- onChange: (t) => fe(e.id, t),
234
+ onChange: (t) => he(e.id, t),
224
235
  ariaLabel: `Select version ${e.version}`
225
236
  })
226
237
  })
@@ -231,7 +242,7 @@ var x = () => {
231
242
  render: (e) => /* @__PURE__ */ b("div", {
232
243
  className: "flex items-center gap-2",
233
244
  children: [
234
- de(e),
245
+ me(e),
235
246
  /* @__PURE__ */ y("span", {
236
247
  className: "font-mono font-medium",
237
248
  children: e.version
@@ -263,7 +274,7 @@ var x = () => {
263
274
  {
264
275
  key: "publishedAt",
265
276
  header: S("devportal.published", "Published"),
266
- render: (e) => e.publishedAt ? re(new Date(e.publishedAt), "MMM d, yyyy") : "-"
277
+ render: (e) => e.publishedAt ? u(new Date(e.publishedAt), "MMM d, yyyy") : "-"
267
278
  },
268
279
  {
269
280
  key: "actions",
@@ -275,13 +286,13 @@ var x = () => {
275
286
  className: "text-xs text-primary hover:underline disabled:opacity-50",
276
287
  disabled: !w,
277
288
  "aria-disabled": !w,
278
- onClick: () => le(e.id),
289
+ onClick: () => fe(e.id),
279
290
  children: S("devportal.publish", "Publish")
280
291
  }), e.status === "PUBLISHED" && !e.isLatest && /* @__PURE__ */ y("button", {
281
292
  className: "text-xs text-status-warning-text hover:underline disabled:opacity-50",
282
293
  disabled: !T,
283
294
  "aria-disabled": !T,
284
- onClick: () => ue(e.id),
295
+ onClick: () => pe(e.id),
285
296
  children: S("devportal.deprecate", "Deprecate")
286
297
  })]
287
298
  })
@@ -297,16 +308,16 @@ var x = () => {
297
308
  children: [/* @__PURE__ */ y(v, { size: 16 }), S("devportal.createVersion", "Create Version")]
298
309
  })
299
310
  }),
300
- D && /* @__PURE__ */ y(s, {
311
+ D && /* @__PURE__ */ y(a, {
301
312
  open: D,
302
313
  onOpenChange: O,
303
- onConfirm: ce,
314
+ onConfirm: de,
304
315
  title: S("devportal.createNewVersion", "Create New Version"),
305
- confirmLabel: U ? S("devportal.creating", "Creating...") : S("devportal.createVersion", "Create Version"),
316
+ confirmLabel: ue ? S("devportal.creating", "Creating...") : S("devportal.createVersion", "Create Version"),
306
317
  children: /* @__PURE__ */ b("div", {
307
318
  className: "space-y-4",
308
319
  children: [
309
- /* @__PURE__ */ y(a, {
320
+ /* @__PURE__ */ y(te, {
310
321
  label: S("devportal.versionNumber", "Version Number"),
311
322
  value: k.version,
312
323
  onChange: (e) => A({
@@ -317,7 +328,7 @@ var x = () => {
317
328
  hint: S("devportal.versionHint", "Use semantic versioning (major.minor.patch)"),
318
329
  required: !0
319
330
  }),
320
- /* @__PURE__ */ y(o, {
331
+ /* @__PURE__ */ y(i, {
321
332
  label: S("devportal.releaseNotes", "Release Notes"),
322
333
  value: k.releaseNotes,
323
334
  onChange: (e) => A({
@@ -327,7 +338,7 @@ var x = () => {
327
338
  placeholder: S("devportal.releaseNotesPlaceholder", "Brief description of this release"),
328
339
  rows: 2
329
340
  }),
330
- /* @__PURE__ */ y(o, {
341
+ /* @__PURE__ */ y(i, {
331
342
  label: S("devportal.changelog", "Changelog"),
332
343
  value: k.changelog,
333
344
  onChange: (e) => A({
@@ -340,7 +351,7 @@ var x = () => {
340
351
  ]
341
352
  })
342
353
  }),
343
- /* @__PURE__ */ y(s, {
354
+ /* @__PURE__ */ y(a, {
344
355
  open: L === "publish",
345
356
  onOpenChange: (e) => {
346
357
  e || R(null);
@@ -357,7 +368,7 @@ var x = () => {
357
368
  cancelLabel: S("devportal.cancel", "Cancel"),
358
369
  variant: "success"
359
370
  }),
360
- /* @__PURE__ */ y(s, {
371
+ /* @__PURE__ */ y(a, {
361
372
  open: L === "deprecate",
362
373
  onOpenChange: (e) => {
363
374
  e || R(null);
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationVersionsPage.js","names":[],"sources":["../../../src/devportal/pages/ApplicationVersionsPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useMemo, useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Plus,\n Package,\n CheckCircle,\n Clock,\n Archive,\n Loader2,\n AlertCircle,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { InputField, TextareaField } from \"../components/FormField\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { SelectionCheckbox } from \"../components/SelectionCheckbox\";\nimport { format } from \"date-fns\";\nimport {\n useGetApplicationVersionsQuery,\n useCreateApplicationVersionMutation,\n usePublishApplicationVersionMutation,\n useDeprecateApplicationVersionMutation,\n type ApplicationVersionFieldsFragment,\n} from \"../hooks\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\n\nexport const ApplicationVersionsPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { allowed: canCreate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_CREATE,\n );\n const { allowed: canPublish } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_PUBLISH,\n );\n const { allowed: canDeprecate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_DEPRECATE,\n );\n const { appId } = useParams<{ appId: string }>();\n const [showCreateDialog, setShowCreateDialog] = useState(false);\n const [newVersion, setNewVersion] = useState({\n version: \"\",\n releaseNotes: \"\",\n changelog: \"\",\n });\n const [actionError, setActionError] = useState<string | null>(null);\n const [actionSuccess, setActionSuccess] = useState<string | null>(null);\n const [selectedVersionIds, setSelectedVersionIds] = useState<Set<string>>(\n new Set(),\n );\n const [bulkAction, setBulkAction] = useState<\"publish\" | \"deprecate\" | null>(\n null,\n );\n\n // Query\n const {\n data: versionsData,\n loading: versionsLoading,\n error: versionsError,\n } = useGetApplicationVersionsQuery({\n variables: { applicationId: appId! },\n skip: !appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n // Mutations\n const [createVersion, { loading: creating }] =\n useCreateApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [publishVersion] = usePublishApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [deprecateVersion] = useDeprecateApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const versions: ApplicationVersionFieldsFragment[] = useMemo(\n () =>\n versionsData?.application?.versions?.edges?.map(\n (edge: { node: ApplicationVersionFieldsFragment }) => edge.node,\n ) ?? [],\n [versionsData?.application?.versions],\n );\n\n const selectedVersions = useMemo(\n () => versions.filter((version) => selectedVersionIds.has(version.id)),\n [versions, selectedVersionIds],\n );\n const publishableVersions = useMemo(\n () => selectedVersions.filter((version) => version.status === \"DRAFT\"),\n [selectedVersions],\n );\n const deprecatableVersions = useMemo(\n () =>\n selectedVersions.filter(\n (version) => version.status === \"PUBLISHED\" && !version.isLatest,\n ),\n [selectedVersions],\n );\n const allVisibleSelected =\n versions.length > 0 &&\n versions.every((version) => selectedVersionIds.has(version.id));\n const partiallySelected =\n versions.some((version) => selectedVersionIds.has(version.id)) &&\n !allVisibleSelected;\n\n const handleCreate = async () => {\n if (!appId || !newVersion.version.trim()) return;\n setActionError(null);\n\n try {\n await createVersion({\n variables: {\n applicationId: appId,\n input: {\n version: newVersion.version,\n releaseNotes: newVersion.releaseNotes || undefined,\n changelog: newVersion.changelog || undefined,\n },\n },\n });\n setShowCreateDialog(false);\n setNewVersion({ version: \"\", releaseNotes: \"\", changelog: \"\" });\n setActionSuccess(\n tr(\"devportal.versionCreatedSuccess\", \"Version created successfully\"),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionCreateFailed\", \"Failed to create version\"),\n );\n }\n };\n\n const handlePublish = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await publishVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionPublishedSuccess\",\n \"Version published successfully\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionPublishFailed\", \"Failed to publish version\"),\n );\n }\n };\n\n const handleDeprecate = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await deprecateVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionDeprecatedSuccess\",\n \"Version deprecated successfully\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.versionDeprecateFailed\",\n \"Failed to deprecate version\",\n ),\n );\n }\n };\n\n const getStatusIcon = (version: ApplicationVersionFieldsFragment) => {\n if (version.status === \"PUBLISHED\" && version.isLatest) {\n return <CheckCircle size={16} className=\"text-status-success-text\" />;\n }\n if (version.status === \"DRAFT\") {\n return <Clock size={16} className=\"text-status-warning-text\" />;\n }\n if (version.status === \"DEPRECATED\") {\n return <Archive size={16} className=\"text-text-secondary\" />;\n }\n return <Package size={16} className=\"text-accent-blue\" />;\n };\n\n const toggleVersionSelection = (versionId: string, checked: boolean) => {\n setSelectedVersionIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(versionId);\n } else {\n next.delete(versionId);\n }\n return next;\n });\n };\n\n const toggleAllVisibleVersions = (checked: boolean) => {\n setSelectedVersionIds((prev) => {\n const next = new Set(prev);\n versions.forEach((version) => {\n if (checked) {\n next.add(version.id);\n } else {\n next.delete(version.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedVersionIds(new Set());\n\n const handleBulkPublish = async () => {\n if (publishableVersions.length === 0) return;\n setActionError(null);\n const results = await Promise.allSettled(\n publishableVersions.map((version) =>\n publishVersion({ variables: { id: version.id } }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setActionSuccess(\n `Published ${succeeded} version${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n clearSelection();\n setBulkAction(null);\n };\n\n const handleBulkDeprecate = async () => {\n if (deprecatableVersions.length === 0) return;\n setActionError(null);\n const results = await Promise.allSettled(\n deprecatableVersions.map((version) =>\n deprecateVersion({ variables: { id: version.id } }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setActionSuccess(\n `Deprecated ${succeeded} version${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n clearSelection();\n setBulkAction(null);\n };\n\n // Loading state\n if (versionsLoading && !versionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <div className=\"flex items-center justify-center py-16\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n </PageLayout>\n );\n }\n\n // Error state\n if (versionsError && !versionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.failedToLoadVersions\",\n \"Failed to load versions\",\n )}\n description={\n versionsError.message ||\n tr(\n \"devportal.versionsLoadError\",\n \"An error occurred while loading versions\",\n )\n }\n />\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_READ}>\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n actions={\n <button\n onClick={() => setShowCreateDialog(true)}\n disabled={!canCreate}\n aria-disabled={!canCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n <Plus size={16} />\n {tr(\"devportal.newVersion\", \"New Version\")}\n </button>\n }\n >\n {/* Action Error Banner */}\n {actionError && (\n <div className=\"rounded-lg border border-border-subtle bg-status-error-bg-subtle p-3 flex items-center gap-2 text-sm text-status-error-text\">\n <AlertCircle size={16} className=\"flex-shrink-0\" />\n {actionError}\n <button\n onClick={() => setActionError(null)}\n className=\"ml-auto text-status-error-text hover:text-status-error-text\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {actionSuccess && (\n <div className=\"rounded-lg border border-border-subtle bg-status-success-bg-subtle p-3 flex items-center gap-2 text-sm text-status-success-text\">\n <CheckCircle size={16} className=\"flex-shrink-0\" />\n {actionSuccess}\n <button\n onClick={() => setActionSuccess(null)}\n className=\"ml-auto text-status-success-text hover:text-status-success-text\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {versions.length > 0 && (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVersions}\n ariaLabel=\"Select all visible versions\"\n />\n <span className=\"text-sm font-medium\">\n {tr(\"devportal.selectAllVisible\", \"Select all visible\")}\n </span>\n </div>\n {selectedVersionIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\n >\n {tr(\"devportal.clearSelection\", \"Clear selection\")}\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-muted px-3 py-1 text-sm font-medium text-muted-foreground\">\n {selectedVersionIds.size} selected\n </span>\n <button\n onClick={() => setBulkAction(\"publish\")}\n disabled={publishableVersions.length === 0 || !canPublish}\n aria-disabled={!canPublish}\n className=\"rounded-md border border-status-success-bg px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr(\"devportal.publishSelected\", \"Publish Selected\")}\n {publishableVersions.length > 0\n ? ` (${publishableVersions.length})`\n : \"\"}\n </button>\n <button\n onClick={() => setBulkAction(\"deprecate\")}\n disabled={deprecatableVersions.length === 0 || !canDeprecate}\n aria-disabled={!canDeprecate}\n className=\"rounded-md border border-status-warning-bg px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr(\"devportal.deprecateSelected\", \"Deprecate Selected\")}\n {deprecatableVersions.length > 0\n ? ` (${deprecatableVersions.length})`\n : \"\"}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {versions.length > 0 ? (\n <DataTable\n data={versions}\n keyField=\"id\"\n columns={[\n {\n key: \"select\",\n header: (\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVersions}\n ariaLabel=\"Select all visible versions\"\n />\n ),\n className: \"w-12\",\n render: (v) => (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selectedVersionIds.has(v.id)}\n onChange={(checked) =>\n toggleVersionSelection(v.id, checked)\n }\n ariaLabel={`Select version ${v.version}`}\n />\n </div>\n ),\n },\n {\n key: \"version\",\n header: tr(\"devportal.version\", \"Version\"),\n render: (v) => (\n <div className=\"flex items-center gap-2\">\n {getStatusIcon(v)}\n <span className=\"font-mono font-medium\">{v.version}</span>\n {v.isLatest && (\n <span className=\"rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-xs text-status-success-text\">\n {tr(\"devportal.latest\", \"Latest\")}\n </span>\n )}\n {!v.isStable && (\n <span className=\"rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs text-status-warning-text\">\n {tr(\"devportal.beta\", \"Beta\")}\n </span>\n )}\n </div>\n ),\n },\n {\n key: \"releaseNotes\",\n header: tr(\"devportal.releaseNotes\", \"Release Notes\"),\n render: (v) => (\n <p className=\"max-w-md truncate text-sm text-muted-foreground\">\n {v.releaseNotes ||\n tr(\"devportal.noReleaseNotes\", \"No release notes\")}\n </p>\n ),\n },\n {\n key: \"status\",\n header: tr(\"devportal.status\", \"Status\"),\n render: (v) => <StatusBadge status={v.status} />,\n },\n {\n key: \"publishedAt\",\n header: tr(\"devportal.published\", \"Published\"),\n render: (v) =>\n v.publishedAt\n ? format(new Date(v.publishedAt), \"MMM d, yyyy\")\n : \"-\",\n },\n {\n key: \"actions\",\n header: \"\",\n className: \"w-32\",\n render: (v) => (\n <div className=\"flex gap-2\">\n {v.status === \"DRAFT\" && (\n <button\n className=\"text-xs text-primary hover:underline disabled:opacity-50\"\n disabled={!canPublish}\n aria-disabled={!canPublish}\n onClick={() => handlePublish(v.id)}\n >\n {tr(\"devportal.publish\", \"Publish\")}\n </button>\n )}\n {v.status === \"PUBLISHED\" && !v.isLatest && (\n <button\n className=\"text-xs text-status-warning-text hover:underline disabled:opacity-50\"\n disabled={!canDeprecate}\n aria-disabled={!canDeprecate}\n onClick={() => handleDeprecate(v.id)}\n >\n {tr(\"devportal.deprecate\", \"Deprecate\")}\n </button>\n )}\n </div>\n ),\n },\n ]}\n />\n ) : (\n <EmptyState\n icon={<Package size={32} />}\n title={tr(\"devportal.noVersionsYet\", \"No versions yet\")}\n description={tr(\n \"devportal.noVersionsYetDescription\",\n \"Create your first version to start distributing your app\",\n )}\n action={\n <button\n onClick={() => setShowCreateDialog(true)}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createVersion\", \"Create Version\")}\n </button>\n }\n />\n )}\n\n {/* Create Version Dialog */}\n {showCreateDialog && (\n <ConfirmDialog\n open={showCreateDialog}\n onOpenChange={setShowCreateDialog}\n onConfirm={handleCreate}\n title={tr(\"devportal.createNewVersion\", \"Create New Version\")}\n confirmLabel={\n creating\n ? tr(\"devportal.creating\", \"Creating...\")\n : tr(\"devportal.createVersion\", \"Create Version\")\n }\n >\n <div className=\"space-y-4\">\n <InputField\n label={tr(\"devportal.versionNumber\", \"Version Number\")}\n value={newVersion.version}\n onChange={(e) =>\n setNewVersion({ ...newVersion, version: e.target.value })\n }\n placeholder=\"e.g., 2.2.0\"\n hint={tr(\n \"devportal.versionHint\",\n \"Use semantic versioning (major.minor.patch)\",\n )}\n required\n />\n <TextareaField\n label={tr(\"devportal.releaseNotes\", \"Release Notes\")}\n value={newVersion.releaseNotes}\n onChange={(e) =>\n setNewVersion({ ...newVersion, releaseNotes: e.target.value })\n }\n placeholder={tr(\n \"devportal.releaseNotesPlaceholder\",\n \"Brief description of this release\",\n )}\n rows={2}\n />\n <TextareaField\n label={tr(\"devportal.changelog\", \"Changelog\")}\n value={newVersion.changelog}\n onChange={(e) =>\n setNewVersion({ ...newVersion, changelog: e.target.value })\n }\n placeholder=\"- Added feature X&#10;- Fixed bug Y&#10;- Improved performance\"\n rows={4}\n />\n </div>\n </ConfirmDialog>\n )}\n\n <ConfirmDialog\n open={bulkAction === \"publish\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={handleBulkPublish}\n title={tr(\n \"devportal.publishSelectedVersions\",\n \"Publish selected versions\",\n )}\n description={`${tr(\"devportal.publish\", \"Publish\")} ${publishableVersions.length} selected version${publishableVersions.length === 1 ? \"\" : \"s\"}?`}\n confirmLabel={tr(\"devportal.publish\", \"Publish\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"success\"\n />\n\n <ConfirmDialog\n open={bulkAction === \"deprecate\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={handleBulkDeprecate}\n title={tr(\n \"devportal.deprecateSelectedVersions\",\n \"Deprecate selected versions\",\n )}\n description={`${tr(\"devportal.deprecate\", \"Deprecate\")} ${deprecatableVersions.length} selected version${deprecatableVersions.length === 1 ? \"\" : \"s\"}? ${tr(\"devportal.deprecateVersionsWarning\", \"Deprecated versions remain visible but should no longer be promoted.\")}`}\n confirmLabel={tr(\"devportal.deprecate\", \"Deprecate\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"warning\"\n />\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ApplicationVersionsPage.js","names":[],"sources":["../../../src/devportal/pages/ApplicationVersionsPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n Plus,\n Package,\n CheckCircle,\n Clock,\n Archive,\n Loader2,\n AlertCircle,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { DataTable } from \"../components/DataTable\";\nimport { StatusBadge } from \"../components/StatusBadge\";\nimport { EmptyState } from \"../components/EmptyState\";\nimport { InputField, TextareaField } from \"../components/FormField\";\nimport { ConfirmDialog } from \"../components/ConfirmDialog\";\nimport { SelectionCheckbox } from \"../components/SelectionCheckbox\";\nimport { format } from \"date-fns\";\nimport {\n useGetApplicationQuery,\n useGetApplicationVersionsQuery,\n useCreateApplicationVersionMutation,\n usePublishApplicationVersionMutation,\n useDeprecateApplicationVersionMutation,\n type ApplicationVersionFieldsFragment,\n} from \"../hooks\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useDevportalPermission } from \"../hooks/usePermission\";\n\nexport const ApplicationVersionsPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { allowed: canCreate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_CREATE,\n );\n const { allowed: canPublish } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_PUBLISH,\n );\n const { allowed: canDeprecate } = useDevportalPermission(\n DEVPORTAL_PERMISSIONS.APP_VERSION_DEPRECATE,\n );\n const { appId } = useParams<{ appId: string }>();\n const [showCreateDialog, setShowCreateDialog] = useState(false);\n const [newVersion, setNewVersion] = useState({\n version: \"\",\n releaseNotes: \"\",\n changelog: \"\",\n });\n const [actionError, setActionError] = useState<string | null>(null);\n const [actionSuccess, setActionSuccess] = useState<string | null>(null);\n const [selectedVersionIds, setSelectedVersionIds] = useState<Set<string>>(\n new Set(),\n );\n const [bulkAction, setBulkAction] = useState<\"publish\" | \"deprecate\" | null>(\n null,\n );\n\n const { data: appData } = useGetApplicationQuery({\n variables: { id: appId ?? \"\" },\n skip: !appId,\n fetchPolicy: \"cache-first\",\n });\n useEffect(() => {\n const name = appData?.application?.name;\n if (!appId || !name) return;\n const reg = (\n window as unknown as {\n __breadcrumbRegistry?: {\n register: (id: string, name: string) => void;\n unregister: (id: string) => void;\n };\n }\n ).__breadcrumbRegistry;\n reg?.register(appId, name);\n return () => reg?.unregister(appId);\n }, [appId, appData?.application?.name]);\n\n // Query\n const {\n data: versionsData,\n loading: versionsLoading,\n error: versionsError,\n } = useGetApplicationVersionsQuery({\n variables: { applicationId: appId! },\n skip: !appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n // Mutations\n const [createVersion, { loading: creating }] =\n useCreateApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [publishVersion] = usePublishApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const [deprecateVersion] = useDeprecateApplicationVersionMutation({\n refetchQueries: [\"GetApplicationVersions\"],\n });\n\n const versions: ApplicationVersionFieldsFragment[] = useMemo(\n () =>\n versionsData?.application?.versions?.edges?.map(\n (edge: { node: ApplicationVersionFieldsFragment }) => edge.node,\n ) ?? [],\n [versionsData?.application?.versions],\n );\n\n const selectedVersions = useMemo(\n () => versions.filter((version) => selectedVersionIds.has(version.id)),\n [versions, selectedVersionIds],\n );\n const publishableVersions = useMemo(\n () => selectedVersions.filter((version) => version.status === \"DRAFT\"),\n [selectedVersions],\n );\n const deprecatableVersions = useMemo(\n () =>\n selectedVersions.filter(\n (version) => version.status === \"PUBLISHED\" && !version.isLatest,\n ),\n [selectedVersions],\n );\n const allVisibleSelected =\n versions.length > 0 &&\n versions.every((version) => selectedVersionIds.has(version.id));\n const partiallySelected =\n versions.some((version) => selectedVersionIds.has(version.id)) &&\n !allVisibleSelected;\n\n const handleCreate = async () => {\n if (!appId || !newVersion.version.trim()) return;\n setActionError(null);\n\n try {\n await createVersion({\n variables: {\n applicationId: appId,\n input: {\n version: newVersion.version,\n releaseNotes: newVersion.releaseNotes || undefined,\n changelog: newVersion.changelog || undefined,\n },\n },\n });\n setShowCreateDialog(false);\n setNewVersion({ version: \"\", releaseNotes: \"\", changelog: \"\" });\n setActionSuccess(\n tr(\"devportal.versionCreatedSuccess\", \"Version created successfully\"),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionCreateFailed\", \"Failed to create version\"),\n );\n }\n };\n\n const handlePublish = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await publishVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionPublishedSuccess\",\n \"Version published successfully\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\"devportal.versionPublishFailed\", \"Failed to publish version\"),\n );\n }\n };\n\n const handleDeprecate = async (versionId: string) => {\n setActionError(null);\n setActionSuccess(null);\n try {\n await deprecateVersion({ variables: { id: versionId } });\n setActionSuccess(\n tr(\n \"devportal.versionDeprecatedSuccess\",\n \"Version deprecated successfully\",\n ),\n );\n } catch (err) {\n setActionError(\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.versionDeprecateFailed\",\n \"Failed to deprecate version\",\n ),\n );\n }\n };\n\n const getStatusIcon = (version: ApplicationVersionFieldsFragment) => {\n if (version.status === \"PUBLISHED\" && version.isLatest) {\n return <CheckCircle size={16} className=\"text-status-success-text\" />;\n }\n if (version.status === \"DRAFT\") {\n return <Clock size={16} className=\"text-status-warning-text\" />;\n }\n if (version.status === \"DEPRECATED\") {\n return <Archive size={16} className=\"text-text-secondary\" />;\n }\n return <Package size={16} className=\"text-accent-blue\" />;\n };\n\n const toggleVersionSelection = (versionId: string, checked: boolean) => {\n setSelectedVersionIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(versionId);\n } else {\n next.delete(versionId);\n }\n return next;\n });\n };\n\n const toggleAllVisibleVersions = (checked: boolean) => {\n setSelectedVersionIds((prev) => {\n const next = new Set(prev);\n versions.forEach((version) => {\n if (checked) {\n next.add(version.id);\n } else {\n next.delete(version.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedVersionIds(new Set());\n\n const handleBulkPublish = async () => {\n if (publishableVersions.length === 0) return;\n setActionError(null);\n const results = await Promise.allSettled(\n publishableVersions.map((version) =>\n publishVersion({ variables: { id: version.id } }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setActionSuccess(\n `Published ${succeeded} version${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n clearSelection();\n setBulkAction(null);\n };\n\n const handleBulkDeprecate = async () => {\n if (deprecatableVersions.length === 0) return;\n setActionError(null);\n const results = await Promise.allSettled(\n deprecatableVersions.map((version) =>\n deprecateVersion({ variables: { id: version.id } }),\n ),\n );\n const succeeded = results.filter(\n (result: PromiseSettledResult<unknown>) => result.status === \"fulfilled\",\n ).length;\n const failed = results.length - succeeded;\n setActionSuccess(\n `Deprecated ${succeeded} version${succeeded === 1 ? \"\" : \"s\"}${failed > 0 ? ` • ${failed} failed` : \"\"}`,\n );\n clearSelection();\n setBulkAction(null);\n };\n\n // Loading state\n if (versionsLoading && !versionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <div className=\"flex items-center justify-center py-16\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n </PageLayout>\n );\n }\n\n // Error state\n if (versionsError && !versionsData) {\n return (\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n >\n <EmptyState\n icon={<AlertCircle size={32} className=\"text-status-error-text\" />}\n title={tr(\n \"devportal.failedToLoadVersions\",\n \"Failed to load versions\",\n )}\n description={\n versionsError.message ||\n tr(\n \"devportal.versionsLoadError\",\n \"An error occurred while loading versions\",\n )\n }\n />\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_READ}>\n <PageLayout\n title={tr(\"devportal.versionManagement\", \"Version Management\")}\n description={tr(\n \"devportal.versionManagementDescription\",\n \"Manage your application versions\",\n )}\n backLink={`/apps/${appId}`}\n backLabel={tr(\"devportal.backToApplication\", \"Back to Application\")}\n actions={\n <button\n onClick={() => setShowCreateDialog(true)}\n disabled={!canCreate}\n aria-disabled={!canCreate}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n <Plus size={16} />\n {tr(\"devportal.newVersion\", \"New Version\")}\n </button>\n }\n >\n {/* Action Error Banner */}\n {actionError && (\n <div className=\"rounded-lg border border-border-subtle bg-status-error-bg-subtle p-3 flex items-center gap-2 text-sm text-status-error-text\">\n <AlertCircle size={16} className=\"flex-shrink-0\" />\n {actionError}\n <button\n onClick={() => setActionError(null)}\n className=\"ml-auto text-status-error-text hover:text-status-error-text\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {actionSuccess && (\n <div className=\"rounded-lg border border-border-subtle bg-status-success-bg-subtle p-3 flex items-center gap-2 text-sm text-status-success-text\">\n <CheckCircle size={16} className=\"flex-shrink-0\" />\n {actionSuccess}\n <button\n onClick={() => setActionSuccess(null)}\n className=\"ml-auto text-status-success-text hover:text-status-success-text\"\n >\n {tr(\"devportal.dismiss\", \"Dismiss\")}\n </button>\n </div>\n )}\n\n {versions.length > 0 && (\n <div className=\"rounded-lg border bg-card p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVersions}\n ariaLabel=\"Select all visible versions\"\n />\n <span className=\"text-sm font-medium\">\n {tr(\"devportal.selectAllVisible\", \"Select all visible\")}\n </span>\n </div>\n {selectedVersionIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-muted-foreground hover:text-foreground\"\n >\n {tr(\"devportal.clearSelection\", \"Clear selection\")}\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-muted px-3 py-1 text-sm font-medium text-muted-foreground\">\n {selectedVersionIds.size} selected\n </span>\n <button\n onClick={() => setBulkAction(\"publish\")}\n disabled={publishableVersions.length === 0 || !canPublish}\n aria-disabled={!canPublish}\n className=\"rounded-md border border-status-success-bg px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr(\"devportal.publishSelected\", \"Publish Selected\")}\n {publishableVersions.length > 0\n ? ` (${publishableVersions.length})`\n : \"\"}\n </button>\n <button\n onClick={() => setBulkAction(\"deprecate\")}\n disabled={deprecatableVersions.length === 0 || !canDeprecate}\n aria-disabled={!canDeprecate}\n className=\"rounded-md border border-status-warning-bg px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle disabled:cursor-not-allowed disabled:opacity-50\"\n >\n {tr(\"devportal.deprecateSelected\", \"Deprecate Selected\")}\n {deprecatableVersions.length > 0\n ? ` (${deprecatableVersions.length})`\n : \"\"}\n </button>\n </div>\n </div>\n </div>\n )}\n\n {versions.length > 0 ? (\n <DataTable\n data={versions}\n keyField=\"id\"\n columns={[\n {\n key: \"select\",\n header: (\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVersions}\n ariaLabel=\"Select all visible versions\"\n />\n ),\n className: \"w-12\",\n render: (v) => (\n <div onClick={(event) => event.stopPropagation()}>\n <SelectionCheckbox\n checked={selectedVersionIds.has(v.id)}\n onChange={(checked) =>\n toggleVersionSelection(v.id, checked)\n }\n ariaLabel={`Select version ${v.version}`}\n />\n </div>\n ),\n },\n {\n key: \"version\",\n header: tr(\"devportal.version\", \"Version\"),\n render: (v) => (\n <div className=\"flex items-center gap-2\">\n {getStatusIcon(v)}\n <span className=\"font-mono font-medium\">{v.version}</span>\n {v.isLatest && (\n <span className=\"rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-xs text-status-success-text\">\n {tr(\"devportal.latest\", \"Latest\")}\n </span>\n )}\n {!v.isStable && (\n <span className=\"rounded-full bg-status-warning-bg-subtle px-2 py-0.5 text-xs text-status-warning-text\">\n {tr(\"devportal.beta\", \"Beta\")}\n </span>\n )}\n </div>\n ),\n },\n {\n key: \"releaseNotes\",\n header: tr(\"devportal.releaseNotes\", \"Release Notes\"),\n render: (v) => (\n <p className=\"max-w-md truncate text-sm text-muted-foreground\">\n {v.releaseNotes ||\n tr(\"devportal.noReleaseNotes\", \"No release notes\")}\n </p>\n ),\n },\n {\n key: \"status\",\n header: tr(\"devportal.status\", \"Status\"),\n render: (v) => <StatusBadge status={v.status} />,\n },\n {\n key: \"publishedAt\",\n header: tr(\"devportal.published\", \"Published\"),\n render: (v) =>\n v.publishedAt\n ? format(new Date(v.publishedAt), \"MMM d, yyyy\")\n : \"-\",\n },\n {\n key: \"actions\",\n header: \"\",\n className: \"w-32\",\n render: (v) => (\n <div className=\"flex gap-2\">\n {v.status === \"DRAFT\" && (\n <button\n className=\"text-xs text-primary hover:underline disabled:opacity-50\"\n disabled={!canPublish}\n aria-disabled={!canPublish}\n onClick={() => handlePublish(v.id)}\n >\n {tr(\"devportal.publish\", \"Publish\")}\n </button>\n )}\n {v.status === \"PUBLISHED\" && !v.isLatest && (\n <button\n className=\"text-xs text-status-warning-text hover:underline disabled:opacity-50\"\n disabled={!canDeprecate}\n aria-disabled={!canDeprecate}\n onClick={() => handleDeprecate(v.id)}\n >\n {tr(\"devportal.deprecate\", \"Deprecate\")}\n </button>\n )}\n </div>\n ),\n },\n ]}\n />\n ) : (\n <EmptyState\n icon={<Package size={32} />}\n title={tr(\"devportal.noVersionsYet\", \"No versions yet\")}\n description={tr(\n \"devportal.noVersionsYetDescription\",\n \"Create your first version to start distributing your app\",\n )}\n action={\n <button\n onClick={() => setShowCreateDialog(true)}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n <Plus size={16} />\n {tr(\"devportal.createVersion\", \"Create Version\")}\n </button>\n }\n />\n )}\n\n {/* Create Version Dialog */}\n {showCreateDialog && (\n <ConfirmDialog\n open={showCreateDialog}\n onOpenChange={setShowCreateDialog}\n onConfirm={handleCreate}\n title={tr(\"devportal.createNewVersion\", \"Create New Version\")}\n confirmLabel={\n creating\n ? tr(\"devportal.creating\", \"Creating...\")\n : tr(\"devportal.createVersion\", \"Create Version\")\n }\n >\n <div className=\"space-y-4\">\n <InputField\n label={tr(\"devportal.versionNumber\", \"Version Number\")}\n value={newVersion.version}\n onChange={(e) =>\n setNewVersion({ ...newVersion, version: e.target.value })\n }\n placeholder=\"e.g., 2.2.0\"\n hint={tr(\n \"devportal.versionHint\",\n \"Use semantic versioning (major.minor.patch)\",\n )}\n required\n />\n <TextareaField\n label={tr(\"devportal.releaseNotes\", \"Release Notes\")}\n value={newVersion.releaseNotes}\n onChange={(e) =>\n setNewVersion({ ...newVersion, releaseNotes: e.target.value })\n }\n placeholder={tr(\n \"devportal.releaseNotesPlaceholder\",\n \"Brief description of this release\",\n )}\n rows={2}\n />\n <TextareaField\n label={tr(\"devportal.changelog\", \"Changelog\")}\n value={newVersion.changelog}\n onChange={(e) =>\n setNewVersion({ ...newVersion, changelog: e.target.value })\n }\n placeholder=\"- Added feature X&#10;- Fixed bug Y&#10;- Improved performance\"\n rows={4}\n />\n </div>\n </ConfirmDialog>\n )}\n\n <ConfirmDialog\n open={bulkAction === \"publish\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={handleBulkPublish}\n title={tr(\n \"devportal.publishSelectedVersions\",\n \"Publish selected versions\",\n )}\n description={`${tr(\"devportal.publish\", \"Publish\")} ${publishableVersions.length} selected version${publishableVersions.length === 1 ? \"\" : \"s\"}?`}\n confirmLabel={tr(\"devportal.publish\", \"Publish\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"success\"\n />\n\n <ConfirmDialog\n open={bulkAction === \"deprecate\"}\n onOpenChange={(open) => {\n if (!open) setBulkAction(null);\n }}\n onConfirm={handleBulkDeprecate}\n title={tr(\n \"devportal.deprecateSelectedVersions\",\n \"Deprecate selected versions\",\n )}\n description={`${tr(\"devportal.deprecate\", \"Deprecate\")} ${deprecatableVersions.length} selected version${deprecatableVersions.length === 1 ? \"\" : \"s\"}? ${tr(\"devportal.deprecateVersionsWarning\", \"Deprecated versions remain visible but should no longer be promoted.\")}`}\n confirmLabel={tr(\"devportal.deprecate\", \"Deprecate\")}\n cancelLabel={tr(\"devportal.cancel\", \"Cancel\")}\n variant=\"warning\"\n />\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
@@ -81,32 +81,7 @@ var w = () => {
81
81
  }
82
82
  }, K = () => {
83
83
  V("/api-keys");
84
- }, q = [
85
- {
86
- value: "30days",
87
- label: "30 days"
88
- },
89
- {
90
- value: "90days",
91
- label: "90 days"
92
- },
93
- {
94
- value: "180days",
95
- label: "180 days"
96
- },
97
- {
98
- value: "1year",
99
- label: "1 year"
100
- },
101
- {
102
- value: "2years",
103
- label: "2 years"
104
- },
105
- {
106
- value: "never",
107
- label: "Never expires"
108
- }
109
- ];
84
+ };
110
85
  return D ? /* @__PURE__ */ y(n, {
111
86
  title: "API Key Generated",
112
87
  description: "Your new API key has been generated successfully",
@@ -210,13 +185,11 @@ var w = () => {
210
185
  label: "App",
211
186
  value: z.appId,
212
187
  onChange: (e) => U("appId", e.target.value),
213
- options: [{
214
- value: "",
215
- label: "Select an app..."
216
- }, ...R.map((e) => ({
188
+ options: R.map((e) => ({
217
189
  value: e.id,
218
190
  label: `${e.name} (${e.clientId})`
219
- }))],
191
+ })),
192
+ placeholder: "Select an app…",
220
193
  error: k.appId,
221
194
  required: !0,
222
195
  hint: "The OAuth app this API key will be associated with"
@@ -256,7 +229,32 @@ var w = () => {
256
229
  label: "Expires In",
257
230
  value: z.expiresIn,
258
231
  onChange: (e) => U("expiresIn", e.target.value),
259
- options: q,
232
+ options: [
233
+ {
234
+ value: "30days",
235
+ label: "30 days"
236
+ },
237
+ {
238
+ value: "90days",
239
+ label: "90 days"
240
+ },
241
+ {
242
+ value: "180days",
243
+ label: "180 days"
244
+ },
245
+ {
246
+ value: "1year",
247
+ label: "1 year"
248
+ },
249
+ {
250
+ value: "2years",
251
+ label: "2 years"
252
+ },
253
+ {
254
+ value: "never",
255
+ label: "Never expires"
256
+ }
257
+ ],
260
258
  hint: "Keys can be revoked at any time regardless of expiration"
261
259
  }), z.expiresIn === "never" && /* @__PURE__ */ y(c, {
262
260
  variant: "warning",
@@ -1 +1 @@
1
- {"version":3,"file":"CreateApiKeyPage.js","names":[],"sources":["../../../src/devportal/pages/CreateApiKeyPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport { Save, X, Loader2 } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { OptionalFields } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { InputField, SelectField, TagInput } from \"../components/FormField\";\nimport { NewSecretAlert } from \"../components/SecretDisplay\";\nimport { Alert } from \"../components/ConfirmDialog\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useGenerateApiKeyMutation, useCurrentUserAppsQuery } from \"../hooks\";\nimport { useDevportalEventBus } from \"../events\";\nimport { executeRecaptcha } from \"../utils/recaptcha\";\n\ninterface CreateApiKeyFormData {\n appId: string;\n name: string;\n scopes: string[];\n expiresIn: string;\n}\n\nconst EXPIRATION_DURATIONS: Record<string, number | null> = {\n \"30days\": 30 * 24 * 60 * 60 * 1000,\n \"90days\": 90 * 24 * 60 * 60 * 1000,\n \"180days\": 180 * 24 * 60 * 60 * 1000,\n \"1year\": 365 * 24 * 60 * 60 * 1000,\n \"2years\": 2 * 365 * 24 * 60 * 60 * 1000,\n never: null,\n};\n\nfunction computeExpiresAt(expiresIn: string): string | undefined {\n const duration = EXPIRATION_DURATIONS[expiresIn];\n if (duration == null) return undefined;\n return new Date(Date.now() + duration).toISOString();\n}\n\n/**\n * Page for generating a new API key via auth-svc.\n *\n * Previously created API keys through devportal-svc per application;\n * now generates keys through auth-svc's generateAPIKey mutation.\n * The user selects which OAuth app to associate the key with.\n */\nexport const CreateApiKeyPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { navigate, basePath } = useDevportal();\n const [newApiKey, setNewApiKey] = useState<{\n apiKey: string;\n apiSecret: string;\n keyId: string;\n } | null>(null);\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [mutationError, setMutationError] = useState<string | null>(null);\n\n const [generateApiKey, { loading: isSubmitting }] =\n useGenerateApiKeyMutation();\n const bus = useDevportalEventBus();\n\n // Fetch user's apps to populate the app selector\n const { data: appsData, loading: appsLoading } = useCurrentUserAppsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const apps: Array<{ id: string; name: string; clientId: string }> =\n appsData?.currentUserApps?.apps.map(\n (app: { id: string; name: string; clientId: string }) => ({\n id: app.id,\n name: app.name,\n clientId: app.clientId,\n }),\n ) ?? [];\n\n const [formData, setFormData] = useState<CreateApiKeyFormData>({\n appId: \"\",\n name: \"\",\n scopes: [],\n expiresIn: \"1year\",\n });\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleCancel = () => {\n handleNavigate(\"/api-keys\");\n };\n\n const updateField = <K extends keyof CreateApiKeyFormData>(\n field: K,\n value: CreateApiKeyFormData[K],\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field]) {\n setErrors((prev) => ({ ...prev, [field]: \"\" }));\n }\n if (mutationError) {\n setMutationError(null);\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: Record<string, string> = {};\n\n if (!formData.appId) {\n newErrors.appId = tr(\n \"devportal.selectAppError\",\n \"Please select an app to associate this key with\",\n );\n }\n\n if (!formData.name.trim()) {\n newErrors.name = tr(\n \"devportal.apiKeyNameRequired\",\n \"API key name is required\",\n );\n }\n\n if (formData.scopes.length === 0) {\n newErrors.scopes = tr(\n \"devportal.atLeastOneScopeRequired\",\n \"At least one scope is required\",\n );\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n\n setMutationError(null);\n\n try {\n const expiresAt = computeExpiresAt(formData.expiresIn);\n const recaptchaToken = await executeRecaptcha(\"api_key_create\");\n const input = {\n appId: formData.appId,\n name: formData.name.trim(),\n scope: formData.scopes,\n expiresAt,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n };\n\n const { data } = await generateApiKey({\n variables: {\n input,\n },\n });\n\n if (data?.generateAPIKey) {\n bus.emit(\"devportal.apikey.generated\", {\n keyId: data.generateAPIKey.keyId,\n appId: formData.appId,\n });\n setNewApiKey({\n apiKey: data.generateAPIKey.apiKey,\n apiSecret: data.generateAPIKey.apiSecret,\n keyId: data.generateAPIKey.keyId,\n });\n }\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.failedToGenerateApiKey\",\n \"Failed to generate API key\",\n );\n setMutationError(message);\n }\n };\n\n const handleKeyDismiss = () => {\n handleNavigate(\"/api-keys\");\n };\n\n const expirationOptions = [\n { value: \"30days\", label: \"30 days\" },\n { value: \"90days\", label: \"90 days\" },\n { value: \"180days\", label: \"180 days\" },\n { value: \"1year\", label: \"1 year\" },\n { value: \"2years\", label: \"2 years\" },\n { value: \"never\", label: \"Never expires\" },\n ];\n\n if (newApiKey) {\n return (\n <PageLayout\n title=\"API Key Generated\"\n description=\"Your new API key has been generated successfully\"\n >\n <div className=\"max-w-2xl space-y-6\">\n <NewSecretAlert\n title=\"API Key & Secret\"\n secret={newApiKey.apiSecret}\n description=\"Store this secret securely — you won't be able to see it again.\"\n onDismiss={handleKeyDismiss}\n />\n\n <Alert variant=\"info\" title=\"Usage Instructions\">\n <div className=\"mt-2 space-y-2\">\n <p>Your API key details:</p>\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Key ID:\n </span>\n <code className=\"rounded bg-muted px-2 py-0.5 text-sm\">\n {newApiKey.keyId}\n </code>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n API Key:\n </span>\n <code className=\"rounded bg-muted px-2 py-0.5 text-sm\">\n {newApiKey.apiKey}\n </code>\n </div>\n </div>\n <p className=\"text-xs text-muted-foreground mt-2\">\n Use the API key and secret to authenticate your requests.\n </p>\n </div>\n </Alert>\n\n <div className=\"flex gap-3\">\n <button\n onClick={handleKeyDismiss}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n Continue to API Keys\n </button>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_UPDATE}>\n <PageLayout\n title=\"Generate API Key\"\n description=\"Generate a new API key for programmatic access\"\n backLink=\"/api-keys\"\n backLabel=\"Back to API Keys\"\n >\n <form onSubmit={handleSubmit} className=\"max-w-3xl space-y-8\">\n {/* Mutation error */}\n {mutationError && (\n <Alert\n variant=\"error\"\n title=\"Error Generating API Key\"\n onDismiss={() => setMutationError(null)}\n >\n {mutationError}\n </Alert>\n )}\n\n {/* Basic Information */}\n <PageSection\n title=\"Basic Information\"\n description=\"Configure the API key settings\"\n >\n <div className=\"space-y-4\">\n {/* App Selector */}\n {appsLoading ? (\n <div className=\"flex items-center gap-2 py-2\">\n <Loader2 className=\"h-4 w-4 animate-spin text-muted-foreground\" />\n <span className=\"text-sm text-muted-foreground\">\n Loading apps...\n </span>\n </div>\n ) : apps.length === 0 ? (\n <Alert variant=\"warning\" title=\"No Apps Found\">\n <p>\n You need to create an OAuth app first before generating API\n keys.{\" \"}\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/oauth/new\")}\n className=\"text-primary underline hover:no-underline\"\n >\n Create an app\n </button>\n </p>\n </Alert>\n ) : (\n <SelectField\n label=\"App\"\n value={formData.appId}\n onChange={(e) => updateField(\"appId\", e.target.value)}\n options={[\n { value: \"\", label: \"Select an app...\" },\n ...apps.map((app) => ({\n value: app.id,\n label: `${app.name} (${app.clientId})`,\n })),\n ]}\n error={errors.appId}\n required\n hint=\"The OAuth app this API key will be associated with\"\n />\n )}\n\n <InputField\n label=\"Key Name\"\n placeholder=\"e.g., Production Backend Key\"\n value={formData.name}\n onChange={(e) => updateField(\"name\", e.target.value)}\n error={errors.name}\n required\n hint=\"A descriptive name to identify this key\"\n />\n </div>\n </PageSection>\n\n {/* Scopes */}\n <PageSection\n title=\"Scopes\"\n description=\"Define what this API key can access\"\n >\n <div className=\"space-y-4\">\n <TagInput\n label=\"Scopes\"\n value={formData.scopes}\n onChange={(values) => updateField(\"scopes\", values)}\n placeholder=\"e.g., workspace:read, vibecontrols:write\"\n hint=\"Press Enter to add each scope. At least one scope is required.\"\n error={errors.scopes}\n />\n </div>\n </PageSection>\n\n {/* Advanced: rate-limit overrides / allowed origins / scope tuning / expiration — collapse the long-tail knobs */}\n <OptionalFields\n title=\"Advanced settings\"\n description=\"Rate-limit overrides, allowed origins, scope tuning, and expiration.\"\n >\n <PageSection\n title=\"Key Expiration\"\n description=\"Set when this key should expire\"\n >\n <SelectField\n label=\"Expires In\"\n value={formData.expiresIn}\n onChange={(e) => updateField(\"expiresIn\", e.target.value)}\n options={expirationOptions}\n hint=\"Keys can be revoked at any time regardless of expiration\"\n />\n\n {formData.expiresIn === \"never\" && (\n <Alert variant=\"warning\" className=\"mt-4\">\n Non-expiring keys are a security risk. Consider setting an\n expiration and rotating keys periodically.\n </Alert>\n )}\n </PageSection>\n </OptionalFields>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 border-t pt-6\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted transition-colors\"\n >\n <X size={16} />\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting || apps.length === 0}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n <Save size={16} />\n {isSubmitting ? \"Generating...\" : \"Generate API Key\"}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CreateApiKeyPage.js","names":[],"sources":["../../../src/devportal/pages/CreateApiKeyPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport { Save, X, Loader2 } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { OptionalFields } from \"@burdenoff/fe-libs/ui\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport { InputField, SelectField, TagInput } from \"../components/FormField\";\nimport { NewSecretAlert } from \"../components/SecretDisplay\";\nimport { Alert } from \"../components/ConfirmDialog\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { useGenerateApiKeyMutation, useCurrentUserAppsQuery } from \"../hooks\";\nimport { useDevportalEventBus } from \"../events\";\nimport { executeRecaptcha } from \"../utils/recaptcha\";\n\ninterface CreateApiKeyFormData {\n appId: string;\n name: string;\n scopes: string[];\n expiresIn: string;\n}\n\nconst EXPIRATION_DURATIONS: Record<string, number | null> = {\n \"30days\": 30 * 24 * 60 * 60 * 1000,\n \"90days\": 90 * 24 * 60 * 60 * 1000,\n \"180days\": 180 * 24 * 60 * 60 * 1000,\n \"1year\": 365 * 24 * 60 * 60 * 1000,\n \"2years\": 2 * 365 * 24 * 60 * 60 * 1000,\n never: null,\n};\n\nfunction computeExpiresAt(expiresIn: string): string | undefined {\n const duration = EXPIRATION_DURATIONS[expiresIn];\n if (duration == null) return undefined;\n return new Date(Date.now() + duration).toISOString();\n}\n\n/**\n * Page for generating a new API key via auth-svc.\n *\n * Previously created API keys through devportal-svc per application;\n * now generates keys through auth-svc's generateAPIKey mutation.\n * The user selects which OAuth app to associate the key with.\n */\nexport const CreateApiKeyPage: FC = () => {\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n const { navigate, basePath } = useDevportal();\n const [newApiKey, setNewApiKey] = useState<{\n apiKey: string;\n apiSecret: string;\n keyId: string;\n } | null>(null);\n const [errors, setErrors] = useState<Record<string, string>>({});\n const [mutationError, setMutationError] = useState<string | null>(null);\n\n const [generateApiKey, { loading: isSubmitting }] =\n useGenerateApiKeyMutation();\n const bus = useDevportalEventBus();\n\n // Fetch user's apps to populate the app selector\n const { data: appsData, loading: appsLoading } = useCurrentUserAppsQuery({\n fetchPolicy: \"cache-and-network\",\n });\n\n const apps: Array<{ id: string; name: string; clientId: string }> =\n appsData?.currentUserApps?.apps.map(\n (app: { id: string; name: string; clientId: string }) => ({\n id: app.id,\n name: app.name,\n clientId: app.clientId,\n }),\n ) ?? [];\n\n const [formData, setFormData] = useState<CreateApiKeyFormData>({\n appId: \"\",\n name: \"\",\n scopes: [],\n expiresIn: \"1year\",\n });\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleCancel = () => {\n handleNavigate(\"/api-keys\");\n };\n\n const updateField = <K extends keyof CreateApiKeyFormData>(\n field: K,\n value: CreateApiKeyFormData[K],\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n if (errors[field]) {\n setErrors((prev) => ({ ...prev, [field]: \"\" }));\n }\n if (mutationError) {\n setMutationError(null);\n }\n };\n\n const validateForm = (): boolean => {\n const newErrors: Record<string, string> = {};\n\n if (!formData.appId) {\n newErrors.appId = tr(\n \"devportal.selectAppError\",\n \"Please select an app to associate this key with\",\n );\n }\n\n if (!formData.name.trim()) {\n newErrors.name = tr(\n \"devportal.apiKeyNameRequired\",\n \"API key name is required\",\n );\n }\n\n if (formData.scopes.length === 0) {\n newErrors.scopes = tr(\n \"devportal.atLeastOneScopeRequired\",\n \"At least one scope is required\",\n );\n }\n\n setErrors(newErrors);\n return Object.keys(newErrors).length === 0;\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n\n if (!validateForm()) return;\n\n setMutationError(null);\n\n try {\n const expiresAt = computeExpiresAt(formData.expiresIn);\n const recaptchaToken = await executeRecaptcha(\"api_key_create\");\n const input = {\n appId: formData.appId,\n name: formData.name.trim(),\n scope: formData.scopes,\n expiresAt,\n ...(recaptchaToken ? { recaptchaToken } : {}),\n };\n\n const { data } = await generateApiKey({\n variables: {\n input,\n },\n });\n\n if (data?.generateAPIKey) {\n bus.emit(\"devportal.apikey.generated\", {\n keyId: data.generateAPIKey.keyId,\n appId: formData.appId,\n });\n setNewApiKey({\n apiKey: data.generateAPIKey.apiKey,\n apiSecret: data.generateAPIKey.apiSecret,\n keyId: data.generateAPIKey.keyId,\n });\n }\n } catch (err) {\n const message =\n err instanceof Error\n ? err.message\n : tr(\n \"devportal.failedToGenerateApiKey\",\n \"Failed to generate API key\",\n );\n setMutationError(message);\n }\n };\n\n const handleKeyDismiss = () => {\n handleNavigate(\"/api-keys\");\n };\n\n const expirationOptions = [\n { value: \"30days\", label: \"30 days\" },\n { value: \"90days\", label: \"90 days\" },\n { value: \"180days\", label: \"180 days\" },\n { value: \"1year\", label: \"1 year\" },\n { value: \"2years\", label: \"2 years\" },\n { value: \"never\", label: \"Never expires\" },\n ];\n\n if (newApiKey) {\n return (\n <PageLayout\n title=\"API Key Generated\"\n description=\"Your new API key has been generated successfully\"\n >\n <div className=\"max-w-2xl space-y-6\">\n <NewSecretAlert\n title=\"API Key & Secret\"\n secret={newApiKey.apiSecret}\n description=\"Store this secret securely — you won't be able to see it again.\"\n onDismiss={handleKeyDismiss}\n />\n\n <Alert variant=\"info\" title=\"Usage Instructions\">\n <div className=\"mt-2 space-y-2\">\n <p>Your API key details:</p>\n <div className=\"space-y-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n Key ID:\n </span>\n <code className=\"rounded bg-muted px-2 py-0.5 text-sm\">\n {newApiKey.keyId}\n </code>\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs font-medium text-muted-foreground\">\n API Key:\n </span>\n <code className=\"rounded bg-muted px-2 py-0.5 text-sm\">\n {newApiKey.apiKey}\n </code>\n </div>\n </div>\n <p className=\"text-xs text-muted-foreground mt-2\">\n Use the API key and secret to authenticate your requests.\n </p>\n </div>\n </Alert>\n\n <div className=\"flex gap-3\">\n <button\n onClick={handleKeyDismiss}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n Continue to API Keys\n </button>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_UPDATE}>\n <PageLayout\n title=\"Generate API Key\"\n description=\"Generate a new API key for programmatic access\"\n backLink=\"/api-keys\"\n backLabel=\"Back to API Keys\"\n >\n <form onSubmit={handleSubmit} className=\"max-w-3xl space-y-8\">\n {/* Mutation error */}\n {mutationError && (\n <Alert\n variant=\"error\"\n title=\"Error Generating API Key\"\n onDismiss={() => setMutationError(null)}\n >\n {mutationError}\n </Alert>\n )}\n\n {/* Basic Information */}\n <PageSection\n title=\"Basic Information\"\n description=\"Configure the API key settings\"\n >\n <div className=\"space-y-4\">\n {/* App Selector */}\n {appsLoading ? (\n <div className=\"flex items-center gap-2 py-2\">\n <Loader2 className=\"h-4 w-4 animate-spin text-muted-foreground\" />\n <span className=\"text-sm text-muted-foreground\">\n Loading apps...\n </span>\n </div>\n ) : apps.length === 0 ? (\n <Alert variant=\"warning\" title=\"No Apps Found\">\n <p>\n You need to create an OAuth app first before generating API\n keys.{\" \"}\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/oauth/new\")}\n className=\"text-primary underline hover:no-underline\"\n >\n Create an app\n </button>\n </p>\n </Alert>\n ) : (\n <SelectField\n label=\"App\"\n value={formData.appId}\n onChange={(e) => updateField(\"appId\", e.target.value)}\n options={apps.map((app) => ({\n value: app.id,\n label: `${app.name} (${app.clientId})`,\n }))}\n placeholder=\"Select an app…\"\n error={errors.appId}\n required\n hint=\"The OAuth app this API key will be associated with\"\n />\n )}\n\n <InputField\n label=\"Key Name\"\n placeholder=\"e.g., Production Backend Key\"\n value={formData.name}\n onChange={(e) => updateField(\"name\", e.target.value)}\n error={errors.name}\n required\n hint=\"A descriptive name to identify this key\"\n />\n </div>\n </PageSection>\n\n {/* Scopes */}\n <PageSection\n title=\"Scopes\"\n description=\"Define what this API key can access\"\n >\n <div className=\"space-y-4\">\n <TagInput\n label=\"Scopes\"\n value={formData.scopes}\n onChange={(values) => updateField(\"scopes\", values)}\n placeholder=\"e.g., workspace:read, vibecontrols:write\"\n hint=\"Press Enter to add each scope. At least one scope is required.\"\n error={errors.scopes}\n />\n </div>\n </PageSection>\n\n {/* Advanced: rate-limit overrides / allowed origins / scope tuning / expiration — collapse the long-tail knobs */}\n <OptionalFields\n title=\"Advanced settings\"\n description=\"Rate-limit overrides, allowed origins, scope tuning, and expiration.\"\n >\n <PageSection\n title=\"Key Expiration\"\n description=\"Set when this key should expire\"\n >\n <SelectField\n label=\"Expires In\"\n value={formData.expiresIn}\n onChange={(e) => updateField(\"expiresIn\", e.target.value)}\n options={expirationOptions}\n hint=\"Keys can be revoked at any time regardless of expiration\"\n />\n\n {formData.expiresIn === \"never\" && (\n <Alert variant=\"warning\" className=\"mt-4\">\n Non-expiring keys are a security risk. Consider setting an\n expiration and rotating keys periodically.\n </Alert>\n )}\n </PageSection>\n </OptionalFields>\n\n {/* Actions */}\n <div className=\"flex items-center justify-end gap-3 border-t pt-6\">\n <button\n type=\"button\"\n onClick={handleCancel}\n className=\"inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted transition-colors\"\n >\n <X size={16} />\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={isSubmitting || apps.length === 0}\n className=\"inline-flex items-center gap-2 rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors disabled:opacity-50\"\n >\n <Save size={16} />\n {isSubmitting ? \"Generating...\" : \"Generate API Key\"}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}