@burdenoff/microfe-devportal 2026.527.1 → 2026.530.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devportal/components/EmptyState.js +9 -23
- package/dist/devportal/components/EmptyState.js.map +1 -1
- package/dist/devportal/components/PageLayout.js +5 -2
- package/dist/devportal/components/PageLayout.js.map +1 -1
- package/dist/devportal/components/TelemetryErrorBoundary.js +37 -17
- package/dist/devportal/components/TelemetryErrorBoundary.js.map +1 -1
- package/dist/devportal/hooks/useGraphQL.js +2 -9
- package/dist/devportal/hooks/useGraphQL.js.map +1 -1
- package/dist/devportal/pages/ApiKeysPage.js +1 -1
- package/dist/devportal/pages/ApiKeysPage.js.map +1 -1
- package/dist/devportal/pages/CreateApiKeyPage.js +78 -73
- package/dist/devportal/pages/CreateApiKeyPage.js.map +1 -1
- package/dist/devportal/pages/CreateApplicationPage.js +1 -1
- package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
- package/dist/devportal/pages/CreateOAuthClientPage.js +135 -131
- package/dist/devportal/pages/CreateOAuthClientPage.js.map +1 -1
- package/dist/devportal/pages/CreateOrganizationPage.js +1 -1
- package/dist/devportal/pages/CreateOrganizationPage.js.map +1 -1
- package/dist/devportal/pages/DashboardPage.js +36 -28
- package/dist/devportal/pages/DashboardPage.js.map +1 -1
- package/dist/devportal/pages/EditApplicationPage.js +3 -3
- package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js +1 -1
- package/dist/devportal/pages/OAuthClientsPage.js.map +1 -1
- package/dist/devportal/pages/SettingsPage.js +10 -10
- package/dist/devportal/pages/SettingsPage.js.map +1 -1
- package/package.json +3 -2
|
@@ -13,68 +13,69 @@ import { useState as m } from "react";
|
|
|
13
13
|
import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
14
14
|
import { Plus as g, Save as _, Trash2 as v, X as y } from "lucide-react";
|
|
15
15
|
import { jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
16
|
+
import { OptionalFields as S } from "@burdenoff/fe-libs/ui";
|
|
16
17
|
//#region src/devportal/pages/CreateOAuthClientPage.tsx
|
|
17
|
-
var
|
|
18
|
-
let { navigate:
|
|
19
|
-
let n =
|
|
18
|
+
var C = () => {
|
|
19
|
+
let { navigate: C, basePath: w } = e(), { t: T } = h(), E = (e, t) => {
|
|
20
|
+
let n = T(e);
|
|
20
21
|
return n === e ? t : n;
|
|
21
|
-
}, [
|
|
22
|
-
|
|
23
|
-
},
|
|
24
|
-
let e =
|
|
25
|
-
e && !
|
|
26
|
-
},
|
|
27
|
-
N
|
|
28
|
-
},
|
|
29
|
-
let e =
|
|
30
|
-
e && !
|
|
31
|
-
},
|
|
32
|
-
L
|
|
22
|
+
}, [D, O] = m(""), [k, A] = m(""), [j, M] = m("CONFIDENTIAL"), [N, P] = m([]), [F, I] = m(""), [L, R] = m([]), [z, B] = m(""), [V, H] = m(null), [U, W] = m(null), [G, { loading: K, error: q }] = u(), J = (e) => {
|
|
23
|
+
C?.(`${w}${e}`);
|
|
24
|
+
}, Y = () => {
|
|
25
|
+
let e = F.trim();
|
|
26
|
+
e && !N.includes(e) && (P([...N, e]), I(""));
|
|
27
|
+
}, X = (e) => {
|
|
28
|
+
P(N.filter((t) => t !== e));
|
|
29
|
+
}, Z = () => {
|
|
30
|
+
let e = z.trim();
|
|
31
|
+
e && !L.includes(e) && (R([...L, e]), B(""));
|
|
32
|
+
}, Q = (e) => {
|
|
33
|
+
R(L.filter((t) => t !== e));
|
|
33
34
|
};
|
|
34
|
-
return
|
|
35
|
-
title:
|
|
36
|
-
description:
|
|
35
|
+
return V ? /* @__PURE__ */ b(n, {
|
|
36
|
+
title: E("devportal.appCreated", "App Created!"),
|
|
37
|
+
description: E("devportal.appCreatedDescription", "Save the client secret below. It will not be shown again."),
|
|
37
38
|
backLink: "/oauth",
|
|
38
|
-
backLabel:
|
|
39
|
+
backLabel: E("devportal.backToApps", "Back to Apps"),
|
|
39
40
|
children: /* @__PURE__ */ x("div", {
|
|
40
41
|
className: "rounded-lg border border-border-subtle bg-status-success-bg-subtle p-6",
|
|
41
42
|
children: [
|
|
42
43
|
/* @__PURE__ */ b("h3", {
|
|
43
44
|
className: "font-medium text-status-success-text",
|
|
44
|
-
children:
|
|
45
|
+
children: E("devportal.oauthAppCreated", "Your OAuth App has been created")
|
|
45
46
|
}),
|
|
46
47
|
/* @__PURE__ */ x("div", {
|
|
47
48
|
className: "mt-4 space-y-3",
|
|
48
49
|
children: [/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("label", {
|
|
49
50
|
className: "text-xs font-medium text-status-success-text",
|
|
50
|
-
children:
|
|
51
|
+
children: E("devportal.clientSecret", "Client Secret")
|
|
51
52
|
}), /* @__PURE__ */ x("div", {
|
|
52
53
|
className: "mt-1 flex items-center gap-2",
|
|
53
54
|
children: [/* @__PURE__ */ b("code", {
|
|
54
|
-
className: "flex-1 rounded border bg-
|
|
55
|
-
children:
|
|
55
|
+
className: "flex-1 rounded border bg-bg-surface px-3 py-2 text-sm font-mono break-all",
|
|
56
|
+
children: V
|
|
56
57
|
}), /* @__PURE__ */ b("button", {
|
|
57
58
|
onClick: async () => {
|
|
58
|
-
a("light"), o(await i(
|
|
59
|
+
a("light"), o(await i(V) ? "success" : "error");
|
|
59
60
|
},
|
|
60
61
|
className: "rounded bg-status-success-bg-subtle px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg-subtle transition-colors",
|
|
61
|
-
children:
|
|
62
|
+
children: E("devportal.copy", "Copy")
|
|
62
63
|
})]
|
|
63
64
|
})] }), /* @__PURE__ */ b("p", {
|
|
64
65
|
className: "text-sm text-status-success-text",
|
|
65
|
-
children:
|
|
66
|
+
children: E("devportal.storeSecretSecurely", "Store this secret securely. You will not be able to see it again.")
|
|
66
67
|
})]
|
|
67
68
|
}),
|
|
68
69
|
/* @__PURE__ */ x("div", {
|
|
69
70
|
className: "mt-6 flex gap-3",
|
|
70
|
-
children: [
|
|
71
|
-
onClick: () =>
|
|
71
|
+
children: [U && /* @__PURE__ */ b("button", {
|
|
72
|
+
onClick: () => J(`/oauth/${U}`),
|
|
72
73
|
className: "rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors",
|
|
73
|
-
children:
|
|
74
|
+
children: E("devportal.viewAppDetails", "View App Details")
|
|
74
75
|
}), /* @__PURE__ */ b("button", {
|
|
75
|
-
onClick: () =>
|
|
76
|
+
onClick: () => J("/oauth"),
|
|
76
77
|
className: "rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted transition-colors",
|
|
77
|
-
children:
|
|
78
|
+
children: E("devportal.backToApps", "Back to Apps")
|
|
78
79
|
})]
|
|
79
80
|
})
|
|
80
81
|
]
|
|
@@ -82,162 +83,165 @@ var S = () => {
|
|
|
82
83
|
}) : /* @__PURE__ */ b(d, {
|
|
83
84
|
permission: f.APP_CREATE,
|
|
84
85
|
children: /* @__PURE__ */ b(n, {
|
|
85
|
-
title:
|
|
86
|
-
description:
|
|
86
|
+
title: E("devportal.createOAuthApp", "Create OAuth App"),
|
|
87
|
+
description: E("devportal.createOAuthAppDescription", "Register a new OAuth application for authentication"),
|
|
87
88
|
backLink: "/oauth",
|
|
88
|
-
backLabel:
|
|
89
|
+
backLabel: E("devportal.backToApps", "Back to Apps"),
|
|
89
90
|
children: /* @__PURE__ */ x("form", {
|
|
90
91
|
onSubmit: async (e) => {
|
|
91
|
-
if (e.preventDefault(),
|
|
92
|
-
let e = await
|
|
93
|
-
name:
|
|
94
|
-
description:
|
|
95
|
-
type:
|
|
96
|
-
scopes:
|
|
97
|
-
redirectUris:
|
|
92
|
+
if (e.preventDefault(), D.trim()) try {
|
|
93
|
+
let e = await G({ variables: { input: {
|
|
94
|
+
name: D.trim(),
|
|
95
|
+
description: k.trim() || void 0,
|
|
96
|
+
type: j,
|
|
97
|
+
scopes: N,
|
|
98
|
+
redirectUris: L
|
|
98
99
|
} } }), t = e.data?.createApp?.clientSecret, n = e.data?.createApp?.app?.id;
|
|
99
|
-
t && (
|
|
100
|
+
t && (H(t), W(n || null));
|
|
100
101
|
} catch {}
|
|
101
102
|
},
|
|
102
103
|
className: "space-y-6 max-w-2xl",
|
|
103
104
|
children: [
|
|
104
|
-
|
|
105
|
+
q && /* @__PURE__ */ b("div", {
|
|
105
106
|
className: "rounded-lg border border-destructive/50 bg-destructive/10 p-4",
|
|
106
107
|
children: /* @__PURE__ */ b("p", {
|
|
107
108
|
className: "text-sm text-destructive",
|
|
108
|
-
children: p(
|
|
109
|
+
children: p(q)
|
|
109
110
|
})
|
|
110
111
|
}),
|
|
111
112
|
/* @__PURE__ */ x(r, {
|
|
112
|
-
title:
|
|
113
|
+
title: E("devportal.basicInformation", "Basic Information"),
|
|
113
114
|
children: [
|
|
114
115
|
/* @__PURE__ */ b(s, {
|
|
115
|
-
label:
|
|
116
|
-
value:
|
|
117
|
-
onChange: (e) =>
|
|
118
|
-
placeholder:
|
|
116
|
+
label: E("devportal.appName", "App Name"),
|
|
117
|
+
value: D,
|
|
118
|
+
onChange: (e) => O(e.target.value),
|
|
119
|
+
placeholder: E("devportal.myOAuthAppPlaceholder", "My OAuth App"),
|
|
119
120
|
required: !0
|
|
120
121
|
}),
|
|
121
122
|
/* @__PURE__ */ b(l, {
|
|
122
|
-
label:
|
|
123
|
-
value:
|
|
124
|
-
onChange: (e) =>
|
|
125
|
-
placeholder:
|
|
123
|
+
label: E("devportal.description", "Description"),
|
|
124
|
+
value: k,
|
|
125
|
+
onChange: (e) => A(e.target.value),
|
|
126
|
+
placeholder: E("devportal.appDescriptionPlaceholder", "Brief description of what this app does...")
|
|
126
127
|
}),
|
|
127
128
|
/* @__PURE__ */ b(c, {
|
|
128
|
-
label:
|
|
129
|
-
value:
|
|
130
|
-
onChange: (e) =>
|
|
129
|
+
label: E("devportal.appType", "App Type"),
|
|
130
|
+
value: j,
|
|
131
|
+
onChange: (e) => M(e.target.value),
|
|
131
132
|
options: [
|
|
132
133
|
{
|
|
133
134
|
value: "SERVICE",
|
|
134
|
-
label:
|
|
135
|
+
label: E("devportal.appTypeService", "Service (M2M)")
|
|
135
136
|
},
|
|
136
137
|
{
|
|
137
138
|
value: "INTEGRATION",
|
|
138
|
-
label:
|
|
139
|
+
label: E("devportal.appTypeIntegration", "Integration")
|
|
139
140
|
},
|
|
140
141
|
{
|
|
141
142
|
value: "BOT",
|
|
142
|
-
label:
|
|
143
|
+
label: E("devportal.appTypeBot", "Bot")
|
|
143
144
|
},
|
|
144
145
|
{
|
|
145
146
|
value: "WEB_APP",
|
|
146
|
-
label:
|
|
147
|
+
label: E("devportal.appTypeWebApp", "Web App")
|
|
147
148
|
},
|
|
148
149
|
{
|
|
149
150
|
value: "AUTOMATION",
|
|
150
|
-
label:
|
|
151
|
+
label: E("devportal.appTypeAutomation", "Automation")
|
|
151
152
|
}
|
|
152
153
|
]
|
|
153
154
|
})
|
|
154
155
|
]
|
|
155
156
|
}),
|
|
156
|
-
/* @__PURE__ */
|
|
157
|
-
title:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
children: /* @__PURE__ */ b(g, { size: 16 })
|
|
176
|
-
})]
|
|
177
|
-
}), M.length > 0 && /* @__PURE__ */ b("div", {
|
|
178
|
-
className: "flex flex-wrap gap-2",
|
|
179
|
-
children: M.map((e) => /* @__PURE__ */ x("span", {
|
|
180
|
-
className: "inline-flex items-center gap-1 rounded-full bg-primary/10 px-3 py-1 text-xs font-medium text-primary",
|
|
181
|
-
children: [e, /* @__PURE__ */ b("button", {
|
|
157
|
+
/* @__PURE__ */ x(S, {
|
|
158
|
+
title: E("devportal.advancedOAuthSettings", "Advanced settings"),
|
|
159
|
+
description: E("devportal.advancedOAuthSettingsDescription", "Advanced grant types, PKCE-required toggles, scope tuning, and redirect URIs."),
|
|
160
|
+
children: [/* @__PURE__ */ b(r, {
|
|
161
|
+
title: E("devportal.scopes", "Scopes"),
|
|
162
|
+
children: /* @__PURE__ */ x("div", {
|
|
163
|
+
className: "space-y-3",
|
|
164
|
+
children: [/* @__PURE__ */ x("div", {
|
|
165
|
+
className: "flex items-center gap-2",
|
|
166
|
+
children: [/* @__PURE__ */ b("input", {
|
|
167
|
+
type: "text",
|
|
168
|
+
value: F,
|
|
169
|
+
onChange: (e) => I(e.target.value),
|
|
170
|
+
onKeyDown: (e) => {
|
|
171
|
+
e.key === "Enter" && (e.preventDefault(), Y());
|
|
172
|
+
},
|
|
173
|
+
placeholder: "e.g. workspace:read",
|
|
174
|
+
className: "flex-1 rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
|
175
|
+
}), /* @__PURE__ */ b("button", {
|
|
182
176
|
type: "button",
|
|
183
|
-
onClick:
|
|
184
|
-
className: "hover:
|
|
185
|
-
children: /* @__PURE__ */ b(
|
|
177
|
+
onClick: Y,
|
|
178
|
+
className: "rounded-md bg-primary/10 p-2 text-primary hover:bg-primary/20 transition-colors",
|
|
179
|
+
children: /* @__PURE__ */ b(g, { size: 16 })
|
|
186
180
|
})]
|
|
187
|
-
},
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
type: "url",
|
|
199
|
-
value: R,
|
|
200
|
-
onChange: (e) => z(e.target.value),
|
|
201
|
-
onKeyDown: (e) => {
|
|
202
|
-
e.key === "Enter" && (e.preventDefault(), X());
|
|
203
|
-
},
|
|
204
|
-
placeholder: "https://example.com/callback",
|
|
205
|
-
className: "flex-1 rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
|
206
|
-
}), /* @__PURE__ */ b("button", {
|
|
207
|
-
type: "button",
|
|
208
|
-
onClick: X,
|
|
209
|
-
className: "rounded-md bg-primary/10 p-2 text-primary hover:bg-primary/20 transition-colors",
|
|
210
|
-
children: /* @__PURE__ */ b(g, { size: 16 })
|
|
181
|
+
}), N.length > 0 && /* @__PURE__ */ b("div", {
|
|
182
|
+
className: "flex flex-wrap gap-2",
|
|
183
|
+
children: N.map((e) => /* @__PURE__ */ x("span", {
|
|
184
|
+
className: "inline-flex items-center gap-1 rounded-full bg-primary/10 px-3 py-1 text-xs font-medium text-primary",
|
|
185
|
+
children: [e, /* @__PURE__ */ b("button", {
|
|
186
|
+
type: "button",
|
|
187
|
+
onClick: () => X(e),
|
|
188
|
+
className: "hover:text-destructive",
|
|
189
|
+
children: /* @__PURE__ */ b(y, { size: 12 })
|
|
190
|
+
})]
|
|
191
|
+
}, e))
|
|
211
192
|
})]
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
|
|
193
|
+
})
|
|
194
|
+
}), /* @__PURE__ */ b(r, {
|
|
195
|
+
title: E("devportal.redirectUris", "Redirect URIs"),
|
|
196
|
+
children: /* @__PURE__ */ x("div", {
|
|
197
|
+
className: "space-y-3",
|
|
198
|
+
children: [/* @__PURE__ */ x("div", {
|
|
215
199
|
className: "flex items-center gap-2",
|
|
216
|
-
children: [/* @__PURE__ */ b("
|
|
217
|
-
|
|
218
|
-
|
|
200
|
+
children: [/* @__PURE__ */ b("input", {
|
|
201
|
+
type: "url",
|
|
202
|
+
value: z,
|
|
203
|
+
onChange: (e) => B(e.target.value),
|
|
204
|
+
onKeyDown: (e) => {
|
|
205
|
+
e.key === "Enter" && (e.preventDefault(), Z());
|
|
206
|
+
},
|
|
207
|
+
placeholder: "https://example.com/callback",
|
|
208
|
+
className: "flex-1 rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary"
|
|
219
209
|
}), /* @__PURE__ */ b("button", {
|
|
220
210
|
type: "button",
|
|
221
|
-
onClick:
|
|
222
|
-
className: "rounded p-
|
|
223
|
-
children: /* @__PURE__ */ b(
|
|
211
|
+
onClick: Z,
|
|
212
|
+
className: "rounded-md bg-primary/10 p-2 text-primary hover:bg-primary/20 transition-colors",
|
|
213
|
+
children: /* @__PURE__ */ b(g, { size: 16 })
|
|
224
214
|
})]
|
|
225
|
-
},
|
|
226
|
-
|
|
227
|
-
|
|
215
|
+
}), L.length > 0 && /* @__PURE__ */ b("div", {
|
|
216
|
+
className: "space-y-2",
|
|
217
|
+
children: L.map((e) => /* @__PURE__ */ x("div", {
|
|
218
|
+
className: "flex items-center gap-2",
|
|
219
|
+
children: [/* @__PURE__ */ b("code", {
|
|
220
|
+
className: "flex-1 rounded bg-muted px-3 py-1.5 text-sm font-mono",
|
|
221
|
+
children: e
|
|
222
|
+
}), /* @__PURE__ */ b("button", {
|
|
223
|
+
type: "button",
|
|
224
|
+
onClick: () => Q(e),
|
|
225
|
+
className: "rounded p-1 text-muted-foreground hover:text-destructive transition-colors",
|
|
226
|
+
children: /* @__PURE__ */ b(v, { size: 14 })
|
|
227
|
+
})]
|
|
228
|
+
}, e))
|
|
229
|
+
})]
|
|
230
|
+
})
|
|
231
|
+
})]
|
|
228
232
|
}),
|
|
229
233
|
/* @__PURE__ */ x("div", {
|
|
230
234
|
className: "flex items-center gap-3 pt-4 border-t",
|
|
231
235
|
children: [/* @__PURE__ */ x("button", {
|
|
232
236
|
type: "submit",
|
|
233
|
-
disabled:
|
|
237
|
+
disabled: K || !D.trim(),
|
|
234
238
|
className: t("inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors", "bg-primary text-primary-foreground hover:bg-primary/90", "disabled:opacity-50 disabled:cursor-not-allowed"),
|
|
235
|
-
children: [/* @__PURE__ */ b(_, { size: 16 }),
|
|
239
|
+
children: [/* @__PURE__ */ b(_, { size: 16 }), K ? E("devportal.creating", "Creating...") : E("devportal.createApp", "Create App")]
|
|
236
240
|
}), /* @__PURE__ */ x("button", {
|
|
237
241
|
type: "button",
|
|
238
|
-
onClick: () =>
|
|
242
|
+
onClick: () => J("/oauth"),
|
|
239
243
|
className: "inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted transition-colors",
|
|
240
|
-
children: [/* @__PURE__ */ b(y, { size: 16 }),
|
|
244
|
+
children: [/* @__PURE__ */ b(y, { size: 16 }), E("devportal.cancel", "Cancel")]
|
|
241
245
|
})]
|
|
242
246
|
})
|
|
243
247
|
]
|
|
@@ -246,6 +250,6 @@ var S = () => {
|
|
|
246
250
|
});
|
|
247
251
|
};
|
|
248
252
|
//#endregion
|
|
249
|
-
export {
|
|
253
|
+
export { C as CreateOAuthClientPage };
|
|
250
254
|
|
|
251
255
|
//# sourceMappingURL=CreateOAuthClientPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CreateOAuthClientPage.js","names":[],"sources":["../../../src/devportal/pages/CreateOAuthClientPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport { Save, X, Plus, Trash2 } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { PageLayout, PageSection } from \"../components/PageLayout\";\nimport {\n InputField,\n SelectField,\n TextareaField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { cn } from \"../utils/cn\";\nimport {\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n} from \"../../utils/nativeBridge\";\nimport { useCreateAuthAppMutation, getErrorMessage } from \"../hooks\";\n\n/**\n * Create OAuth App page\n *\n * Creates a new App in auth-svc (replaces devportal-svc CreateOAuthClient).\n * Shows the client secret once after creation.\n */\nexport const CreateOAuthClientPage: FC = () => {\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\n const [name, setName] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [type, setType] = useState<string>(\"CONFIDENTIAL\");\n const [scopes, setScopes] = useState<string[]>([]);\n const [newScope, setNewScope] = useState(\"\");\n const [redirectUris, setRedirectUris] = useState<string[]>([]);\n const [newUri, setNewUri] = useState(\"\");\n\n const [createdSecret, setCreatedSecret] = useState<string | null>(null);\n const [createdAppId, setCreatedAppId] = useState<string | null>(null);\n\n const [createApp, { loading, error }] = useCreateAuthAppMutation();\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleAddScope = () => {\n const trimmed = newScope.trim();\n if (trimmed && !scopes.includes(trimmed)) {\n setScopes([...scopes, trimmed]);\n setNewScope(\"\");\n }\n };\n\n const handleRemoveScope = (scope: string) => {\n setScopes(scopes.filter((s) => s !== scope));\n };\n\n const handleAddUri = () => {\n const trimmed = newUri.trim();\n if (trimmed && !redirectUris.includes(trimmed)) {\n setRedirectUris([...redirectUris, trimmed]);\n setNewUri(\"\");\n }\n };\n\n const handleRemoveUri = (uri: string) => {\n setRedirectUris(redirectUris.filter((u) => u !== uri));\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!name.trim()) return;\n\n try {\n const result = await createApp({\n variables: {\n input: {\n name: name.trim(),\n description: description.trim() || undefined,\n type,\n scopes,\n redirectUris,\n },\n },\n });\n\n const secret = result.data?.createApp?.clientSecret;\n const appId = result.data?.createApp?.app?.id;\n if (secret) {\n setCreatedSecret(secret);\n setCreatedAppId(appId || null);\n }\n } catch {\n // error handled by Apollo\n }\n };\n\n // Success — show the secret\n if (createdSecret) {\n return (\n <PageLayout\n title={tr(\"devportal.appCreated\", \"App Created!\")}\n description={tr(\n \"devportal.appCreatedDescription\",\n \"Save the client secret below. It will not be shown again.\",\n )}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n >\n <div className=\"rounded-lg border border-border-subtle bg-status-success-bg-subtle p-6\">\n <h3 className=\"font-medium text-status-success-text\">\n {tr(\"devportal.oauthAppCreated\", \"Your OAuth App has been created\")}\n </h3>\n <div className=\"mt-4 space-y-3\">\n <div>\n <label className=\"text-xs font-medium text-status-success-text\">\n {tr(\"devportal.clientSecret\", \"Client Secret\")}\n </label>\n <div className=\"mt-1 flex items-center gap-2\">\n <code className=\"flex-1 rounded border bg-white px-3 py-2 text-sm font-mono break-all\">\n {createdSecret}\n </code>\n <button\n onClick={async () => {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(createdSecret);\n void nativeNotify(ok ? \"success\" : \"error\");\n }}\n className=\"rounded bg-status-success-bg-subtle px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg-subtle transition-colors\"\n >\n {tr(\"devportal.copy\", \"Copy\")}\n </button>\n </div>\n </div>\n <p className=\"text-sm text-status-success-text\">\n {tr(\n \"devportal.storeSecretSecurely\",\n \"Store this secret securely. You will not be able to see it again.\",\n )}\n </p>\n </div>\n <div className=\"mt-6 flex gap-3\">\n {createdAppId && (\n <button\n onClick={() => handleNavigate(`/oauth/${createdAppId}`)}\n className=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n {tr(\"devportal.viewAppDetails\", \"View App Details\")}\n </button>\n )}\n <button\n onClick={() => handleNavigate(\"/oauth\")}\n className=\"rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted transition-colors\"\n >\n {tr(\"devportal.backToApps\", \"Back to Apps\")}\n </button>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_CREATE}>\n <PageLayout\n title={tr(\"devportal.createOAuthApp\", \"Create OAuth App\")}\n description={tr(\n \"devportal.createOAuthAppDescription\",\n \"Register a new OAuth application for authentication\",\n )}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n >\n <form onSubmit={handleSubmit} className=\"space-y-6 max-w-2xl\">\n {error && (\n <div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-4\">\n <p className=\"text-sm text-destructive\">\n {getErrorMessage(error)}\n </p>\n </div>\n )}\n\n <PageSection\n title={tr(\"devportal.basicInformation\", \"Basic Information\")}\n >\n <InputField\n label={tr(\"devportal.appName\", \"App Name\")}\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder={tr(\n \"devportal.myOAuthAppPlaceholder\",\n \"My OAuth App\",\n )}\n required\n />\n <TextareaField\n label={tr(\"devportal.description\", \"Description\")}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder={tr(\n \"devportal.appDescriptionPlaceholder\",\n \"Brief description of what this app does...\",\n )}\n />\n <SelectField\n label={tr(\"devportal.appType\", \"App Type\")}\n value={type}\n onChange={(e) => setType(e.target.value)}\n options={[\n {\n value: \"SERVICE\",\n label: tr(\"devportal.appTypeService\", \"Service (M2M)\"),\n },\n {\n value: \"INTEGRATION\",\n label: tr(\"devportal.appTypeIntegration\", \"Integration\"),\n },\n { value: \"BOT\", label: tr(\"devportal.appTypeBot\", \"Bot\") },\n {\n value: \"WEB_APP\",\n label: tr(\"devportal.appTypeWebApp\", \"Web App\"),\n },\n {\n value: \"AUTOMATION\",\n label: tr(\"devportal.appTypeAutomation\", \"Automation\"),\n },\n ]}\n />\n </PageSection>\n\n <PageSection title={tr(\"devportal.scopes\", \"Scopes\")}>\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <input\n type=\"text\"\n value={newScope}\n onChange={(e) => setNewScope(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n handleAddScope();\n }\n }}\n placeholder=\"e.g. workspace:read\"\n className=\"flex-1 rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n <button\n type=\"button\"\n onClick={handleAddScope}\n className=\"rounded-md bg-primary/10 p-2 text-primary hover:bg-primary/20 transition-colors\"\n >\n <Plus size={16} />\n </button>\n </div>\n {scopes.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {scopes.map((scope) => (\n <span\n key={scope}\n className=\"inline-flex items-center gap-1 rounded-full bg-primary/10 px-3 py-1 text-xs font-medium text-primary\"\n >\n {scope}\n <button\n type=\"button\"\n onClick={() => handleRemoveScope(scope)}\n className=\"hover:text-destructive\"\n >\n <X size={12} />\n </button>\n </span>\n ))}\n </div>\n )}\n </div>\n </PageSection>\n\n <PageSection title={tr(\"devportal.redirectUris\", \"Redirect URIs\")}>\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <input\n type=\"url\"\n value={newUri}\n onChange={(e) => setNewUri(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n handleAddUri();\n }\n }}\n placeholder=\"https://example.com/callback\"\n className=\"flex-1 rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n <button\n type=\"button\"\n onClick={handleAddUri}\n className=\"rounded-md bg-primary/10 p-2 text-primary hover:bg-primary/20 transition-colors\"\n >\n <Plus size={16} />\n </button>\n </div>\n {redirectUris.length > 0 && (\n <div className=\"space-y-2\">\n {redirectUris.map((uri) => (\n <div key={uri} className=\"flex items-center gap-2\">\n <code className=\"flex-1 rounded bg-muted px-3 py-1.5 text-sm font-mono\">\n {uri}\n </code>\n <button\n type=\"button\"\n onClick={() => handleRemoveUri(uri)}\n className=\"rounded p-1 text-muted-foreground hover:text-destructive transition-colors\"\n >\n <Trash2 size={14} />\n </button>\n </div>\n ))}\n </div>\n )}\n </div>\n </PageSection>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4 border-t\">\n <button\n type=\"submit\"\n disabled={loading || !name.trim()}\n className={cn(\n \"inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors\",\n \"bg-primary text-primary-foreground hover:bg-primary/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n )}\n >\n <Save size={16} />\n {loading\n ? tr(\"devportal.creating\", \"Creating...\")\n : tr(\"devportal.createApp\", \"Create App\")}\n </button>\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/oauth\")}\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 {tr(\"devportal.cancel\", \"Cancel\")}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CreateOAuthClientPage.js","names":[],"sources":["../../../src/devportal/pages/CreateOAuthClientPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport { Save, X, Plus, Trash2 } 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 {\n InputField,\n SelectField,\n TextareaField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { PermissionGuard } from \"../components\";\nimport { DEVPORTAL_PERMISSIONS } from \"../constants/permissions\";\nimport { cn } from \"../utils/cn\";\nimport {\n nativeCopyText,\n nativeImpact,\n nativeNotify,\n} from \"../../utils/nativeBridge\";\nimport { useCreateAuthAppMutation, getErrorMessage } from \"../hooks\";\n\n/**\n * Create OAuth App page\n *\n * Creates a new App in auth-svc (replaces devportal-svc CreateOAuthClient).\n * Shows the client secret once after creation.\n */\nexport const CreateOAuthClientPage: FC = () => {\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\n const [name, setName] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [type, setType] = useState<string>(\"CONFIDENTIAL\");\n const [scopes, setScopes] = useState<string[]>([]);\n const [newScope, setNewScope] = useState(\"\");\n const [redirectUris, setRedirectUris] = useState<string[]>([]);\n const [newUri, setNewUri] = useState(\"\");\n\n const [createdSecret, setCreatedSecret] = useState<string | null>(null);\n const [createdAppId, setCreatedAppId] = useState<string | null>(null);\n\n const [createApp, { loading, error }] = useCreateAuthAppMutation();\n\n const handleNavigate = (path: string) => {\n navigate?.(`${basePath}${path}`);\n };\n\n const handleAddScope = () => {\n const trimmed = newScope.trim();\n if (trimmed && !scopes.includes(trimmed)) {\n setScopes([...scopes, trimmed]);\n setNewScope(\"\");\n }\n };\n\n const handleRemoveScope = (scope: string) => {\n setScopes(scopes.filter((s) => s !== scope));\n };\n\n const handleAddUri = () => {\n const trimmed = newUri.trim();\n if (trimmed && !redirectUris.includes(trimmed)) {\n setRedirectUris([...redirectUris, trimmed]);\n setNewUri(\"\");\n }\n };\n\n const handleRemoveUri = (uri: string) => {\n setRedirectUris(redirectUris.filter((u) => u !== uri));\n };\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!name.trim()) return;\n\n try {\n const result = await createApp({\n variables: {\n input: {\n name: name.trim(),\n description: description.trim() || undefined,\n type,\n scopes,\n redirectUris,\n },\n },\n });\n\n const secret = result.data?.createApp?.clientSecret;\n const appId = result.data?.createApp?.app?.id;\n if (secret) {\n setCreatedSecret(secret);\n setCreatedAppId(appId || null);\n }\n } catch {\n // error handled by Apollo\n }\n };\n\n // Success — show the secret\n if (createdSecret) {\n return (\n <PageLayout\n title={tr(\"devportal.appCreated\", \"App Created!\")}\n description={tr(\n \"devportal.appCreatedDescription\",\n \"Save the client secret below. It will not be shown again.\",\n )}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n >\n <div className=\"rounded-lg border border-border-subtle bg-status-success-bg-subtle p-6\">\n <h3 className=\"font-medium text-status-success-text\">\n {tr(\"devportal.oauthAppCreated\", \"Your OAuth App has been created\")}\n </h3>\n <div className=\"mt-4 space-y-3\">\n <div>\n <label className=\"text-xs font-medium text-status-success-text\">\n {tr(\"devportal.clientSecret\", \"Client Secret\")}\n </label>\n <div className=\"mt-1 flex items-center gap-2\">\n <code className=\"flex-1 rounded border bg-bg-surface px-3 py-2 text-sm font-mono break-all\">\n {createdSecret}\n </code>\n <button\n onClick={async () => {\n void nativeImpact(\"light\");\n const ok = await nativeCopyText(createdSecret);\n void nativeNotify(ok ? \"success\" : \"error\");\n }}\n className=\"rounded bg-status-success-bg-subtle px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg-subtle transition-colors\"\n >\n {tr(\"devportal.copy\", \"Copy\")}\n </button>\n </div>\n </div>\n <p className=\"text-sm text-status-success-text\">\n {tr(\n \"devportal.storeSecretSecurely\",\n \"Store this secret securely. You will not be able to see it again.\",\n )}\n </p>\n </div>\n <div className=\"mt-6 flex gap-3\">\n {createdAppId && (\n <button\n onClick={() => handleNavigate(`/oauth/${createdAppId}`)}\n className=\"rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n {tr(\"devportal.viewAppDetails\", \"View App Details\")}\n </button>\n )}\n <button\n onClick={() => handleNavigate(\"/oauth\")}\n className=\"rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted transition-colors\"\n >\n {tr(\"devportal.backToApps\", \"Back to Apps\")}\n </button>\n </div>\n </div>\n </PageLayout>\n );\n }\n\n return (\n <PermissionGuard permission={DEVPORTAL_PERMISSIONS.APP_CREATE}>\n <PageLayout\n title={tr(\"devportal.createOAuthApp\", \"Create OAuth App\")}\n description={tr(\n \"devportal.createOAuthAppDescription\",\n \"Register a new OAuth application for authentication\",\n )}\n backLink=\"/oauth\"\n backLabel={tr(\"devportal.backToApps\", \"Back to Apps\")}\n >\n <form onSubmit={handleSubmit} className=\"space-y-6 max-w-2xl\">\n {error && (\n <div className=\"rounded-lg border border-destructive/50 bg-destructive/10 p-4\">\n <p className=\"text-sm text-destructive\">\n {getErrorMessage(error)}\n </p>\n </div>\n )}\n\n <PageSection\n title={tr(\"devportal.basicInformation\", \"Basic Information\")}\n >\n <InputField\n label={tr(\"devportal.appName\", \"App Name\")}\n value={name}\n onChange={(e) => setName(e.target.value)}\n placeholder={tr(\n \"devportal.myOAuthAppPlaceholder\",\n \"My OAuth App\",\n )}\n required\n />\n <TextareaField\n label={tr(\"devportal.description\", \"Description\")}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder={tr(\n \"devportal.appDescriptionPlaceholder\",\n \"Brief description of what this app does...\",\n )}\n />\n <SelectField\n label={tr(\"devportal.appType\", \"App Type\")}\n value={type}\n onChange={(e) => setType(e.target.value)}\n options={[\n {\n value: \"SERVICE\",\n label: tr(\"devportal.appTypeService\", \"Service (M2M)\"),\n },\n {\n value: \"INTEGRATION\",\n label: tr(\"devportal.appTypeIntegration\", \"Integration\"),\n },\n { value: \"BOT\", label: tr(\"devportal.appTypeBot\", \"Bot\") },\n {\n value: \"WEB_APP\",\n label: tr(\"devportal.appTypeWebApp\", \"Web App\"),\n },\n {\n value: \"AUTOMATION\",\n label: tr(\"devportal.appTypeAutomation\", \"Automation\"),\n },\n ]}\n />\n </PageSection>\n\n {/* Advanced: grant types / PKCE-required toggles / scope tuning / redirect URIs — collapse the long-tail knobs */}\n <OptionalFields\n title={tr(\"devportal.advancedOAuthSettings\", \"Advanced settings\")}\n description={tr(\n \"devportal.advancedOAuthSettingsDescription\",\n \"Advanced grant types, PKCE-required toggles, scope tuning, and redirect URIs.\",\n )}\n >\n <PageSection title={tr(\"devportal.scopes\", \"Scopes\")}>\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <input\n type=\"text\"\n value={newScope}\n onChange={(e) => setNewScope(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n handleAddScope();\n }\n }}\n placeholder=\"e.g. workspace:read\"\n className=\"flex-1 rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n <button\n type=\"button\"\n onClick={handleAddScope}\n className=\"rounded-md bg-primary/10 p-2 text-primary hover:bg-primary/20 transition-colors\"\n >\n <Plus size={16} />\n </button>\n </div>\n {scopes.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {scopes.map((scope) => (\n <span\n key={scope}\n className=\"inline-flex items-center gap-1 rounded-full bg-primary/10 px-3 py-1 text-xs font-medium text-primary\"\n >\n {scope}\n <button\n type=\"button\"\n onClick={() => handleRemoveScope(scope)}\n className=\"hover:text-destructive\"\n >\n <X size={12} />\n </button>\n </span>\n ))}\n </div>\n )}\n </div>\n </PageSection>\n\n <PageSection title={tr(\"devportal.redirectUris\", \"Redirect URIs\")}>\n <div className=\"space-y-3\">\n <div className=\"flex items-center gap-2\">\n <input\n type=\"url\"\n value={newUri}\n onChange={(e) => setNewUri(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n handleAddUri();\n }\n }}\n placeholder=\"https://example.com/callback\"\n className=\"flex-1 rounded-md border bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-primary\"\n />\n <button\n type=\"button\"\n onClick={handleAddUri}\n className=\"rounded-md bg-primary/10 p-2 text-primary hover:bg-primary/20 transition-colors\"\n >\n <Plus size={16} />\n </button>\n </div>\n {redirectUris.length > 0 && (\n <div className=\"space-y-2\">\n {redirectUris.map((uri) => (\n <div key={uri} className=\"flex items-center gap-2\">\n <code className=\"flex-1 rounded bg-muted px-3 py-1.5 text-sm font-mono\">\n {uri}\n </code>\n <button\n type=\"button\"\n onClick={() => handleRemoveUri(uri)}\n className=\"rounded p-1 text-muted-foreground hover:text-destructive transition-colors\"\n >\n <Trash2 size={14} />\n </button>\n </div>\n ))}\n </div>\n )}\n </div>\n </PageSection>\n </OptionalFields>\n\n {/* Actions */}\n <div className=\"flex items-center gap-3 pt-4 border-t\">\n <button\n type=\"submit\"\n disabled={loading || !name.trim()}\n className={cn(\n \"inline-flex items-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors\",\n \"bg-primary text-primary-foreground hover:bg-primary/90\",\n \"disabled:opacity-50 disabled:cursor-not-allowed\",\n )}\n >\n <Save size={16} />\n {loading\n ? tr(\"devportal.creating\", \"Creating...\")\n : tr(\"devportal.createApp\", \"Create App\")}\n </button>\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/oauth\")}\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 {tr(\"devportal.cancel\", \"Cancel\")}\n </button>\n </div>\n </form>\n </PageLayout>\n </PermissionGuard>\n );\n};\n"],"mappings":"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"}
|
|
@@ -331,7 +331,7 @@ var v = [
|
|
|
331
331
|
className: `h-4 w-4 rounded-full border-2 ${n ? "border-primary bg-primary" : "border-muted-foreground"}`,
|
|
332
332
|
children: n && /* @__PURE__ */ g("div", {
|
|
333
333
|
className: "h-full w-full flex items-center justify-center",
|
|
334
|
-
children: /* @__PURE__ */ g("div", { className: "h-1.5 w-1.5 rounded-full bg-
|
|
334
|
+
children: /* @__PURE__ */ g("div", { className: "h-1.5 w-1.5 rounded-full bg-bg-surface" })
|
|
335
335
|
})
|
|
336
336
|
})]
|
|
337
337
|
}), /* @__PURE__ */ _("div", {
|