@burdenoff/microfe-devportal 2026.1112.1 → 2026.1112.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.
@@ -1,228 +1,247 @@
1
1
  import { PageLayout as e } from "../components/PageLayout.js";
2
2
  import { ActiveIndicator as t } from "../components/StatusBadge.js";
3
- import { useDeactivateAppMutation as n, useGetAuthAppQuery as r, useRotateAppSecretMutation as i, useUpdateAuthAppMutation as a } from "../../generated/global-operations.js";
4
- import { getErrorMessage as ee } from "../hooks/useGraphQL.js";
5
- import { useDevportal as o } from "../providers/DevportalProvider.js";
3
+ import { useDeactivateAppMutation as n, useGetAuthAppQuery as ee, useRotateAppSecretMutation as r, useUpdateAuthAppMutation as i } from "../../generated/global-operations.js";
4
+ import { getErrorMessage as a } from "../hooks/useGraphQL.js";
5
+ import { useDevportal as o, useIsFluidgridsVariant as te } from "../providers/DevportalProvider.js";
6
+ import { errorMessage as s } from "../utils/errorMessage.js";
6
7
  import "../components/index.js";
7
- import { cn as te } from "../utils/cn.js";
8
8
  import { PermissionGuard as ne } from "../components/PermissionGuard.js";
9
- import { nativeCopyText as s, nativeImpact as c, nativeNotify as re } from "../../utils/nativeBridge.js";
9
+ import { cn as re } from "../utils/cn.js";
10
+ import { nativeCopyText as ie, nativeImpact as ae, nativeNotify as c } from "../../utils/nativeBridge.js";
10
11
  import "../hooks/index.js";
11
12
  import { useState as l } from "react";
12
- import { AlertCircle as ie, ArrowLeft as ae, Copy as u, Eye as d, EyeOff as f, Loader2 as p, RefreshCw as m, Trash2 as h } from "lucide-react";
13
- import { useParams as g } from "react-router";
14
- import { jsx as _, jsxs as v } from "react/jsx-runtime";
15
- import { useI18n as y } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
13
+ import { AlertCircle as u, ArrowLeft as d, Copy as f, Eye as p, EyeOff as m, Loader2 as h, RefreshCw as g, Trash2 as oe } from "lucide-react";
14
+ import { useParams as _ } from "react-router";
15
+ import { jsx as v, jsxs as y } from "react/jsx-runtime";
16
+ import { useI18n as se } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
16
17
  import { AlertDialog as b, AlertDialogAction as x, AlertDialogCancel as S, AlertDialogContent as C, AlertDialogDescription as w, AlertDialogFooter as T, AlertDialogHeader as E, AlertDialogTitle as D, CTAOverflowMenu as O, GlassCard as k } from "@burdenoff/fe-libs/ui";
17
18
  // src/devportal/pages/OAuthClientDetailPage.tsx
18
19
  var A = () => {
19
- let { clientId: A } = g(), { navigate: j, basePath: M } = o(), { t: N } = y(), P = (e, t) => {
20
+ let { clientId: A } = _(), { navigate: j, basePath: M } = o(), { t: N } = se(), P = (e, t) => {
20
21
  let n = N(e);
21
22
  return n === e ? t : n;
22
- }, [F, I] = l(!1), [L, R] = l(null), [z, B] = l(!1), [V, H] = l(!1), { data: U, loading: W, error: G, refetch: K } = r({
23
+ }, [F, I] = l(!1), [L, R] = l(null), [z, B] = l(!1), [V, H] = l(!1), [U, W] = l(null), G = te(), { data: K, loading: ce, error: q, refetch: J } = ee({
23
24
  variables: { id: A || "" },
24
25
  skip: !A,
25
26
  fetchPolicy: "cache-and-network"
26
- }), [q, { loading: J }] = i(), [Y, { loading: X }] = n(), [oe] = a(), Z = U?.app ? {
27
- id: U.app.id,
28
- name: U.app.name,
29
- description: U.app.description ?? void 0,
30
- clientId: U.app.clientId,
31
- type: U.app.type,
32
- status: U.app.status,
33
- scopes: U.app.scopes ?? [],
34
- redirectUris: U.app.redirectUris ?? [],
35
- ownerUserId: U.app.ownerUserId,
36
- tenantId: U.app.tenantId,
37
- createdAt: U.app.createdAt,
38
- updatedAt: U.app.updatedAt
27
+ }), [le, { loading: Y }] = r(), [ue, { loading: X }] = n(), [de] = i(), Z = K?.app ? {
28
+ id: K.app.id,
29
+ name: K.app.name,
30
+ description: K.app.description ?? void 0,
31
+ clientId: K.app.clientId,
32
+ type: K.app.type,
33
+ status: K.app.status,
34
+ scopes: K.app.scopes ?? [],
35
+ redirectUris: K.app.redirectUris ?? [],
36
+ ownerUserId: K.app.ownerUserId,
37
+ tenantId: K.app.tenantId,
38
+ createdAt: K.app.createdAt,
39
+ updatedAt: K.app.updatedAt
39
40
  } : null, Q = (e) => {
40
41
  j?.(`${M}${e}`);
41
42
  }, $ = async (e) => {
42
- c("light");
43
- let t = await s(e);
44
- re(t ? "success" : "error");
45
- }, se = async () => {
46
- if (A) try {
47
- let e = (await q({ variables: { id: A } })).data?.rotateAppSecret?.clientSecret;
48
- e && (R(e), I(!0)), H(!1), K();
49
- } catch {}
50
- }, ce = async () => {
51
- if (A) try {
52
- await Y({ variables: { id: A } }), B(!1), Q("/oauth");
53
- } catch {}
43
+ ae("light");
44
+ let t = await ie(e);
45
+ c(t ? "success" : "error");
46
+ }, fe = async () => {
47
+ if (A) {
48
+ W(null);
49
+ try {
50
+ let e = (await le({ variables: { id: A } })).data?.rotateAppSecret?.clientSecret;
51
+ e && (R(e), I(!0)), H(!1), J();
52
+ } catch (e) {
53
+ W(s(e, P("devportal.oauthActionFailed", "That did not work. Nothing was changed. Please try again.")));
54
+ }
55
+ }
56
+ }, pe = async () => {
57
+ if (A) {
58
+ W(null);
59
+ try {
60
+ await ue({ variables: { id: A } }), B(!1), Q("/oauth");
61
+ } catch (e) {
62
+ W(s(e, P("devportal.oauthActionFailed", "That did not work. Nothing was changed. Please try again.")));
63
+ }
64
+ }
54
65
  };
55
- return W && !U ? /* @__PURE__ */ _(e, {
66
+ return ce && !K ? /* @__PURE__ */ v(e, {
56
67
  title: P("devportal.appDetails", "App Details"),
57
68
  description: P("devportal.loadingDots", "Loading..."),
58
69
  backLink: "/oauth",
59
70
  backLabel: P("devportal.backToApps", "Back to Apps"),
60
- children: /* @__PURE__ */ _("div", {
71
+ children: /* @__PURE__ */ v("div", {
61
72
  className: "flex items-center justify-center py-12",
62
- children: /* @__PURE__ */ _(p, { className: "h-8 w-8 animate-spin text-text-secondary" })
73
+ children: /* @__PURE__ */ v(h, { className: "h-8 w-8 animate-spin text-text-secondary" })
63
74
  })
64
- }) : G || !Z ? /* @__PURE__ */ _(e, {
75
+ }) : q || !Z ? /* @__PURE__ */ v(e, {
65
76
  title: P("devportal.appDetails", "App Details"),
66
77
  description: P("devportal.failedToLoad", "Failed to load"),
67
78
  backLink: "/oauth",
68
79
  backLabel: P("devportal.backToApps", "Back to Apps"),
69
- children: /* @__PURE__ */ v("div", {
80
+ children: /* @__PURE__ */ y("div", {
70
81
  className: "rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center",
71
82
  children: [
72
- /* @__PURE__ */ _(ie, { className: "mx-auto h-8 w-8 text-status-error-text" }),
73
- /* @__PURE__ */ _("h3", {
83
+ /* @__PURE__ */ v(u, { className: "mx-auto h-8 w-8 text-status-error-text" }),
84
+ /* @__PURE__ */ v("h3", {
74
85
  className: "mt-2 font-medium text-status-error-text",
75
- children: G ? P("devportal.failedToLoadApp", "Failed to load app") : P("devportal.appNotFound", "App not found")
86
+ children: q ? P("devportal.failedToLoadApp", "Failed to load app") : P("devportal.appNotFound", "App not found")
76
87
  }),
77
- G && /* @__PURE__ */ _("p", {
88
+ q && /* @__PURE__ */ v("p", {
78
89
  className: "mt-1 text-sm text-text-secondary",
79
- children: ee(G)
90
+ children: a(q)
80
91
  }),
81
- /* @__PURE__ */ v("button", {
92
+ /* @__PURE__ */ y("button", {
82
93
  onClick: () => Q("/oauth"),
83
94
  className: "mt-4 inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90",
84
- children: [/* @__PURE__ */ _(ae, { size: 16 }), P("devportal.backToApps", "Back to Apps")]
95
+ children: [/* @__PURE__ */ v(d, { size: 16 }), P("devportal.backToApps", "Back to Apps")]
85
96
  })
86
97
  ]
87
98
  })
88
- }) : /* @__PURE__ */ _(ne, {
99
+ }) : /* @__PURE__ */ v(ne, {
89
100
  authOnly: !0,
90
- children: /* @__PURE__ */ v(e, {
101
+ children: /* @__PURE__ */ y(e, {
91
102
  title: Z.name,
92
103
  description: Z.description || P("devportal.oauthAppDetailsAndCredentials", "OAuth App details and credentials"),
93
104
  purpose: P("devportal.oauthAppDetailPurpose", "These are this OAuth app's credentials and the scopes it may request. Copy the client ID for your integration, rotate the secret if it leaks, and keep scopes minimal so users only grant what the app truly needs."),
94
105
  backLink: "/oauth",
95
106
  backLabel: P("devportal.backToApps", "Back to Apps"),
96
- actions: /* @__PURE__ */ _(O, {
107
+ actions: /* @__PURE__ */ v(O, {
97
108
  primary: {
98
109
  label: Z.status === "ACTIVE" ? P("devportal.deactivate", "Deactivate") : P("devportal.activate", "Activate"),
99
110
  onSelect: async () => {
100
111
  if (!A || !Z) return;
112
+ W(null);
101
113
  let e = Z.status === "ACTIVE" ? "INACTIVE" : "ACTIVE";
102
114
  try {
103
- await oe({ variables: {
115
+ await de({ variables: {
104
116
  id: A,
105
117
  input: { status: e }
106
- } }), K();
107
- } catch {}
118
+ } }), J();
119
+ } catch (e) {
120
+ W(s(e, P("devportal.oauthActionFailed", "That did not work. Nothing was changed. Please try again.")));
121
+ }
108
122
  }
109
123
  },
110
124
  actions: [{
111
- label: P("devportal.delete", "Delete"),
112
- icon: /* @__PURE__ */ _(h, { size: 16 }),
125
+ label: G ? P("devportal.deactivate", "Deactivate") : P("devportal.delete", "Delete"),
126
+ icon: /* @__PURE__ */ v(oe, { size: 16 }),
113
127
  intent: "destructive",
114
128
  onSelect: () => B(!0)
115
129
  }]
116
130
  }),
117
131
  children: [
118
- /* @__PURE__ */ v("div", {
132
+ U && /* @__PURE__ */ v("div", {
133
+ role: "alert",
134
+ className: "mb-4 rounded-md bg-status-error-bg px-3 py-2 text-sm text-status-error-text",
135
+ children: U
136
+ }),
137
+ /* @__PURE__ */ y("div", {
119
138
  className: "grid gap-6 md:grid-cols-2",
120
- children: [/* @__PURE__ */ v("div", {
139
+ children: [/* @__PURE__ */ y("div", {
121
140
  className: "rounded-card border border-border-seam bg-bg-surface p-6",
122
- children: [/* @__PURE__ */ _("h3", {
141
+ children: [/* @__PURE__ */ v("h3", {
123
142
  className: "text-sm font-medium text-text-secondary mb-4",
124
143
  children: P("devportal.statusAndConfiguration", "Status & Configuration")
125
- }), /* @__PURE__ */ v("div", {
144
+ }), /* @__PURE__ */ y("div", {
126
145
  className: "space-y-3",
127
146
  children: [
128
- /* @__PURE__ */ v("div", {
147
+ /* @__PURE__ */ y("div", {
129
148
  className: "flex justify-between",
130
- children: [/* @__PURE__ */ _("span", {
149
+ children: [/* @__PURE__ */ v("span", {
131
150
  className: "text-sm text-text-secondary",
132
151
  children: P("devportal.status", "Status")
133
- }), /* @__PURE__ */ v("div", {
152
+ }), /* @__PURE__ */ y("div", {
134
153
  className: "flex items-center gap-2",
135
- children: [/* @__PURE__ */ _(t, { active: Z.status === "ACTIVE" }), /* @__PURE__ */ _("span", {
154
+ children: [/* @__PURE__ */ v(t, { active: Z.status === "ACTIVE" }), /* @__PURE__ */ v("span", {
136
155
  className: "text-sm font-medium",
137
156
  children: Z.status
138
157
  })]
139
158
  })]
140
159
  }),
141
- /* @__PURE__ */ v("div", {
160
+ /* @__PURE__ */ y("div", {
142
161
  className: "flex justify-between",
143
- children: [/* @__PURE__ */ _("span", {
162
+ children: [/* @__PURE__ */ v("span", {
144
163
  className: "text-sm text-text-secondary",
145
164
  children: P("devportal.type", "Type")
146
- }), /* @__PURE__ */ _("span", {
147
- className: te("rounded px-2 py-0.5 text-xs font-medium", Z.type === "SERVICE" ? "bg-accent-purple-subtle text-accent-purple" : Z.type === "INTEGRATION" || Z.type === "API_INTEGRATION" ? "bg-status-info-bg-subtle text-status-info-text" : "bg-status-success-bg-subtle text-status-success-text"),
165
+ }), /* @__PURE__ */ v("span", {
166
+ className: re("rounded px-2 py-0.5 text-xs font-medium", Z.type === "SERVICE" ? "bg-accent-purple-subtle text-accent-purple" : Z.type === "INTEGRATION" || Z.type === "API_INTEGRATION" ? "bg-status-info-bg-subtle text-status-info-text" : "bg-status-success-bg-subtle text-status-success-text"),
148
167
  children: Z.type
149
168
  })]
150
169
  }),
151
- /* @__PURE__ */ v("div", {
170
+ /* @__PURE__ */ y("div", {
152
171
  className: "flex justify-between",
153
- children: [/* @__PURE__ */ _("span", {
172
+ children: [/* @__PURE__ */ v("span", {
154
173
  className: "text-sm text-text-secondary",
155
174
  children: P("devportal.created", "Created")
156
- }), /* @__PURE__ */ _("span", {
175
+ }), /* @__PURE__ */ v("span", {
157
176
  className: "text-sm",
158
177
  children: new Date(Z.createdAt).toLocaleDateString()
159
178
  })]
160
179
  })
161
180
  ]
162
181
  })]
163
- }), /* @__PURE__ */ v(k, {
182
+ }), /* @__PURE__ */ y(k, {
164
183
  treatment: "glass",
165
184
  glow: !0,
166
185
  className: "p-6",
167
- children: [/* @__PURE__ */ _("h3", {
186
+ children: [/* @__PURE__ */ v("h3", {
168
187
  className: "text-sm font-medium text-text-secondary mb-4",
169
188
  children: P("devportal.credentials", "Credentials")
170
- }), /* @__PURE__ */ v("div", {
189
+ }), /* @__PURE__ */ y("div", {
171
190
  className: "space-y-3",
172
191
  children: [
173
- /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
192
+ /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
174
193
  className: "text-xs text-text-secondary",
175
194
  children: P("devportal.clientId", "Client ID")
176
- }), /* @__PURE__ */ v("div", {
195
+ }), /* @__PURE__ */ y("div", {
177
196
  className: "mt-1 flex items-center gap-2",
178
- children: [/* @__PURE__ */ _("code", {
197
+ children: [/* @__PURE__ */ v("code", {
179
198
  className: "flex-1 rounded bg-bg-sunken px-3 py-1.5 text-sm font-mono",
180
199
  children: Z.clientId
181
- }), /* @__PURE__ */ _("button", {
200
+ }), /* @__PURE__ */ v("button", {
182
201
  onClick: () => $(Z.clientId),
183
202
  className: "rounded p-1.5 hover:bg-bg-sunken transition-colors",
184
203
  title: "Copy",
185
- children: /* @__PURE__ */ _(u, { size: 14 })
204
+ children: /* @__PURE__ */ v(f, { size: 14 })
186
205
  })]
187
206
  })] }),
188
- L && /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
207
+ L && /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("label", {
189
208
  className: "text-xs text-status-error-text font-medium",
190
209
  children: P("devportal.newClientSecretShownOnce", "New Client Secret (shown only once!)")
191
- }), /* @__PURE__ */ v("div", {
210
+ }), /* @__PURE__ */ y("div", {
192
211
  className: "mt-1 flex items-center gap-2",
193
212
  children: [
194
- /* @__PURE__ */ _("code", {
213
+ /* @__PURE__ */ v("code", {
195
214
  className: "flex-1 rounded bg-status-error-bg/10 border border-status-error-border/30 px-3 py-1.5 text-sm font-mono",
196
215
  children: F ? L : "••••••••••••••••"
197
216
  }),
198
- /* @__PURE__ */ _("button", {
217
+ /* @__PURE__ */ v("button", {
199
218
  onClick: () => I(!F),
200
219
  className: "rounded p-1.5 hover:bg-bg-sunken transition-colors",
201
- children: _(F ? f : d, { size: 14 })
220
+ children: v(F ? m : p, { size: 14 })
202
221
  }),
203
- /* @__PURE__ */ _("button", {
222
+ /* @__PURE__ */ v("button", {
204
223
  onClick: () => $(L),
205
224
  className: "rounded p-1.5 hover:bg-bg-sunken transition-colors",
206
- children: /* @__PURE__ */ _(u, { size: 14 })
225
+ children: /* @__PURE__ */ v(f, { size: 14 })
207
226
  })
208
227
  ]
209
228
  })] }),
210
- /* @__PURE__ */ v("button", {
229
+ /* @__PURE__ */ y("button", {
211
230
  onClick: () => H(!0),
212
- disabled: J,
231
+ disabled: Y,
213
232
  className: "mt-2 inline-flex items-center gap-2 rounded-md bg-status-warning-bg-subtle px-3 py-1.5 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle transition-colors disabled:opacity-50",
214
- children: [/* @__PURE__ */ _(m, {
233
+ children: [/* @__PURE__ */ v(g, {
215
234
  size: 14,
216
- className: J ? "animate-spin" : ""
235
+ className: Y ? "animate-spin" : ""
217
236
  }), P("devportal.rotateSecret", "Rotate Secret")]
218
237
  })
219
238
  ]
220
239
  })]
221
240
  })]
222
241
  }),
223
- /* @__PURE__ */ v("div", {
242
+ /* @__PURE__ */ y("div", {
224
243
  className: "rounded-card border border-border-seam bg-bg-surface p-6",
225
- children: [/* @__PURE__ */ v("h3", {
244
+ children: [/* @__PURE__ */ y("h3", {
226
245
  className: "text-sm font-medium text-text-secondary mb-3",
227
246
  children: [
228
247
  P("devportal.scopes", "Scopes"),
@@ -230,20 +249,20 @@ var A = () => {
230
249
  Z.scopes.length,
231
250
  ")"
232
251
  ]
233
- }), Z.scopes.length > 0 ? /* @__PURE__ */ _("div", {
252
+ }), Z.scopes.length > 0 ? /* @__PURE__ */ v("div", {
234
253
  className: "flex flex-wrap gap-2",
235
- children: Z.scopes.map((e) => /* @__PURE__ */ _("span", {
254
+ children: Z.scopes.map((e) => /* @__PURE__ */ v("span", {
236
255
  className: "rounded-full bg-action-primary-bg/10 px-3 py-1 text-xs font-medium text-primary",
237
256
  children: e
238
257
  }, e))
239
- }) : /* @__PURE__ */ _("p", {
258
+ }) : /* @__PURE__ */ v("p", {
240
259
  className: "text-sm text-text-secondary",
241
260
  children: P("devportal.noScopesConfigured", "No scopes configured")
242
261
  })]
243
262
  }),
244
- /* @__PURE__ */ v("div", {
263
+ /* @__PURE__ */ y("div", {
245
264
  className: "rounded-card border border-border-seam bg-bg-surface p-6",
246
- children: [/* @__PURE__ */ v("h3", {
265
+ children: [/* @__PURE__ */ y("h3", {
247
266
  className: "text-sm font-medium text-text-secondary mb-3",
248
267
  children: [
249
268
  P("devportal.redirectUris", "Redirect URIs"),
@@ -251,51 +270,51 @@ var A = () => {
251
270
  Z.redirectUris.length,
252
271
  ")"
253
272
  ]
254
- }), Z.redirectUris.length > 0 ? /* @__PURE__ */ _("div", {
273
+ }), Z.redirectUris.length > 0 ? /* @__PURE__ */ v("div", {
255
274
  className: "space-y-2",
256
- children: Z.redirectUris.map((e) => /* @__PURE__ */ v("div", {
275
+ children: Z.redirectUris.map((e) => /* @__PURE__ */ y("div", {
257
276
  className: "flex items-center gap-2",
258
- children: [/* @__PURE__ */ _("code", {
277
+ children: [/* @__PURE__ */ v("code", {
259
278
  className: "rounded bg-bg-sunken px-3 py-1.5 text-sm font-mono",
260
279
  children: e
261
- }), /* @__PURE__ */ _("button", {
280
+ }), /* @__PURE__ */ v("button", {
262
281
  onClick: () => $(e),
263
282
  className: "rounded p-1 hover:bg-bg-sunken transition-colors",
264
- children: /* @__PURE__ */ _(u, { size: 12 })
283
+ children: /* @__PURE__ */ v(f, { size: 12 })
265
284
  })]
266
285
  }, e))
267
- }) : /* @__PURE__ */ _("p", {
286
+ }) : /* @__PURE__ */ v("p", {
268
287
  className: "text-sm text-text-secondary",
269
288
  children: P("devportal.noRedirectUrisConfigured", "No redirect URIs configured")
270
289
  })]
271
290
  }),
272
- /* @__PURE__ */ _(b, {
291
+ /* @__PURE__ */ v(b, {
273
292
  open: V,
274
293
  onOpenChange: H,
275
- children: /* @__PURE__ */ v(C, { children: [/* @__PURE__ */ v(E, { children: [/* @__PURE__ */ _(D, { children: P("devportal.rotateClientSecretQuestion", "Rotate Client Secret?") }), /* @__PURE__ */ _(w, { children: P("devportal.rotateSecretWarning", "This will invalidate the current client secret. All integrations using the old secret will stop working immediately.") })] }), /* @__PURE__ */ v(T, { children: [/* @__PURE__ */ _(S, {
276
- disabled: J,
294
+ children: /* @__PURE__ */ y(C, { children: [/* @__PURE__ */ y(E, { children: [/* @__PURE__ */ v(D, { children: P("devportal.rotateClientSecretQuestion", "Rotate Client Secret?") }), /* @__PURE__ */ v(w, { children: P("devportal.rotateSecretWarning", "This will invalidate the current client secret. All integrations using the old secret will stop working immediately.") })] }), /* @__PURE__ */ y(T, { children: [/* @__PURE__ */ v(S, {
295
+ disabled: Y,
277
296
  children: P("devportal.cancel", "Cancel")
278
- }), /* @__PURE__ */ _(x, {
297
+ }), /* @__PURE__ */ v(x, {
279
298
  onClick: (e) => {
280
- e.preventDefault(), se();
299
+ e.preventDefault(), fe();
281
300
  },
282
- disabled: J,
301
+ disabled: Y,
283
302
  className: "bg-status-warning-bg text-white hover:bg-status-warning-bg/90",
284
- children: J ? P("devportal.rotating", "Rotating...") : P("devportal.rotateSecret", "Rotate Secret")
303
+ children: Y ? P("devportal.rotating", "Rotating...") : P("devportal.rotateSecret", "Rotate Secret")
285
304
  })] })] })
286
305
  }),
287
- /* @__PURE__ */ _(b, {
306
+ /* @__PURE__ */ v(b, {
288
307
  open: z,
289
308
  onOpenChange: B,
290
- children: /* @__PURE__ */ v(C, { children: [/* @__PURE__ */ v(E, { children: [/* @__PURE__ */ _(D, {
309
+ children: /* @__PURE__ */ y(C, { children: [/* @__PURE__ */ y(E, { children: [/* @__PURE__ */ v(D, {
291
310
  className: "text-status-error-text",
292
311
  children: P("devportal.deactivateAppQuestion", "Deactivate App?")
293
- }), /* @__PURE__ */ _(w, { children: P("devportal.deactivateAppWarning", "This will deactivate the app and revoke all tokens. This action cannot be undone easily.") })] }), /* @__PURE__ */ v(T, { children: [/* @__PURE__ */ _(S, {
312
+ }), /* @__PURE__ */ v(w, { children: P("devportal.deactivateAppWarning", "This will deactivate the app and revoke all tokens. This action cannot be undone easily.") })] }), /* @__PURE__ */ y(T, { children: [/* @__PURE__ */ v(S, {
294
313
  disabled: X,
295
314
  children: P("devportal.cancel", "Cancel")
296
- }), /* @__PURE__ */ _(x, {
315
+ }), /* @__PURE__ */ v(x, {
297
316
  onClick: (e) => {
298
- e.preventDefault(), ce();
317
+ e.preventDefault(), pe();
299
318
  },
300
319
  disabled: X,
301
320
  className: "bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90",
@@ -1 +1 @@
1
- {"version":3,"file":"OAuthClientDetailPage.js","names":["OAuthClientDetailPage","appId","useParams","useDevportal","useI18n","tr","value","t","key","fallback","showSecret","setShowSecret","useState","newSecret","setNewSecret","confirmDelete","setConfirmDelete","confirmRotate","setConfirmRotate","useGetAuthAppQuery","rotateSecret","rotating","useRotateAppSecretMutation","deactivateApp","deactivating","useDeactivateAppMutation","updateApp","useUpdateAuthAppMutation","app","data","handleNavigate","basePath","path","handleCopy","ok","nativeCopyText","text","handleRotateSecret","secret","refetch","handleDeactivate","loading","PageLayout","Loader2","error","AlertCircle","getErrorMessage","ArrowLeft","PermissionGuard","CTAOverflowMenu","newStatus","Trash2","ActiveIndicator","cn","GlassCard","Copy","jsx","EyeOff","Eye","RefreshCw","scope","uri","AlertDialog","AlertDialogContent","AlertDialogHeader","AlertDialogTitle","AlertDialogDescription","AlertDialogFooter","AlertDialogCancel","AlertDialogAction"],"sources":["../../../src/devportal/pages/OAuthClientDetailPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport {\n Copy,\n Eye,\n EyeOff,\n Loader2,\n AlertCircle,\n RefreshCw,\n Trash2,\n ArrowLeft,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n CTAOverflowMenu,\n GlassCard,\n} from \"@burdenoff/fe-libs/ui\";\nimport { useParams } from \"react-router\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { ActiveIndicator } from \"../components/StatusBadge\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport type { AuthApp } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport {\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n} from \"../../utils/nativeBridge\";\nimport {\n useGetAuthAppQuery,\n useRotateAppSecretMutation,\n useDeactivateAppMutation,\n useUpdateAuthAppMutation,\n getErrorMessage,\n} from \"../hooks\";\n\n/**\n * OAuth App Detail page\n *\n * Shows details for an auth-svc App (replaces devportal-svc OAuthClient detail).\n * Supports secret rotation, deactivation, and status toggle.\n */\nexport const OAuthClientDetailPage: FC = () => {\n const { clientId: appId } = useParams<{ clientId: string }>();\n const { navigate, basePath } = useDevportal();\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 [showSecret, setShowSecret] = useState(false);\n const [newSecret, setNewSecret] = useState<string | null>(null);\n const [confirmDelete, setConfirmDelete] = useState(false);\n const [confirmRotate, setConfirmRotate] = useState(false);\n\n const { data, loading, error, refetch } = useGetAuthAppQuery({\n variables: { id: appId || \"\" },\n skip: !appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const [rotateSecret, { loading: rotating }] = useRotateAppSecretMutation();\n const [deactivateApp, { loading: deactivating }] = useDeactivateAppMutation();\n const [updateApp] = useUpdateAuthAppMutation();\n\n const app: AuthApp | null = data?.app\n ? {\n id: data.app.id,\n name: data.app.name,\n description: data.app.description ?? undefined,\n clientId: data.app.clientId,\n type: data.app.type as AuthApp[\"type\"],\n status: data.app.status as AuthApp[\"status\"],\n scopes: data.app.scopes ?? [],\n redirectUris: data.app.redirectUris ?? [],\n ownerUserId: data.app.ownerUserId,\n tenantId: data.app.tenantId,\n createdAt: data.app.createdAt,\n updatedAt: data.app.updatedAt,\n }\n : null;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleCopy = async (text: string) => {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(text);\n void nativeNotify(ok ? \"success\" : \"error\");\n };\n\n const handleRotateSecret = async () => {\n if (!appId) return;\n try {\n const result = await rotateSecret({ variables: { id: appId } });\n const secret = result.data?.rotateAppSecret?.clientSecret;\n if (secret) {\n setNewSecret(secret);\n setShowSecret(true);\n }\n setConfirmRotate(false);\n refetch();\n } catch {\n // error handled by Apollo\n }\n };\n\n const handleDeactivate = async () => {\n if (!appId) return;\n try {\n await deactivateApp({ variables: { id: appId } });\n setConfirmDelete(false);\n handleNavigate(\"/oauth\");\n } catch {\n // error handled by Apollo\n }\n };\n\n const handleToggleStatus = async () => {\n if (!appId || !app) return;\n const newStatus = app.status === \"ACTIVE\" ? \"INACTIVE\" : \"ACTIVE\";\n try {\n await updateApp({\n variables: { id: appId, input: { status: newStatus } },\n });\n refetch();\n } catch {\n // error handled by Apollo\n }\n };\n\n if (loading && !data) {\n return (\n <PageLayout\n title={tr(\"devportal.appDetails\", \"App Details\")}\n description={tr(\"devportal.loadingDots\", \"Loading...\")}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n >\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n </div>\n </PageLayout>\n );\n }\n\n if (error || !app) {\n return (\n <PageLayout\n title={tr(\"devportal.appDetails\", \"App Details\")}\n description={tr(\"devportal.failedToLoad\", \"Failed to load\")}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n >\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center\">\n <AlertCircle className=\"mx-auto h-8 w-8 text-status-error-text\" />\n <h3 className=\"mt-2 font-medium text-status-error-text\">\n {error\n ? tr(\"devportal.failedToLoadApp\", \"Failed to load app\")\n : tr(\"devportal.appNotFound\", \"App not found\")}\n </h3>\n {error && (\n <p className=\"mt-1 text-sm text-text-secondary\">\n {getErrorMessage(error)}\n </p>\n )}\n <button\n onClick={() => handleNavigate(\"/oauth\")}\n className=\"mt-4 inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <ArrowLeft size={16} />\n {tr(\"devportal.backToApps\", \"Back to Apps\")}\n </button>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={app.name}\n description={\n app.description ||\n tr(\n \"devportal.oauthAppDetailsAndCredentials\",\n \"OAuth App details and credentials\",\n )\n }\n purpose={tr(\n \"devportal.oauthAppDetailPurpose\",\n \"These are this OAuth app's credentials and the scopes it may request. Copy the client ID for your integration, rotate the secret if it leaks, and keep scopes minimal so users only grant what the app truly needs.\",\n )}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n actions={\n <CTAOverflowMenu\n primary={{\n label:\n app.status === \"ACTIVE\"\n ? tr(\"devportal.deactivate\", \"Deactivate\")\n : tr(\"devportal.activate\", \"Activate\"),\n onSelect: handleToggleStatus,\n }}\n actions={[\n {\n label: tr(\"devportal.delete\", \"Delete\"),\n icon: <Trash2 size={16} />,\n intent: \"destructive\",\n onSelect: () => setConfirmDelete(true),\n },\n ]}\n />\n }\n >\n {/* Status & Type */}\n <div className=\"grid gap-6 md:grid-cols-2\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-4\">\n {tr(\"devportal.statusAndConfiguration\", \"Status & Configuration\")}\n </h3>\n <div className=\"space-y-3\">\n <div className=\"flex justify-between\">\n <span className=\"text-sm text-text-secondary\">\n {tr(\"devportal.status\", \"Status\")}\n </span>\n <div className=\"flex items-center gap-2\">\n <ActiveIndicator active={app.status === \"ACTIVE\"} />\n <span className=\"text-sm font-medium\">{app.status}</span>\n </div>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-sm text-text-secondary\">\n {tr(\"devportal.type\", \"Type\")}\n </span>\n <span\n className={cn(\n \"rounded px-2 py-0.5 text-xs font-medium\",\n app.type === \"SERVICE\"\n ? \"bg-accent-purple-subtle text-accent-purple\"\n : app.type === \"INTEGRATION\" ||\n app.type === \"API_INTEGRATION\"\n ? \"bg-status-info-bg-subtle text-status-info-text\"\n : \"bg-status-success-bg-subtle text-status-success-text\",\n )}\n >\n {app.type}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-sm text-text-secondary\">\n {tr(\"devportal.created\", \"Created\")}\n </span>\n <span className=\"text-sm\">\n {new Date(app.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n </div>\n\n {/* Credentials — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-4\">\n {tr(\"devportal.credentials\", \"Credentials\")}\n </h3>\n <div className=\"space-y-3\">\n <div>\n <label className=\"text-xs text-text-secondary\">\n {tr(\"devportal.clientId\", \"Client ID\")}\n </label>\n <div className=\"mt-1 flex items-center gap-2\">\n <code className=\"flex-1 rounded bg-bg-sunken px-3 py-1.5 text-sm font-mono\">\n {app.clientId}\n </code>\n <button\n onClick={() => handleCopy(app.clientId)}\n className=\"rounded p-1.5 hover:bg-bg-sunken transition-colors\"\n title=\"Copy\"\n >\n <Copy size={14} />\n </button>\n </div>\n </div>\n {newSecret && (\n <div>\n <label className=\"text-xs text-status-error-text font-medium\">\n {tr(\n \"devportal.newClientSecretShownOnce\",\n \"New Client Secret (shown only once!)\",\n )}\n </label>\n <div className=\"mt-1 flex items-center gap-2\">\n <code className=\"flex-1 rounded bg-status-error-bg/10 border border-status-error-border/30 px-3 py-1.5 text-sm font-mono\">\n {showSecret\n ? newSecret\n : \"\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\"}\n </code>\n <button\n onClick={() => setShowSecret(!showSecret)}\n className=\"rounded p-1.5 hover:bg-bg-sunken transition-colors\"\n >\n {showSecret ? <EyeOff size={14} /> : <Eye size={14} />}\n </button>\n <button\n onClick={() => handleCopy(newSecret)}\n className=\"rounded p-1.5 hover:bg-bg-sunken transition-colors\"\n >\n <Copy size={14} />\n </button>\n </div>\n </div>\n )}\n <button\n onClick={() => setConfirmRotate(true)}\n disabled={rotating}\n className=\"mt-2 inline-flex items-center gap-2 rounded-md bg-status-warning-bg-subtle px-3 py-1.5 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle transition-colors disabled:opacity-50\"\n >\n <RefreshCw\n size={14}\n className={rotating ? \"animate-spin\" : \"\"}\n />\n {tr(\"devportal.rotateSecret\", \"Rotate Secret\")}\n </button>\n </div>\n </GlassCard>\n </div>\n\n {/* Scopes */}\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-3\">\n {tr(\"devportal.scopes\", \"Scopes\")} ({app.scopes.length})\n </h3>\n {app.scopes.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {app.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-xs font-medium text-primary\"\n >\n {scope}\n </span>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr(\"devportal.noScopesConfigured\", \"No scopes configured\")}\n </p>\n )}\n </div>\n\n {/* Redirect URIs */}\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-3\">\n {tr(\"devportal.redirectUris\", \"Redirect URIs\")} (\n {app.redirectUris.length})\n </h3>\n {app.redirectUris.length > 0 ? (\n <div className=\"space-y-2\">\n {app.redirectUris.map((uri) => (\n <div key={uri} className=\"flex items-center gap-2\">\n <code className=\"rounded bg-bg-sunken px-3 py-1.5 text-sm font-mono\">\n {uri}\n </code>\n <button\n onClick={() => handleCopy(uri)}\n className=\"rounded p-1 hover:bg-bg-sunken transition-colors\"\n >\n <Copy size={12} />\n </button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.noRedirectUrisConfigured\",\n \"No redirect URIs configured\",\n )}\n </p>\n )}\n </div>\n\n {/* Confirm Rotate Dialog */}\n <AlertDialog open={confirmRotate} onOpenChange={setConfirmRotate}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {tr(\n \"devportal.rotateClientSecretQuestion\",\n \"Rotate Client Secret?\",\n )}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {tr(\n \"devportal.rotateSecretWarning\",\n \"This will invalidate the current client secret. All integrations using the old secret will stop working immediately.\",\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={rotating}>\n {tr(\"devportal.cancel\", \"Cancel\")}\n </AlertDialogCancel>\n <AlertDialogAction\n onClick={(e) => {\n e.preventDefault();\n void handleRotateSecret();\n }}\n disabled={rotating}\n className=\"bg-status-warning-bg text-white hover:bg-status-warning-bg/90\"\n >\n {rotating\n ? tr(\"devportal.rotating\", \"Rotating...\")\n : tr(\"devportal.rotateSecret\", \"Rotate Secret\")}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {/* Confirm Deactivate Dialog */}\n <AlertDialog open={confirmDelete} onOpenChange={setConfirmDelete}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle className=\"text-status-error-text\">\n {tr(\"devportal.deactivateAppQuestion\", \"Deactivate App?\")}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {tr(\n \"devportal.deactivateAppWarning\",\n \"This will deactivate the app and revoke all tokens. This action cannot be undone easily.\",\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={deactivating}>\n {tr(\"devportal.cancel\", \"Cancel\")}\n </AlertDialogCancel>\n <AlertDialogAction\n onClick={(e) => {\n e.preventDefault();\n void handleDeactivate();\n }}\n disabled={deactivating}\n className=\"bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90\"\n >\n {deactivating\n ? tr(\"devportal.deactivating\", \"Deactivating...\")\n : tr(\"devportal.deactivate\", \"Deactivate\")}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAmDA,IAAaA,UAAkC;CAC7C,IAAM,EAAE,UAAUC,MAAUC,EAAgC,GACtD,EAAE,aAAU,gBAAaC,EAAa,GACtC,EAAE,SAAMC,EAAQ,GAChBC,KAAM,GAAa,MAA6B;EACpD,IAAMC,IAAQC,EAAEC,CAAG;EACnB,OAAOF,MAAUE,IAAMC,IAAWH;CACpC,GACM,CAACI,GAAYC,KAAiBC,EAAS,EAAK,GAC5C,CAACC,GAAWC,KAAgBF,EAAwB,IAAI,GACxD,CAACG,GAAeC,KAAoBJ,EAAS,EAAK,GAClD,CAACK,GAAeC,KAAoBN,EAAS,EAAK,GAElD,EAAE,SAAM,YAAS,UAAO,eAAYO,EAAmB;EAC3D,WAAW,EAAE,IAAIlB,KAAS,GAAG;EAC7B,MAAM,CAACA;EACP,aAAa;CACf,CAAC,GAEK,CAACmB,GAAc,EAAE,SAASC,OAAcC,EAA2B,GACnE,CAACC,GAAe,EAAE,SAASC,OAAkBC,EAAyB,GACtE,CAACC,MAAaC,EAAyB,GAEvCC,IAAsBC,GAAM,MAC9B;EACE,IAAIA,EAAK,IAAI;EACb,MAAMA,EAAK,IAAI;EACf,aAAaA,EAAK,IAAI,eAAe,KAAA;EACrC,UAAUA,EAAK,IAAI;EACnB,MAAMA,EAAK,IAAI;EACf,QAAQA,EAAK,IAAI;EACjB,QAAQA,EAAK,IAAI,UAAU,CAAC;EAC5B,cAAcA,EAAK,IAAI,gBAAgB,CAAC;EACxC,aAAaA,EAAK,IAAI;EACtB,UAAUA,EAAK,IAAI;EACnB,WAAWA,EAAK,IAAI;EACpB,WAAWA,EAAK,IAAI;CACtB,IACA,MAEEC,KAAkB,MAAiB;EACvC,IAAW,GAAGC,IAAWC,GAAM;CACjC,GAEMC,IAAa,OAAO,MAAiB;EACzC,EAAkB,OAAO;EACzB,IAAMC,IAAK,MAAMC,EAAeC,CAAI;EACpC,GAAkBF,IAAK,YAAY,OAAO;CAC5C,GAEMG,KAAqB,YAAY;EACrC,IAAKpC,GACL,IAAI;GAEF,IAAMqC,KAAS,MADMlB,EAAa,EAAE,WAAW,EAAE,IAAInB,EAAM,EAAE,CAAC,EAAA,CACxC,MAAM,iBAAiB;GAM7C,AALIqC,MACFxB,EAAawB,CAAM,GACnB3B,EAAc,EAAI,IAEpBO,EAAiB,EAAK,GACtBqB,EAAQ;EACV,QAAQ,CAER;CACF,GAEMC,KAAmB,YAAY;EACnC,IAAKvC,GACL,IAAI;GAGF,AAFA,MAAMsB,EAAc,EAAE,WAAW,EAAE,IAAItB,EAAM,EAAE,CAAC,GAChDe,EAAiB,EAAK,GACtBc,EAAe,QAAQ;EACzB,QAAQ,CAER;CACF;CA8DA,OA/CIW,KAAW,CAACZ,IAEZ,kBAACa,GAAD;EACE,OAAOrC,EAAG,wBAAwB,aAAa;EAC/C,aAAaA,EAAG,yBAAyB,YAAY;EACrD,UAAS;EACT,WAAWA,EAAG,wBAAwB,cAAc;EAEpD,UAAA,kBAAC,OAAD;GAAK,WAAU;GACb,UAAA,kBAACsC,GAAD,EAAS,WAAU,2CAA4C,CAAA;EAC5D,CAAA;CACK,CAAA,IAIZC,KAAS,CAAChB,IAEV,kBAACc,GAAD;EACE,OAAOrC,EAAG,wBAAwB,aAAa;EAC/C,aAAaA,EAAG,0BAA0B,gBAAgB;EAC1D,UAAS;EACT,WAAWA,EAAG,wBAAwB,cAAc;EAEpD,UAAA,kBAAC,OAAD;GAAK,WAAU;GAAf,UAAA;IACE,kBAACwC,IAAD,EAAa,WAAU,yCAA0C,CAAA;IACjE,kBAAC,MAAD;KAAI,WAAU;KACX,UAAAD,IACGvC,EAAG,6BAA6B,oBAAoB,IACpDA,EAAG,yBAAyB,eAAe;IAC7C,CAAA;IACHuC,KACC,kBAAC,KAAD;KAAG,WAAU;KACV,UAAAE,GAAgBF,CAAK;IACrB,CAAA;IAEL,kBAAC,UAAD;KACE,eAAed,EAAe,QAAQ;KACtC,WAAU;KAFZ,UAAA,CAIE,kBAACiB,IAAD,EAAW,MAAM,GAAK,CAAA,GACrB1C,EAAG,wBAAwB,cAAc,CACpC;;GACL;;CACK,CAAA,IAKd,kBAAC2C,IAAD;EAAiB,UAAA;EACf,UAAA,kBAACN,GAAD;GACE,OAAOd,EAAI;GACX,aACEA,EAAI,eACJvB,EACE,2CACA,mCACF;GAEF,SAASA,EACP,mCACA,qNACF;GACA,UAAS;GACT,WAAWA,EAAG,wBAAwB,cAAc;GACpD,SACE,kBAAC4C,GAAD;IACE,SAAS;KACP,OACErB,EAAI,WAAW,WACXvB,EAAG,wBAAwB,YAAY,IACvCA,EAAG,sBAAsB,UAAU;KACzC,UAAU,YApFiB;MACrC,IAAI,CAACJ,KAAS,CAAC2B,GAAK;MACpB,IAAMsB,IAAYtB,EAAI,WAAW,WAAW,aAAa;MACzD,IAAI;OAIF,AAHA,MAAMF,GAAU,EACd,WAAW;QAAE,IAAIzB;QAAO,OAAO,EAAE,QAAQiD,EAAU;OAAE,EACvD,CAAC,GACDX,EAAQ;MACV,QAAQ,CAER;KACF;IA0EU;IACA,SAAS,CACP;KACE,OAAOlC,EAAG,oBAAoB,QAAQ;KACtC,MAAM,kBAAC8C,GAAD,EAAQ,MAAM,GAAK,CAAA;KACzB,QAAQ;KACR,gBAAgBnC,EAAiB,EAAI;IACvC,CACF;GACD,CAAA;GAhCL,UAAA;IAoCE,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;MAAf,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;OACX,UAAAX,EAAG,oCAAoC,wBAAwB;MAC9D,CAAA,GACJ,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA;QACE,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;UACb,UAAAA,EAAG,oBAAoB,QAAQ;SAC5B,CAAA,GACN,kBAAC,OAAD;UAAK,WAAU;UAAf,UAAA,CACE,kBAAC+C,GAAD,EAAiB,QAAQxB,EAAI,WAAW,SAAW,CAAA,GACnD,kBAAC,QAAD;WAAM,WAAU;WAAuB,UAAAA,EAAI;UAAa,CAAA,CACrD;SACF,CAAA,CAAA;;QACL,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;UACb,UAAAvB,EAAG,kBAAkB,MAAM;SACxB,CAAA,GACN,kBAAC,QAAD;UACE,WAAWgD,GACT,2CACAzB,EAAI,SAAS,YACT,+CACAA,EAAI,SAAS,iBACXA,EAAI,SAAS,oBACb,mDACA,sDACR;UAEC,UAAAA,EAAI;SACD,CAAA,CACH;;QACL,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;UACb,UAAAvB,EAAG,qBAAqB,SAAS;SAC9B,CAAA,GACN,kBAAC,QAAD;UAAM,WAAU;UACb,UAAA,IAAI,KAAKuB,EAAI,SAAS,CAAC,CAAC,mBAAmB;SACxC,CAAA,CACH;;OACF;MACF,CAAA,CAAA;KAGL,CAAA,GAAA,kBAAC0B,GAAD;MAAW,WAAU;MAAQ,MAAA;MAAK,WAAU;MAA5C,UAAA,CACE,kBAAC,MAAD;OAAI,WAAU;OACX,UAAAjD,EAAG,yBAAyB,aAAa;MACxC,CAAA,GACJ,kBAAC,OAAD;OAAK,WAAU;OAAf,UAAA;QACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;SACd,UAAAA,EAAG,sBAAsB,WAAW;QAChC,CAAA,GACP,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA,CACE,kBAAC,QAAD;UAAM,WAAU;UACb,UAAAuB,EAAI;SACD,CAAA,GACN,kBAAC,UAAD;UACE,eAAeK,EAAWL,EAAI,QAAQ;UACtC,WAAU;UACV,OAAM;UAEN,UAAA,kBAAC2B,GAAD,EAAM,MAAM,GAAK,CAAA;SACX,CAAA,CACL;QACF,CAAA,CAAA,EAAA,CAAA;QACJ1C,KACC,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;SAAO,WAAU;SACd,UAAAR,EACC,sCACA,sCACF;QACK,CAAA,GACP,kBAAC,OAAD;SAAK,WAAU;SAAf,UAAA;UACE,kBAAC,QAAD;WAAM,WAAU;WACb,UAAAK,IACGG,IACA;UACA,CAAA;UACN,kBAAC,UAAD;WACE,eAAeF,EAAc,CAACD,CAAU;WACxC,WAAU;WAET,UAAa8C,EAAb9C,IAAc+C,IAAuBC,GAAxB,EAAQ,MAAM,GAAK,CAAoB;UAC/C,CAAA;UACR,kBAAC,UAAD;WACE,eAAezB,EAAWpB,CAAS;WACnC,WAAU;WAEV,UAAA,kBAAC0C,GAAD,EAAM,MAAM,GAAK,CAAA;UACX,CAAA;SACL;QACF,CAAA,CAAA,EAAA,CAAA;QAEP,kBAAC,UAAD;SACE,eAAerC,EAAiB,EAAI;SACpC,UAAUG;SACV,WAAU;SAHZ,UAAA,CAKE,kBAACsC,GAAD;UACE,MAAM;UACN,WAAWtC,IAAW,iBAAiB;SACxC,CAAA,GACAhB,EAAG,0BAA0B,eAAe,CACvC;;OACL;MACI,CAAA,CAAA;KACR,CAAA,CAAA;;IAGL,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;MAAd,UAAA;OACGA,EAAG,oBAAoB,QAAQ;OAAE;OAAGuB,EAAI,OAAO;OAAO;MACrD;KACH,CAAA,GAAAA,EAAI,OAAO,SAAS,IACnB,kBAAC,OAAD;MAAK,WAAU;MACZ,UAAAA,EAAI,OAAO,KAAK,MACf,kBAAC,QAAD;OAEE,WAAU;OAET,UAAAgC;MACG,GAJCA,CAID,CACP;KACE,CAAA,IAEL,kBAAC,KAAD;MAAG,WAAU;MACV,UAAAvD,EAAG,gCAAgC,sBAAsB;KACzD,CAAA,CAEF;;IAGL,kBAAC,OAAD;KAAK,WAAU;KAAf,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;MAAd,UAAA;OACGA,EAAG,0BAA0B,eAAe;OAAE;OAC9CuB,EAAI,aAAa;OAAO;MACvB;KACH,CAAA,GAAAA,EAAI,aAAa,SAAS,IACzB,kBAAC,OAAD;MAAK,WAAU;MACZ,UAAAA,EAAI,aAAa,KAAK,MACrB,kBAAC,OAAD;OAAe,WAAU;OAAzB,UAAA,CACE,kBAAC,QAAD;QAAM,WAAU;QACb,UAAAiC;OACG,CAAA,GACN,kBAAC,UAAD;QACE,eAAe5B,EAAW4B,CAAG;QAC7B,WAAU;QAEV,UAAA,kBAACN,GAAD,EAAM,MAAM,GAAK,CAAA;OACX,CAAA,CACL;MAVK,GAAAM,CAUL,CACN;KACE,CAAA,IAEL,kBAAC,KAAD;MAAG,WAAU;MACV,UAAAxD,EACC,sCACA,6BACF;KACC,CAAA,CAEF;;IAGL,kBAACyD,GAAD;KAAa,MAAM7C;KAAe,cAAcC;KAC9C,UAAA,kBAAC6C,GAAD,EAAA,UAAA,CACE,kBAACC,GAAD,EAAA,UAAA,CACE,kBAACC,GAAD,EAAA,UACG5D,EACC,wCACA,uBACF,EACgB,CAAA,GAClB,kBAAC6D,GAAD,EAAA,UACG7D,EACC,iCACA,sHACF,EACsB,CAAA,CACP,EAAA,CAAA,GACnB,kBAAC8D,GAAD,EAAA,UAAA,CACE,kBAACC,GAAD;MAAmB,UAAU/C;MAC1B,UAAAhB,EAAG,oBAAoB,QAAQ;KACf,CAAA,GACnB,kBAACgE,GAAD;MACE,UAAU,MAAM;OAEd,AADA,EAAE,eAAe,GACjBhC,GAAwB;MAC1B;MACA,UAAUhB;MACV,WAAU;MAET,UAAAA,IACGhB,EAAG,sBAAsB,aAAa,IACtCA,EAAG,0BAA0B,eAAe;KAC/B,CAAA,CACF,EAAA,CAAA,CACD,EAAA,CAAA;IACT,CAAA;IAGb,kBAACyD,GAAD;KAAa,MAAM/C;KAAe,cAAcC;KAC9C,UAAA,kBAAC+C,GAAD,EAAA,UAAA,CACE,kBAACC,GAAD,EAAA,UAAA,CACE,kBAACC,GAAD;MAAkB,WAAU;MACzB,UAAA5D,EAAG,mCAAmC,iBAAiB;KACxC,CAAA,GAClB,kBAAC6D,GAAD,EAAA,UACG7D,EACC,kCACA,0FACF,EACsB,CAAA,CACP,EAAA,CAAA,GACnB,kBAAC8D,GAAD,EAAA,UAAA,CACE,kBAACC,GAAD;MAAmB,UAAU5C;MAC1B,UAAAnB,EAAG,oBAAoB,QAAQ;KACf,CAAA,GACnB,kBAACgE,GAAD;MACE,UAAU,MAAM;OAEd,AADA,EAAE,eAAe,GACjB7B,GAAsB;MACxB;MACA,UAAUhB;MACV,WAAU;MAET,UAAAA,IACGnB,EAAG,0BAA0B,iBAAiB,IAC9CA,EAAG,wBAAwB,YAAY;KAC1B,CAAA,CACF,EAAA,CAAA,CACD,EAAA,CAAA;IACT,CAAA;GACH;;CACG,CAAA;AAErB"}
1
+ {"version":3,"file":"OAuthClientDetailPage.js","names":["OAuthClientDetailPage","appId","useParams","useDevportal","useI18n","tr","value","t","key","fallback","showSecret","setShowSecret","useState","newSecret","setNewSecret","confirmDelete","setConfirmDelete","confirmRotate","setConfirmRotate","actionError","setActionError","isFluidgrids","useIsFluidgridsVariant","useGetAuthAppQuery","rotateSecret","rotating","useRotateAppSecretMutation","deactivateApp","deactivating","useDeactivateAppMutation","updateApp","useUpdateAuthAppMutation","app","data","handleNavigate","basePath","path","handleCopy","ok","nativeCopyText","text","handleRotateSecret","secret","refetch","err","errorMessage","handleDeactivate","loading","PageLayout","Loader2","error","AlertCircle","getErrorMessage","ArrowLeft","PermissionGuard","CTAOverflowMenu","newStatus","Trash2","ActiveIndicator","cn","GlassCard","Copy","jsx","EyeOff","Eye","RefreshCw","scope","uri","AlertDialog","AlertDialogContent","AlertDialogHeader","AlertDialogTitle","AlertDialogDescription","AlertDialogFooter","AlertDialogCancel","AlertDialogAction"],"sources":["../../../src/devportal/pages/OAuthClientDetailPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport {\n Copy,\n Eye,\n EyeOff,\n Loader2,\n AlertCircle,\n RefreshCw,\n Trash2,\n ArrowLeft,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n CTAOverflowMenu,\n GlassCard,\n} from \"@burdenoff/fe-libs/ui\";\nimport { useParams } from \"react-router\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { ActiveIndicator } from \"../components/StatusBadge\";\nimport {\n useDevportal,\n useIsFluidgridsVariant,\n} from \"../providers/DevportalProvider\";\nimport { errorMessage } from \"../utils/errorMessage\";\nimport { PermissionGuard } from \"../components\";\nimport type { AuthApp } from \"../types\";\nimport { cn } from \"../utils/cn\";\nimport {\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n} from \"../../utils/nativeBridge\";\nimport {\n useGetAuthAppQuery,\n useRotateAppSecretMutation,\n useDeactivateAppMutation,\n useUpdateAuthAppMutation,\n getErrorMessage,\n} from \"../hooks\";\n\n/**\n * OAuth App Detail page\n *\n * Shows details for an auth-svc App (replaces devportal-svc OAuthClient detail).\n * Supports secret rotation, deactivation, and status toggle.\n */\nexport const OAuthClientDetailPage: FC = () => {\n const { clientId: appId } = useParams<{ clientId: string }>();\n const { navigate, basePath } = useDevportal();\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 [showSecret, setShowSecret] = useState(false);\n const [newSecret, setNewSecret] = useState<string | null>(null);\n const [confirmDelete, setConfirmDelete] = useState(false);\n const [confirmRotate, setConfirmRotate] = useState(false);\n // A failed rotate / deactivate / status change stays visible (never swallowed).\n const [actionError, setActionError] = useState<string | null>(null);\n // FluidGrids: the \"Delete\" action only deactivates, so it is labelled that way.\n const isFluidgrids = useIsFluidgridsVariant();\n\n const { data, loading, error, refetch } = useGetAuthAppQuery({\n variables: { id: appId || \"\" },\n skip: !appId,\n fetchPolicy: \"cache-and-network\",\n });\n\n const [rotateSecret, { loading: rotating }] = useRotateAppSecretMutation();\n const [deactivateApp, { loading: deactivating }] = useDeactivateAppMutation();\n const [updateApp] = useUpdateAuthAppMutation();\n\n const app: AuthApp | null = data?.app\n ? {\n id: data.app.id,\n name: data.app.name,\n description: data.app.description ?? undefined,\n clientId: data.app.clientId,\n type: data.app.type as AuthApp[\"type\"],\n status: data.app.status as AuthApp[\"status\"],\n scopes: data.app.scopes ?? [],\n redirectUris: data.app.redirectUris ?? [],\n ownerUserId: data.app.ownerUserId,\n tenantId: data.app.tenantId,\n createdAt: data.app.createdAt,\n updatedAt: data.app.updatedAt,\n }\n : null;\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleCopy = async (text: string) => {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(text);\n void nativeNotify(ok ? \"success\" : \"error\");\n };\n\n const handleRotateSecret = async () => {\n if (!appId) return;\n setActionError(null);\n try {\n const result = await rotateSecret({ variables: { id: appId } });\n const secret = result.data?.rotateAppSecret?.clientSecret;\n if (secret) {\n setNewSecret(secret);\n setShowSecret(true);\n }\n setConfirmRotate(false);\n refetch();\n } catch (err) {\n setActionError(\n errorMessage(\n err,\n tr(\n \"devportal.oauthActionFailed\",\n \"That did not work. Nothing was changed. Please try again.\",\n ),\n ),\n );\n }\n };\n\n const handleDeactivate = async () => {\n if (!appId) return;\n setActionError(null);\n try {\n await deactivateApp({ variables: { id: appId } });\n setConfirmDelete(false);\n handleNavigate(\"/oauth\");\n } catch (err) {\n setActionError(\n errorMessage(\n err,\n tr(\n \"devportal.oauthActionFailed\",\n \"That did not work. Nothing was changed. Please try again.\",\n ),\n ),\n );\n }\n };\n\n const handleToggleStatus = async () => {\n if (!appId || !app) return;\n setActionError(null);\n const newStatus = app.status === \"ACTIVE\" ? \"INACTIVE\" : \"ACTIVE\";\n try {\n await updateApp({\n variables: { id: appId, input: { status: newStatus } },\n });\n refetch();\n } catch (err) {\n setActionError(\n errorMessage(\n err,\n tr(\n \"devportal.oauthActionFailed\",\n \"That did not work. Nothing was changed. Please try again.\",\n ),\n ),\n );\n }\n };\n\n if (loading && !data) {\n return (\n <PageLayout\n title={tr(\"devportal.appDetails\", \"App Details\")}\n description={tr(\"devportal.loadingDots\", \"Loading...\")}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n >\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-text-secondary\" />\n </div>\n </PageLayout>\n );\n }\n\n if (error || !app) {\n return (\n <PageLayout\n title={tr(\"devportal.appDetails\", \"App Details\")}\n description={tr(\"devportal.failedToLoad\", \"Failed to load\")}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n >\n <div className=\"rounded-lg border border-status-error-border/50 bg-status-error-bg/10 p-6 text-center\">\n <AlertCircle className=\"mx-auto h-8 w-8 text-status-error-text\" />\n <h3 className=\"mt-2 font-medium text-status-error-text\">\n {error\n ? tr(\"devportal.failedToLoadApp\", \"Failed to load app\")\n : tr(\"devportal.appNotFound\", \"App not found\")}\n </h3>\n {error && (\n <p className=\"mt-1 text-sm text-text-secondary\">\n {getErrorMessage(error)}\n </p>\n )}\n <button\n onClick={() => handleNavigate(\"/oauth\")}\n className=\"mt-4 inline-flex items-center gap-2 rounded-md bg-action-primary-bg px-4 py-2 text-sm font-medium text-action-primary-text hover:bg-action-primary-bg/90\"\n >\n <ArrowLeft size={16} />\n {tr(\"devportal.backToApps\", \"Back to Apps\")}\n </button>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard authOnly>\n <PageLayout\n title={app.name}\n description={\n app.description ||\n tr(\n \"devportal.oauthAppDetailsAndCredentials\",\n \"OAuth App details and credentials\",\n )\n }\n purpose={tr(\n \"devportal.oauthAppDetailPurpose\",\n \"These are this OAuth app's credentials and the scopes it may request. Copy the client ID for your integration, rotate the secret if it leaks, and keep scopes minimal so users only grant what the app truly needs.\",\n )}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n actions={\n <CTAOverflowMenu\n primary={{\n label:\n app.status === \"ACTIVE\"\n ? tr(\"devportal.deactivate\", \"Deactivate\")\n : tr(\"devportal.activate\", \"Activate\"),\n onSelect: handleToggleStatus,\n }}\n actions={[\n {\n label: isFluidgrids\n ? tr(\"devportal.deactivate\", \"Deactivate\")\n : tr(\"devportal.delete\", \"Delete\"),\n icon: <Trash2 size={16} />,\n intent: \"destructive\",\n onSelect: () => setConfirmDelete(true),\n },\n ]}\n />\n }\n >\n {actionError && (\n <div\n role=\"alert\"\n className=\"mb-4 rounded-md bg-status-error-bg px-3 py-2 text-sm text-status-error-text\"\n >\n {actionError}\n </div>\n )}\n {/* Status & Type */}\n <div className=\"grid gap-6 md:grid-cols-2\">\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-4\">\n {tr(\"devportal.statusAndConfiguration\", \"Status & Configuration\")}\n </h3>\n <div className=\"space-y-3\">\n <div className=\"flex justify-between\">\n <span className=\"text-sm text-text-secondary\">\n {tr(\"devportal.status\", \"Status\")}\n </span>\n <div className=\"flex items-center gap-2\">\n <ActiveIndicator active={app.status === \"ACTIVE\"} />\n <span className=\"text-sm font-medium\">{app.status}</span>\n </div>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-sm text-text-secondary\">\n {tr(\"devportal.type\", \"Type\")}\n </span>\n <span\n className={cn(\n \"rounded px-2 py-0.5 text-xs font-medium\",\n app.type === \"SERVICE\"\n ? \"bg-accent-purple-subtle text-accent-purple\"\n : app.type === \"INTEGRATION\" ||\n app.type === \"API_INTEGRATION\"\n ? \"bg-status-info-bg-subtle text-status-info-text\"\n : \"bg-status-success-bg-subtle text-status-success-text\",\n )}\n >\n {app.type}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-sm text-text-secondary\">\n {tr(\"devportal.created\", \"Created\")}\n </span>\n <span className=\"text-sm\">\n {new Date(app.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n </div>\n\n {/* Credentials — primary emphasis zone */}\n <GlassCard treatment=\"glass\" glow className=\"p-6\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-4\">\n {tr(\"devportal.credentials\", \"Credentials\")}\n </h3>\n <div className=\"space-y-3\">\n <div>\n <label className=\"text-xs text-text-secondary\">\n {tr(\"devportal.clientId\", \"Client ID\")}\n </label>\n <div className=\"mt-1 flex items-center gap-2\">\n <code className=\"flex-1 rounded bg-bg-sunken px-3 py-1.5 text-sm font-mono\">\n {app.clientId}\n </code>\n <button\n onClick={() => handleCopy(app.clientId)}\n className=\"rounded p-1.5 hover:bg-bg-sunken transition-colors\"\n title=\"Copy\"\n >\n <Copy size={14} />\n </button>\n </div>\n </div>\n {newSecret && (\n <div>\n <label className=\"text-xs text-status-error-text font-medium\">\n {tr(\n \"devportal.newClientSecretShownOnce\",\n \"New Client Secret (shown only once!)\",\n )}\n </label>\n <div className=\"mt-1 flex items-center gap-2\">\n <code className=\"flex-1 rounded bg-status-error-bg/10 border border-status-error-border/30 px-3 py-1.5 text-sm font-mono\">\n {showSecret\n ? newSecret\n : \"\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\\u2022\"}\n </code>\n <button\n onClick={() => setShowSecret(!showSecret)}\n className=\"rounded p-1.5 hover:bg-bg-sunken transition-colors\"\n >\n {showSecret ? <EyeOff size={14} /> : <Eye size={14} />}\n </button>\n <button\n onClick={() => handleCopy(newSecret)}\n className=\"rounded p-1.5 hover:bg-bg-sunken transition-colors\"\n >\n <Copy size={14} />\n </button>\n </div>\n </div>\n )}\n <button\n onClick={() => setConfirmRotate(true)}\n disabled={rotating}\n className=\"mt-2 inline-flex items-center gap-2 rounded-md bg-status-warning-bg-subtle px-3 py-1.5 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg-subtle transition-colors disabled:opacity-50\"\n >\n <RefreshCw\n size={14}\n className={rotating ? \"animate-spin\" : \"\"}\n />\n {tr(\"devportal.rotateSecret\", \"Rotate Secret\")}\n </button>\n </div>\n </GlassCard>\n </div>\n\n {/* Scopes */}\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-3\">\n {tr(\"devportal.scopes\", \"Scopes\")} ({app.scopes.length})\n </h3>\n {app.scopes.length > 0 ? (\n <div className=\"flex flex-wrap gap-2\">\n {app.scopes.map((scope) => (\n <span\n key={scope}\n className=\"rounded-full bg-action-primary-bg/10 px-3 py-1 text-xs font-medium text-primary\"\n >\n {scope}\n </span>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr(\"devportal.noScopesConfigured\", \"No scopes configured\")}\n </p>\n )}\n </div>\n\n {/* Redirect URIs */}\n <div className=\"rounded-card border border-border-seam bg-bg-surface p-6\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-3\">\n {tr(\"devportal.redirectUris\", \"Redirect URIs\")} (\n {app.redirectUris.length})\n </h3>\n {app.redirectUris.length > 0 ? (\n <div className=\"space-y-2\">\n {app.redirectUris.map((uri) => (\n <div key={uri} className=\"flex items-center gap-2\">\n <code className=\"rounded bg-bg-sunken px-3 py-1.5 text-sm font-mono\">\n {uri}\n </code>\n <button\n onClick={() => handleCopy(uri)}\n className=\"rounded p-1 hover:bg-bg-sunken transition-colors\"\n >\n <Copy size={12} />\n </button>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.noRedirectUrisConfigured\",\n \"No redirect URIs configured\",\n )}\n </p>\n )}\n </div>\n\n {/* Confirm Rotate Dialog */}\n <AlertDialog open={confirmRotate} onOpenChange={setConfirmRotate}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {tr(\n \"devportal.rotateClientSecretQuestion\",\n \"Rotate Client Secret?\",\n )}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {tr(\n \"devportal.rotateSecretWarning\",\n \"This will invalidate the current client secret. All integrations using the old secret will stop working immediately.\",\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={rotating}>\n {tr(\"devportal.cancel\", \"Cancel\")}\n </AlertDialogCancel>\n <AlertDialogAction\n onClick={(e) => {\n e.preventDefault();\n void handleRotateSecret();\n }}\n disabled={rotating}\n className=\"bg-status-warning-bg text-white hover:bg-status-warning-bg/90\"\n >\n {rotating\n ? tr(\"devportal.rotating\", \"Rotating...\")\n : tr(\"devportal.rotateSecret\", \"Rotate Secret\")}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {/* Confirm Deactivate Dialog */}\n <AlertDialog open={confirmDelete} onOpenChange={setConfirmDelete}>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle className=\"text-status-error-text\">\n {tr(\"devportal.deactivateAppQuestion\", \"Deactivate App?\")}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {tr(\n \"devportal.deactivateAppWarning\",\n \"This will deactivate the app and revoke all tokens. This action cannot be undone easily.\",\n )}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={deactivating}>\n {tr(\"devportal.cancel\", \"Cancel\")}\n </AlertDialogCancel>\n <AlertDialogAction\n onClick={(e) => {\n e.preventDefault();\n void handleDeactivate();\n }}\n disabled={deactivating}\n className=\"bg-status-error-bg text-status-error-text hover:bg-status-error-bg/90\"\n >\n {deactivating\n ? tr(\"devportal.deactivating\", \"Deactivating...\")\n : tr(\"devportal.deactivate\", \"Deactivate\")}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}