@burdenoff/microfe-devportal 2026.713.9 → 2026.714.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,19 +1,16 @@
|
|
|
1
1
|
import { useDevportal as e } from "../providers/DevportalProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { InputField as o, SelectField as
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { useDevportalEventBus as n } from "../events/index.js";
|
|
4
|
+
import { useCreateApplicationMutation as r, useCreateBillingAccountMutation as i, useCreateOrganizationMutation as a } from "../../generated/global-operations.js";
|
|
5
|
+
import { InputField as o, SelectField as ee, TextareaField as s } from "../components/FormField.js";
|
|
6
6
|
import "../hooks/index.js";
|
|
7
|
-
import { useState as
|
|
8
|
-
import { useI18n as
|
|
9
|
-
import { AppWindow as u, ArrowRight as te, BookOpen as d, Building2 as f, Check as p, ChevronRight as m, Key as
|
|
10
|
-
import { jsx as
|
|
11
|
-
import { Illustration as
|
|
7
|
+
import { useState as c } from "react";
|
|
8
|
+
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
9
|
+
import { AppWindow as u, ArrowRight as te, BookOpen as d, Box as ne, Building2 as f, Check as p, ChevronRight as m, GitFork as re, Key as h, Puzzle as g, Rocket as _ } from "lucide-react";
|
|
10
|
+
import { jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
11
|
+
import { Illustration as ie, PagePurpose as ae } from "@burdenoff/fe-libs/ui";
|
|
12
12
|
//#region src/devportal/pages/SetupPage.tsx
|
|
13
|
-
var
|
|
14
|
-
value: e,
|
|
15
|
-
label: t
|
|
16
|
-
})), ie = [
|
|
13
|
+
var b = [
|
|
17
14
|
{
|
|
18
15
|
value: "productivity",
|
|
19
16
|
label: "Productivity"
|
|
@@ -63,15 +60,15 @@ var y = Object.entries(a).map(([e, t]) => ({
|
|
|
63
60
|
label: "Other"
|
|
64
61
|
}
|
|
65
62
|
];
|
|
66
|
-
function
|
|
63
|
+
function x(e) {
|
|
67
64
|
return e.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
68
65
|
}
|
|
69
|
-
function
|
|
66
|
+
function S() {
|
|
70
67
|
try {
|
|
71
68
|
localStorage.setItem("devportal_setup_dismissed", "1");
|
|
72
69
|
} catch {}
|
|
73
70
|
}
|
|
74
|
-
var
|
|
71
|
+
var C = [
|
|
75
72
|
{
|
|
76
73
|
key: "org",
|
|
77
74
|
label: "Organization"
|
|
@@ -84,273 +81,317 @@ var S = [
|
|
|
84
81
|
key: "done",
|
|
85
82
|
label: "Done"
|
|
86
83
|
}
|
|
87
|
-
],
|
|
88
|
-
let t =
|
|
89
|
-
return /* @__PURE__ */
|
|
84
|
+
], oe = ({ current: e }) => {
|
|
85
|
+
let t = C.findIndex((t) => t.key === e);
|
|
86
|
+
return /* @__PURE__ */ v("div", {
|
|
90
87
|
className: "flex items-center gap-0",
|
|
91
|
-
children:
|
|
88
|
+
children: C.map((e, n) => {
|
|
92
89
|
let r = n < t, i = n === t;
|
|
93
|
-
return /* @__PURE__ */
|
|
90
|
+
return /* @__PURE__ */ y("div", {
|
|
94
91
|
className: "flex items-center",
|
|
95
|
-
children: [/* @__PURE__ */
|
|
92
|
+
children: [/* @__PURE__ */ y("div", {
|
|
96
93
|
className: "flex flex-col items-center gap-1.5",
|
|
97
|
-
children: [/* @__PURE__ */
|
|
94
|
+
children: [/* @__PURE__ */ v("div", {
|
|
98
95
|
className: ["flex h-8 w-8 items-center justify-center rounded-full text-sm font-semibold transition-colors", r ? "bg-action-primary-bg text-action-primary-text" : i ? "bg-action-primary-bg text-action-primary-text ring-4 ring-primary/20" : "bg-bg-sunken text-text-secondary"].join(" "),
|
|
99
|
-
children: r ? /* @__PURE__ */
|
|
100
|
-
}), /* @__PURE__ */
|
|
96
|
+
children: r ? /* @__PURE__ */ v(p, { size: 14 }) : n + 1
|
|
97
|
+
}), /* @__PURE__ */ v("span", {
|
|
101
98
|
className: ["text-xs font-medium whitespace-nowrap", i ? "text-text-primary" : "text-text-secondary"].join(" "),
|
|
102
99
|
children: e.label
|
|
103
100
|
})]
|
|
104
|
-
}), n <
|
|
101
|
+
}), n < C.length - 1 && /* @__PURE__ */ v("div", { className: ["mx-3 mb-5 h-px w-16 flex-shrink-0 transition-colors", n < t ? "bg-action-primary-bg" : "bg-border"].join(" ") })]
|
|
105
102
|
}, e.key);
|
|
106
103
|
})
|
|
107
104
|
});
|
|
108
|
-
},
|
|
109
|
-
let { navigate:
|
|
110
|
-
let n =
|
|
105
|
+
}, w = () => {
|
|
106
|
+
let { navigate: t, basePath: p, currentUser: C } = e(), { t: w } = l(), D = n(), O = (e, t) => {
|
|
107
|
+
let n = w(e);
|
|
111
108
|
return n === e ? t : n;
|
|
112
|
-
},
|
|
109
|
+
}, k = (e) => t?.(`${p}${e}`), [A, j] = c("org"), [M, se] = c(null), [N, ce] = c(null), [P, le] = c(""), [F, I] = c(""), [L, R] = c(""), [z, B] = c({}), [V, { loading: H }] = a(), [U] = i(), ue = () => {
|
|
113
110
|
let e = {};
|
|
114
|
-
return
|
|
115
|
-
},
|
|
116
|
-
if (
|
|
117
|
-
let e = (await
|
|
118
|
-
name:
|
|
119
|
-
slug:
|
|
120
|
-
description:
|
|
111
|
+
return P.trim() ? P.length < 2 && (e.name = O("devportal.orgNameTooShort", "Name must be at least 2 characters")) : e.name = O("devportal.orgNameRequired", "Organization name is required"), F.trim() ? /^[a-z0-9-]+$/.test(F) || (e.slug = O("devportal.slugInvalid", "Lowercase letters, numbers, and hyphens only")) : e.slug = O("devportal.slugRequired", "Slug is required"), B(e), Object.keys(e).length === 0;
|
|
112
|
+
}, de = async () => {
|
|
113
|
+
if (ue()) try {
|
|
114
|
+
let e = (await V({ variables: { input: {
|
|
115
|
+
name: P,
|
|
116
|
+
slug: F || void 0,
|
|
117
|
+
description: L || void 0
|
|
121
118
|
} } })).data?.createDeveloperOrganization?.id;
|
|
122
119
|
if (e) {
|
|
123
|
-
|
|
120
|
+
D.emit("devportal.org.created", {
|
|
124
121
|
organizationId: e,
|
|
125
|
-
name:
|
|
126
|
-
}),
|
|
122
|
+
name: P
|
|
123
|
+
}), se(e);
|
|
127
124
|
try {
|
|
128
|
-
|
|
129
|
-
name:
|
|
130
|
-
email:
|
|
125
|
+
U({ variables: { input: {
|
|
126
|
+
name: P,
|
|
127
|
+
email: C?.email ?? ""
|
|
131
128
|
} } }).catch(() => {});
|
|
132
129
|
} catch {}
|
|
133
|
-
|
|
130
|
+
j("app");
|
|
134
131
|
}
|
|
135
132
|
} catch {}
|
|
136
|
-
}, [
|
|
133
|
+
}, [W, fe] = c(""), [G, pe] = c(""), [K, q] = c(null), [J, me] = c(""), [Y, he] = c(""), [X, Z] = c({}), [ge, { loading: Q }] = r(), _e = () => K === "node" ? "NODE" : K === "workflow" ? "WORKFLOW" : "API_INTEGRATION", ve = () => {
|
|
137
134
|
let e = {};
|
|
138
|
-
return
|
|
139
|
-
},
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
135
|
+
return W.trim() ? W.length < 3 && (e.name = O("devportal.nameTooShort", "Name must be at least 3 characters")) : e.name = O("devportal.appNameRequired", "Application name is required"), K || (e.builderType = O("devportal.appTypeRequired", "Application type is required")), J || (e.category = O("devportal.categoryRequired", "Category is required")), Y.trim() || (e.shortDescription = O("devportal.shortDescriptionRequired", "Short description is required")), Z(e), Object.keys(e).length === 0;
|
|
136
|
+
}, ye = async () => {
|
|
137
|
+
if (!ve()) return;
|
|
138
|
+
let e = _e();
|
|
139
|
+
try {
|
|
140
|
+
let t = (await ge({ variables: { input: {
|
|
141
|
+
name: W,
|
|
142
|
+
slug: G || void 0,
|
|
143
|
+
appType: e,
|
|
145
144
|
category: J || void 0,
|
|
146
145
|
shortDescription: Y || void 0,
|
|
147
146
|
visibility: "PRIVATE",
|
|
148
|
-
developerOrganizationId:
|
|
147
|
+
developerOrganizationId: M || void 0
|
|
149
148
|
} } })).data?.createApplication?.id;
|
|
150
|
-
|
|
151
|
-
appId:
|
|
152
|
-
name:
|
|
153
|
-
appType:
|
|
154
|
-
organizationId:
|
|
155
|
-
}),
|
|
149
|
+
t && (D.emit("devportal.app.created", {
|
|
150
|
+
appId: t,
|
|
151
|
+
name: W,
|
|
152
|
+
appType: e,
|
|
153
|
+
organizationId: M
|
|
154
|
+
}), ce(t), S(), j("done"));
|
|
156
155
|
} catch {}
|
|
157
156
|
}, $ = () => {
|
|
158
|
-
|
|
157
|
+
S(), k("/");
|
|
159
158
|
};
|
|
160
|
-
return /* @__PURE__ */
|
|
159
|
+
return /* @__PURE__ */ y("main", {
|
|
161
160
|
className: "min-h-screen bg-surface-default",
|
|
162
|
-
children: [/* @__PURE__ */
|
|
161
|
+
children: [/* @__PURE__ */ v("div", {
|
|
163
162
|
className: "border-b border-border-default bg-surface-raised",
|
|
164
|
-
children: /* @__PURE__ */
|
|
163
|
+
children: /* @__PURE__ */ v("div", {
|
|
165
164
|
className: "mx-auto max-w-2xl px-6 py-5",
|
|
166
|
-
children: /* @__PURE__ */
|
|
165
|
+
children: /* @__PURE__ */ y("div", {
|
|
167
166
|
className: "flex items-center justify-between",
|
|
168
|
-
children: [/* @__PURE__ */
|
|
167
|
+
children: [/* @__PURE__ */ y("div", {
|
|
169
168
|
className: "flex items-center gap-3",
|
|
170
|
-
children: [/* @__PURE__ */
|
|
169
|
+
children: [/* @__PURE__ */ v("div", {
|
|
171
170
|
className: "flex h-9 w-9 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
|
|
172
|
-
children: /* @__PURE__ */
|
|
173
|
-
}), /* @__PURE__ */
|
|
171
|
+
children: /* @__PURE__ */ v(_, { size: 20 })
|
|
172
|
+
}), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
174
173
|
className: "text-xs font-medium text-text-secondary",
|
|
175
|
-
children:
|
|
176
|
-
}), /* @__PURE__ */
|
|
174
|
+
children: O("devportal.developerPortal", "Developer Portal")
|
|
175
|
+
}), /* @__PURE__ */ v("h1", {
|
|
177
176
|
className: "text-base font-semibold text-text-primary",
|
|
178
|
-
children:
|
|
177
|
+
children: O("devportal.setup.title", "Get set up")
|
|
179
178
|
})] })]
|
|
180
|
-
}),
|
|
179
|
+
}), A !== "done" && /* @__PURE__ */ v("button", {
|
|
181
180
|
type: "button",
|
|
182
181
|
onClick: $,
|
|
183
182
|
className: "text-sm text-text-secondary hover:text-text-primary transition-colors",
|
|
184
|
-
children:
|
|
183
|
+
children: O("devportal.setup.skipForNow", "Skip for now")
|
|
185
184
|
})]
|
|
186
185
|
})
|
|
187
186
|
})
|
|
188
|
-
}), /* @__PURE__ */
|
|
187
|
+
}), /* @__PURE__ */ y("div", {
|
|
189
188
|
className: "mx-auto max-w-2xl px-6 py-10",
|
|
190
189
|
children: [
|
|
191
|
-
/* @__PURE__ */
|
|
190
|
+
/* @__PURE__ */ v("div", {
|
|
192
191
|
className: "mb-10 flex justify-center",
|
|
193
|
-
children: /* @__PURE__ */
|
|
192
|
+
children: /* @__PURE__ */ v(oe, { current: A })
|
|
194
193
|
}),
|
|
195
|
-
|
|
194
|
+
A === "org" && /* @__PURE__ */ y("div", {
|
|
196
195
|
className: "mb-8 flex flex-col items-center gap-3 text-center",
|
|
197
|
-
children: [/* @__PURE__ */
|
|
196
|
+
children: [/* @__PURE__ */ v(ie, {
|
|
198
197
|
name: "onboarding",
|
|
199
|
-
alt:
|
|
198
|
+
alt: O("devportal.setup.illustrationAlt", "Getting started with the developer portal"),
|
|
200
199
|
size: "md"
|
|
201
|
-
}), /* @__PURE__ */
|
|
200
|
+
}), /* @__PURE__ */ v(ae, {
|
|
202
201
|
className: "justify-center",
|
|
203
|
-
children:
|
|
202
|
+
children: O("devportal.setup.purpose", "Two quick steps to start building: create the organization that owns your apps, then register your first app. You can change everything later — this just gets you to a working starting point.")
|
|
204
203
|
})]
|
|
205
204
|
}),
|
|
206
|
-
|
|
205
|
+
A === "org" && /* @__PURE__ */ y("div", {
|
|
207
206
|
className: "rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm",
|
|
208
207
|
children: [
|
|
209
|
-
/* @__PURE__ */
|
|
208
|
+
/* @__PURE__ */ y("div", {
|
|
210
209
|
className: "mb-6 flex items-center gap-3",
|
|
211
|
-
children: [/* @__PURE__ */
|
|
210
|
+
children: [/* @__PURE__ */ v("div", {
|
|
212
211
|
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
|
|
213
|
-
children: /* @__PURE__ */
|
|
214
|
-
}), /* @__PURE__ */
|
|
212
|
+
children: /* @__PURE__ */ v(f, { size: 20 })
|
|
213
|
+
}), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("h2", {
|
|
215
214
|
className: "text-lg font-semibold text-text-primary",
|
|
216
|
-
children:
|
|
217
|
-
}), /* @__PURE__ */
|
|
215
|
+
children: O("devportal.setup.orgTitle", "Set up your organization")
|
|
216
|
+
}), /* @__PURE__ */ v("p", {
|
|
218
217
|
className: "text-sm text-text-secondary",
|
|
219
|
-
children:
|
|
218
|
+
children: O("devportal.setup.orgSubtitle", "This groups your apps and teams under one publisher identity.")
|
|
220
219
|
})] })]
|
|
221
220
|
}),
|
|
222
|
-
/* @__PURE__ */
|
|
221
|
+
/* @__PURE__ */ y("div", {
|
|
223
222
|
className: "space-y-5",
|
|
224
223
|
children: [
|
|
225
|
-
/* @__PURE__ */
|
|
226
|
-
label:
|
|
227
|
-
value:
|
|
224
|
+
/* @__PURE__ */ v(o, {
|
|
225
|
+
label: O("devportal.organizationName", "Organization Name"),
|
|
226
|
+
value: P,
|
|
228
227
|
onChange: (e) => {
|
|
229
|
-
|
|
228
|
+
le(e.target.value), (!F || F === x(P)) && I(x(e.target.value)), B((e) => ({
|
|
230
229
|
...e,
|
|
231
230
|
name: void 0
|
|
232
231
|
}));
|
|
233
232
|
},
|
|
234
|
-
placeholder:
|
|
233
|
+
placeholder: O("devportal.organizationNamePlaceholder", "Acme Development"),
|
|
235
234
|
required: !0,
|
|
236
|
-
error:
|
|
235
|
+
error: z.name
|
|
237
236
|
}),
|
|
238
|
-
/* @__PURE__ */
|
|
239
|
-
label:
|
|
240
|
-
value:
|
|
237
|
+
/* @__PURE__ */ v(o, {
|
|
238
|
+
label: O("devportal.slug", "Slug"),
|
|
239
|
+
value: F,
|
|
241
240
|
onChange: (e) => {
|
|
242
|
-
|
|
241
|
+
I(e.target.value.toLowerCase()), B((e) => ({
|
|
243
242
|
...e,
|
|
244
243
|
slug: void 0
|
|
245
244
|
}));
|
|
246
245
|
},
|
|
247
246
|
placeholder: "acme-dev",
|
|
248
247
|
required: !0,
|
|
249
|
-
error:
|
|
250
|
-
hint:
|
|
248
|
+
error: z.slug,
|
|
249
|
+
hint: O("devportal.slugHint", "URL-friendly identifier — auto-generated from name")
|
|
251
250
|
}),
|
|
252
|
-
/* @__PURE__ */
|
|
253
|
-
label:
|
|
254
|
-
value:
|
|
255
|
-
onChange: (e) =>
|
|
256
|
-
placeholder:
|
|
251
|
+
/* @__PURE__ */ v(s, {
|
|
252
|
+
label: O("devportal.description", "Description"),
|
|
253
|
+
value: L,
|
|
254
|
+
onChange: (e) => R(e.target.value),
|
|
255
|
+
placeholder: O("devportal.orgDescriptionPlaceholder", "Tell us about your organization…"),
|
|
257
256
|
rows: 2
|
|
258
257
|
})
|
|
259
258
|
]
|
|
260
259
|
}),
|
|
261
|
-
/* @__PURE__ */
|
|
260
|
+
/* @__PURE__ */ y("div", {
|
|
262
261
|
className: "mt-8 flex items-center justify-between border-t border-border-default pt-6",
|
|
263
|
-
children: [/* @__PURE__ */
|
|
262
|
+
children: [/* @__PURE__ */ v("button", {
|
|
264
263
|
type: "button",
|
|
265
264
|
onClick: $,
|
|
266
265
|
className: "text-sm text-text-secondary hover:text-text-primary transition-colors",
|
|
267
|
-
children:
|
|
268
|
-
}), /* @__PURE__ */
|
|
266
|
+
children: O("devportal.setup.skipForNow", "Skip for now")
|
|
267
|
+
}), /* @__PURE__ */ y("button", {
|
|
269
268
|
type: "button",
|
|
270
|
-
onClick: () => void
|
|
271
|
-
disabled:
|
|
269
|
+
onClick: () => void de(),
|
|
270
|
+
disabled: H,
|
|
272
271
|
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2.5 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-60 transition-colors",
|
|
273
272
|
children: [
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
!
|
|
273
|
+
H ? /* @__PURE__ */ v("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" }) : null,
|
|
274
|
+
O("devportal.setup.createAndContinue", "Create & Continue"),
|
|
275
|
+
!H && /* @__PURE__ */ v(m, { size: 16 })
|
|
277
276
|
]
|
|
278
277
|
})]
|
|
279
278
|
})
|
|
280
279
|
]
|
|
281
280
|
}),
|
|
282
|
-
|
|
281
|
+
A === "app" && /* @__PURE__ */ y("div", {
|
|
283
282
|
className: "rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm",
|
|
284
283
|
children: [
|
|
285
|
-
/* @__PURE__ */
|
|
284
|
+
/* @__PURE__ */ y("div", {
|
|
286
285
|
className: "mb-6 flex items-center gap-3",
|
|
287
|
-
children: [/* @__PURE__ */
|
|
286
|
+
children: [/* @__PURE__ */ v("div", {
|
|
288
287
|
className: "flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary",
|
|
289
|
-
children: /* @__PURE__ */
|
|
290
|
-
}), /* @__PURE__ */
|
|
288
|
+
children: /* @__PURE__ */ v(u, { size: 20 })
|
|
289
|
+
}), /* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("h2", {
|
|
291
290
|
className: "text-lg font-semibold text-text-primary",
|
|
292
|
-
children:
|
|
293
|
-
}), /* @__PURE__ */
|
|
291
|
+
children: O("devportal.setup.appTitle", "Create your first app")
|
|
292
|
+
}), /* @__PURE__ */ v("p", {
|
|
294
293
|
className: "text-sm text-text-secondary",
|
|
295
|
-
children:
|
|
294
|
+
children: O("devportal.setup.appSubtitle", "Register the app you want to publish or integrate.")
|
|
296
295
|
})] })]
|
|
297
296
|
}),
|
|
298
|
-
/* @__PURE__ */
|
|
297
|
+
/* @__PURE__ */ y("div", {
|
|
299
298
|
className: "space-y-5",
|
|
300
299
|
children: [
|
|
301
|
-
/* @__PURE__ */
|
|
302
|
-
label:
|
|
303
|
-
value:
|
|
300
|
+
/* @__PURE__ */ v(o, {
|
|
301
|
+
label: O("devportal.applicationName", "Application Name"),
|
|
302
|
+
value: W,
|
|
304
303
|
onChange: (e) => {
|
|
305
|
-
|
|
304
|
+
fe(e.target.value), (!G || G === x(W)) && pe(x(e.target.value)), Z((e) => ({
|
|
306
305
|
...e,
|
|
307
306
|
name: void 0
|
|
308
307
|
}));
|
|
309
308
|
},
|
|
310
|
-
placeholder:
|
|
309
|
+
placeholder: O("devportal.applicationNamePlaceholder", "My Awesome App"),
|
|
311
310
|
required: !0,
|
|
312
311
|
error: X.name
|
|
313
312
|
}),
|
|
314
|
-
/* @__PURE__ */
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
313
|
+
/* @__PURE__ */ y("div", { children: [
|
|
314
|
+
/* @__PURE__ */ y("p", {
|
|
315
|
+
className: "mb-2 text-sm font-medium text-text-primary",
|
|
316
|
+
children: [
|
|
317
|
+
O("devportal.applicationType", "Application Type"),
|
|
318
|
+
" ",
|
|
319
|
+
/* @__PURE__ */ v("span", {
|
|
320
|
+
className: "text-status-error-text",
|
|
321
|
+
children: "*"
|
|
322
|
+
})
|
|
323
|
+
]
|
|
324
|
+
}),
|
|
325
|
+
/* @__PURE__ */ y("div", {
|
|
326
|
+
className: "grid grid-cols-3 gap-3",
|
|
327
|
+
children: [
|
|
328
|
+
/* @__PURE__ */ v(T, {
|
|
329
|
+
icon: ne,
|
|
330
|
+
title: "App",
|
|
331
|
+
description: "OAuth integration for the platform",
|
|
332
|
+
selected: K === "app",
|
|
333
|
+
onClick: () => {
|
|
334
|
+
q("app"), Z((e) => ({
|
|
335
|
+
...e,
|
|
336
|
+
builderType: void 0
|
|
337
|
+
}));
|
|
338
|
+
}
|
|
339
|
+
}),
|
|
340
|
+
/* @__PURE__ */ v(T, {
|
|
341
|
+
icon: g,
|
|
342
|
+
title: "Node",
|
|
343
|
+
description: "Custom FluidGrids workflow node",
|
|
344
|
+
selected: K === "node",
|
|
345
|
+
onClick: () => {
|
|
346
|
+
q("node"), Z((e) => ({
|
|
347
|
+
...e,
|
|
348
|
+
builderType: void 0
|
|
349
|
+
}));
|
|
350
|
+
}
|
|
351
|
+
}),
|
|
352
|
+
/* @__PURE__ */ v(T, {
|
|
353
|
+
icon: re,
|
|
354
|
+
title: "Workflow",
|
|
355
|
+
description: "Reusable automation template",
|
|
356
|
+
selected: K === "workflow",
|
|
357
|
+
onClick: () => {
|
|
358
|
+
q("workflow"), Z((e) => ({
|
|
359
|
+
...e,
|
|
360
|
+
builderType: void 0
|
|
361
|
+
}));
|
|
362
|
+
}
|
|
363
|
+
})
|
|
364
|
+
]
|
|
365
|
+
}),
|
|
366
|
+
X.builderType && /* @__PURE__ */ v("p", {
|
|
367
|
+
className: "mt-1.5 text-xs text-status-error-text",
|
|
368
|
+
children: X.builderType
|
|
369
|
+
})
|
|
370
|
+
] }),
|
|
371
|
+
/* @__PURE__ */ v(ee, {
|
|
372
|
+
label: O("devportal.category", "Category"),
|
|
373
|
+
value: J,
|
|
374
|
+
onChange: (e) => {
|
|
375
|
+
me(e.target.value), Z((e) => ({
|
|
376
|
+
...e,
|
|
377
|
+
category: void 0
|
|
378
|
+
}));
|
|
379
|
+
},
|
|
380
|
+
options: b,
|
|
381
|
+
placeholder: O("devportal.selectCategory", "Select category…"),
|
|
382
|
+
required: !0,
|
|
383
|
+
error: X.category
|
|
343
384
|
}),
|
|
344
|
-
/* @__PURE__ */
|
|
345
|
-
label:
|
|
385
|
+
/* @__PURE__ */ v(s, {
|
|
386
|
+
label: O("devportal.shortDescription", "Short Description"),
|
|
346
387
|
value: Y,
|
|
347
388
|
onChange: (e) => {
|
|
348
|
-
|
|
389
|
+
he(e.target.value), Z((e) => ({
|
|
349
390
|
...e,
|
|
350
391
|
shortDescription: void 0
|
|
351
392
|
}));
|
|
352
393
|
},
|
|
353
|
-
placeholder:
|
|
394
|
+
placeholder: O("devportal.shortDescriptionPlaceholder", "A brief description of what your app does"),
|
|
354
395
|
required: !0,
|
|
355
396
|
error: X.shortDescription,
|
|
356
397
|
rows: 2,
|
|
@@ -358,109 +399,133 @@ var S = [
|
|
|
358
399
|
})
|
|
359
400
|
]
|
|
360
401
|
}),
|
|
361
|
-
/* @__PURE__ */
|
|
402
|
+
/* @__PURE__ */ y("div", {
|
|
362
403
|
className: "mt-8 flex items-center justify-between border-t border-border-default pt-6",
|
|
363
|
-
children: [/* @__PURE__ */
|
|
404
|
+
children: [/* @__PURE__ */ v("button", {
|
|
364
405
|
type: "button",
|
|
365
406
|
onClick: $,
|
|
366
407
|
className: "text-sm text-text-secondary hover:text-text-primary transition-colors",
|
|
367
|
-
children:
|
|
368
|
-
}), /* @__PURE__ */
|
|
408
|
+
children: O("devportal.setup.skipForNow", "Skip for now")
|
|
409
|
+
}), /* @__PURE__ */ y("button", {
|
|
369
410
|
type: "button",
|
|
370
|
-
onClick: () => void
|
|
411
|
+
onClick: () => void ye(),
|
|
371
412
|
disabled: Q,
|
|
372
413
|
className: "inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2.5 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-60 transition-colors",
|
|
373
414
|
children: [
|
|
374
|
-
Q ? /* @__PURE__ */
|
|
375
|
-
|
|
376
|
-
!Q && /* @__PURE__ */
|
|
415
|
+
Q ? /* @__PURE__ */ v("span", { className: "h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent" }) : null,
|
|
416
|
+
O("devportal.setup.createApp", "Create App"),
|
|
417
|
+
!Q && /* @__PURE__ */ v(m, { size: 16 })
|
|
377
418
|
]
|
|
378
419
|
})]
|
|
379
420
|
})
|
|
380
421
|
]
|
|
381
422
|
}),
|
|
382
|
-
|
|
423
|
+
A === "done" && /* @__PURE__ */ y("div", {
|
|
383
424
|
className: "text-center",
|
|
384
425
|
children: [
|
|
385
|
-
/* @__PURE__ */
|
|
426
|
+
/* @__PURE__ */ v("div", {
|
|
386
427
|
className: "mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-action-primary-bg/10 text-primary",
|
|
387
|
-
children: /* @__PURE__ */
|
|
428
|
+
children: /* @__PURE__ */ v(_, { size: 32 })
|
|
388
429
|
}),
|
|
389
|
-
/* @__PURE__ */
|
|
430
|
+
/* @__PURE__ */ v("h2", {
|
|
390
431
|
className: "text-2xl font-bold text-text-primary",
|
|
391
|
-
children:
|
|
432
|
+
children: O("devportal.setup.doneTitle", "You're all set!")
|
|
392
433
|
}),
|
|
393
|
-
/* @__PURE__ */
|
|
434
|
+
/* @__PURE__ */ v("p", {
|
|
394
435
|
className: "mt-2 text-text-secondary",
|
|
395
|
-
children:
|
|
436
|
+
children: O("devportal.setup.doneSubtitle", "Your organization and first app are ready. Here's what to do next.")
|
|
396
437
|
}),
|
|
397
|
-
/* @__PURE__ */
|
|
438
|
+
/* @__PURE__ */ y("div", {
|
|
398
439
|
className: "mt-8 grid gap-3 sm:grid-cols-2",
|
|
399
440
|
children: [
|
|
400
|
-
|
|
401
|
-
icon: /* @__PURE__ */
|
|
402
|
-
title:
|
|
403
|
-
description:
|
|
404
|
-
onClick: () =>
|
|
441
|
+
N && /* @__PURE__ */ v(E, {
|
|
442
|
+
icon: /* @__PURE__ */ v(u, { size: 20 }),
|
|
443
|
+
title: O("devportal.setup.viewApp", "View your app"),
|
|
444
|
+
description: O("devportal.setup.viewAppDesc", "Configure OAuth, webhooks, and versioning"),
|
|
445
|
+
onClick: () => k(`/apps/${N}`),
|
|
405
446
|
primary: !0
|
|
406
447
|
}),
|
|
407
|
-
/* @__PURE__ */
|
|
408
|
-
icon: /* @__PURE__ */
|
|
409
|
-
title:
|
|
410
|
-
description:
|
|
411
|
-
onClick: () =>
|
|
448
|
+
/* @__PURE__ */ v(E, {
|
|
449
|
+
icon: /* @__PURE__ */ v(h, { size: 20 }),
|
|
450
|
+
title: O("devportal.setup.generateApiKey", "Generate API key"),
|
|
451
|
+
description: O("devportal.setup.generateApiKeyDesc", "Create credentials to start calling APIs"),
|
|
452
|
+
onClick: () => k("/api-keys/new")
|
|
412
453
|
}),
|
|
413
|
-
/* @__PURE__ */
|
|
414
|
-
icon: /* @__PURE__ */
|
|
415
|
-
title:
|
|
416
|
-
description:
|
|
417
|
-
onClick: () =>
|
|
454
|
+
/* @__PURE__ */ v(E, {
|
|
455
|
+
icon: /* @__PURE__ */ v(d, { size: 20 }),
|
|
456
|
+
title: O("devportal.setup.browseDocs", "Browse docs"),
|
|
457
|
+
description: O("devportal.setup.browseDocsDesc", "Learn about publishing, SDKs, and webhooks"),
|
|
458
|
+
onClick: () => k("/docs")
|
|
418
459
|
}),
|
|
419
|
-
|
|
420
|
-
icon: /* @__PURE__ */
|
|
421
|
-
title:
|
|
422
|
-
description:
|
|
423
|
-
onClick: () =>
|
|
460
|
+
M && /* @__PURE__ */ v(E, {
|
|
461
|
+
icon: /* @__PURE__ */ v(f, { size: 20 }),
|
|
462
|
+
title: O("devportal.setup.inviteTeam", "Invite your team"),
|
|
463
|
+
description: O("devportal.setup.inviteTeamDesc", "Add members to collaborate on your apps"),
|
|
464
|
+
onClick: () => k(`/organizations/${M}/members`)
|
|
424
465
|
})
|
|
425
466
|
]
|
|
426
467
|
}),
|
|
427
|
-
/* @__PURE__ */
|
|
468
|
+
/* @__PURE__ */ y("button", {
|
|
428
469
|
type: "button",
|
|
429
|
-
onClick: () =>
|
|
470
|
+
onClick: () => k("/"),
|
|
430
471
|
className: "mt-8 inline-flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary transition-colors",
|
|
431
|
-
children: [
|
|
472
|
+
children: [O("devportal.setup.goToDashboard", "Go to dashboard"), /* @__PURE__ */ v(te, { size: 14 })]
|
|
432
473
|
})
|
|
433
474
|
]
|
|
434
475
|
})
|
|
435
476
|
]
|
|
436
477
|
})]
|
|
437
478
|
});
|
|
438
|
-
},
|
|
479
|
+
}, T = ({ icon: e, title: n, description: r, selected: i, onClick: a }) => /* @__PURE__ */ y("button", {
|
|
480
|
+
type: "button",
|
|
481
|
+
onClick: a,
|
|
482
|
+
className: t("relative flex flex-col gap-2 rounded-xl border-2 p-4 text-left transition-all hover:border-primary/50 hover:shadow-sm", i ? "border-primary bg-action-primary-bg/5 shadow-sm" : "border-border-subtle bg-bg-surface"),
|
|
483
|
+
children: [
|
|
484
|
+
i && /* @__PURE__ */ v("span", {
|
|
485
|
+
className: "absolute top-3 right-3 flex h-4 w-4 items-center justify-center rounded-full bg-action-primary-bg text-action-primary-text",
|
|
486
|
+
children: /* @__PURE__ */ v(p, { size: 10 })
|
|
487
|
+
}),
|
|
488
|
+
/* @__PURE__ */ v("div", {
|
|
489
|
+
className: "flex h-8 w-8 items-center justify-center rounded-lg bg-bg-sunken",
|
|
490
|
+
children: /* @__PURE__ */ v(e, {
|
|
491
|
+
size: 18,
|
|
492
|
+
className: "text-text-primary"
|
|
493
|
+
})
|
|
494
|
+
}),
|
|
495
|
+
/* @__PURE__ */ y("div", { children: [/* @__PURE__ */ v("p", {
|
|
496
|
+
className: "text-sm font-semibold text-text-primary",
|
|
497
|
+
children: n
|
|
498
|
+
}), /* @__PURE__ */ v("p", {
|
|
499
|
+
className: "mt-0.5 text-xs text-text-secondary",
|
|
500
|
+
children: r
|
|
501
|
+
})] })
|
|
502
|
+
]
|
|
503
|
+
}), E = ({ icon: e, title: t, description: n, onClick: r, primary: i }) => /* @__PURE__ */ y("button", {
|
|
439
504
|
type: "button",
|
|
440
505
|
onClick: r,
|
|
441
506
|
className: ["flex items-start gap-4 rounded-xl border p-5 text-left transition-colors", i ? "border-primary bg-action-primary-bg/5 hover:bg-action-primary-bg/10" : "border-border-default bg-surface-raised hover:bg-surface-hover"].join(" "),
|
|
442
507
|
children: [
|
|
443
|
-
/* @__PURE__ */
|
|
508
|
+
/* @__PURE__ */ v("div", {
|
|
444
509
|
className: ["flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg", i ? "bg-action-primary-bg/20 text-primary" : "bg-bg-sunken text-text-secondary"].join(" "),
|
|
445
510
|
children: e
|
|
446
511
|
}),
|
|
447
|
-
/* @__PURE__ */
|
|
512
|
+
/* @__PURE__ */ y("div", {
|
|
448
513
|
className: "min-w-0 flex-1",
|
|
449
|
-
children: [/* @__PURE__ */
|
|
514
|
+
children: [/* @__PURE__ */ v("p", {
|
|
450
515
|
className: "font-semibold text-text-primary",
|
|
451
516
|
children: t
|
|
452
|
-
}), /* @__PURE__ */
|
|
517
|
+
}), /* @__PURE__ */ v("p", {
|
|
453
518
|
className: "mt-0.5 text-sm text-text-secondary",
|
|
454
519
|
children: n
|
|
455
520
|
})]
|
|
456
521
|
}),
|
|
457
|
-
/* @__PURE__ */
|
|
522
|
+
/* @__PURE__ */ v(m, {
|
|
458
523
|
size: 16,
|
|
459
524
|
className: "mt-0.5 flex-shrink-0 text-text-secondary"
|
|
460
525
|
})
|
|
461
526
|
]
|
|
462
527
|
});
|
|
463
528
|
//#endregion
|
|
464
|
-
export {
|
|
529
|
+
export { w as SetupPage };
|
|
465
530
|
|
|
466
531
|
//# sourceMappingURL=SetupPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SetupPage.js","names":[],"sources":["../../../src/devportal/pages/SetupPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport {\n ArrowRight,\n Building2,\n AppWindow,\n Rocket,\n Check,\n Key,\n BookOpen,\n ChevronRight,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { Illustration, PagePurpose } from \"@burdenoff/fe-libs/ui\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { useDevportalEventBus } from \"../events\";\nimport type { AppType } from \"../types\";\nimport { APP_TYPE_LABELS } from \"../utils/constants\";\nimport {\n useCreateOrganizationMutation,\n useCreateApplicationMutation,\n} from \"../../generated/global-operations\";\nimport { useCreateBillingAccountMutation } from \"../hooks\";\n\ntype SetupStep = \"org\" | \"app\" | \"done\";\n\nconst APP_TYPE_OPTIONS = Object.entries(APP_TYPE_LABELS).map(\n ([value, label]) => ({\n value,\n label,\n }),\n);\n\nconst CATEGORY_OPTIONS = [\n { value: \"productivity\", label: \"Productivity\" },\n { value: \"communication\", label: \"Communication\" },\n { value: \"analytics\", label: \"Analytics\" },\n { value: \"automation\", label: \"Automation\" },\n { value: \"developer-tools\", label: \"Developer Tools\" },\n { value: \"finance\", label: \"Finance\" },\n { value: \"hr\", label: \"HR & People\" },\n { value: \"marketing\", label: \"Marketing\" },\n { value: \"project-management\", label: \"Project Management\" },\n { value: \"security\", label: \"Security\" },\n { value: \"support\", label: \"Support\" },\n { value: \"other\", label: \"Other\" },\n];\n\nfunction generateSlug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/** Mark that the user has interacted with setup so the dashboard gate won't redirect again. */\nfunction markSetupSeen() {\n try {\n localStorage.setItem(\"devportal_setup_dismissed\", \"1\");\n } catch {\n // localStorage unavailable — not critical\n }\n}\n\n// ─── Step indicator ────────────────────────────────────────────────────────\n\nconst STEPS: { key: SetupStep; label: string }[] = [\n { key: \"org\", label: \"Organization\" },\n { key: \"app\", label: \"First app\" },\n { key: \"done\", label: \"Done\" },\n];\n\nconst StepIndicator: FC<{ current: SetupStep }> = ({ current }) => {\n const currentIdx = STEPS.findIndex((s) => s.key === current);\n return (\n <div className=\"flex items-center gap-0\">\n {STEPS.map((step, idx) => {\n const isCompleted = idx < currentIdx;\n const isActive = idx === currentIdx;\n return (\n <div key={step.key} className=\"flex items-center\">\n <div className=\"flex flex-col items-center gap-1.5\">\n <div\n className={[\n \"flex h-8 w-8 items-center justify-center rounded-full text-sm font-semibold transition-colors\",\n isCompleted\n ? \"bg-action-primary-bg text-action-primary-text\"\n : isActive\n ? \"bg-action-primary-bg text-action-primary-text ring-4 ring-primary/20\"\n : \"bg-bg-sunken text-text-secondary\",\n ].join(\" \")}\n >\n {isCompleted ? <Check size={14} /> : idx + 1}\n </div>\n <span\n className={[\n \"text-xs font-medium whitespace-nowrap\",\n isActive ? \"text-text-primary\" : \"text-text-secondary\",\n ].join(\" \")}\n >\n {step.label}\n </span>\n </div>\n {idx < STEPS.length - 1 && (\n <div\n className={[\n \"mx-3 mb-5 h-px w-16 flex-shrink-0 transition-colors\",\n idx < currentIdx ? \"bg-action-primary-bg\" : \"bg-border\",\n ].join(\" \")}\n />\n )}\n </div>\n );\n })}\n </div>\n );\n};\n\n// ─── Main component ────────────────────────────────────────────────────────\n\nexport const SetupPage: FC = () => {\n const { navigate, basePath, currentUser } = useDevportal();\n const { t } = useI18n();\n const bus = useDevportalEventBus();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const handleNavigate = (path: string) => navigate?.(`${basePath}${path}`);\n\n const [step, setStep] = useState<SetupStep>(\"org\");\n\n // IDs created during wizard — carried forward for success CTAs\n const [createdOrgId, setCreatedOrgId] = useState<string | null>(null);\n const [createdAppId, setCreatedAppId] = useState<string | null>(null);\n\n // ── Step 1: org ──────────────────────────────────────────────\n const [orgName, setOrgName] = useState(\"\");\n const [orgSlug, setOrgSlug] = useState(\"\");\n const [orgDescription, setOrgDescription] = useState(\"\");\n const [orgErrors, setOrgErrors] = useState<{ name?: string; slug?: string }>(\n {},\n );\n\n const [createOrg, { loading: creatingOrg }] = useCreateOrganizationMutation();\n const [createBillingAccountMutation] = useCreateBillingAccountMutation();\n\n const validateOrg = () => {\n const errs: { name?: string; slug?: string } = {};\n if (!orgName.trim())\n errs.name = tr(\n \"devportal.orgNameRequired\",\n \"Organization name is required\",\n );\n else if (orgName.length < 2)\n errs.name = tr(\n \"devportal.orgNameTooShort\",\n \"Name must be at least 2 characters\",\n );\n if (!orgSlug.trim())\n errs.slug = tr(\"devportal.slugRequired\", \"Slug is required\");\n else if (!/^[a-z0-9-]+$/.test(orgSlug))\n errs.slug = tr(\n \"devportal.slugInvalid\",\n \"Lowercase letters, numbers, and hyphens only\",\n );\n setOrgErrors(errs);\n return Object.keys(errs).length === 0;\n };\n\n const handleOrgSubmit = async () => {\n if (!validateOrg()) return;\n try {\n const result = await createOrg({\n variables: {\n input: {\n name: orgName,\n slug: orgSlug || undefined,\n description: orgDescription || undefined,\n },\n },\n });\n const id = result.data?.createDeveloperOrganization?.id;\n if (id) {\n bus.emit(\"devportal.org.created\", {\n organizationId: id,\n name: orgName,\n });\n setCreatedOrgId(id);\n // Fire-and-forget: create a billing account for the new org\n try {\n createBillingAccountMutation({\n variables: {\n input: {\n name: orgName,\n email: currentUser?.email ?? \"\",\n },\n },\n }).catch(() => {});\n } catch {\n // silent failure — billing account creation is non-blocking\n }\n setStep(\"app\");\n }\n } catch {\n // error surfaced via Apollo cache; leave the form open\n }\n };\n\n // ── Step 2: app ──────────────────────────────────────────────\n const [appName, setAppName] = useState(\"\");\n const [appSlug, setAppSlug] = useState(\"\");\n const [appType, setAppType] = useState<AppType | \"\">(\"\");\n const [appCategory, setAppCategory] = useState(\"\");\n const [appShortDesc, setAppShortDesc] = useState(\"\");\n const [appErrors, setAppErrors] = useState<{\n name?: string;\n appType?: string;\n category?: string;\n shortDescription?: string;\n }>({});\n\n const [createApp, { loading: creatingApp }] = useCreateApplicationMutation();\n\n const validateApp = () => {\n const errs: typeof appErrors = {};\n if (!appName.trim())\n errs.name = tr(\n \"devportal.appNameRequired\",\n \"Application name is required\",\n );\n else if (appName.length < 3)\n errs.name = tr(\n \"devportal.nameTooShort\",\n \"Name must be at least 3 characters\",\n );\n if (!appType)\n errs.appType = tr(\n \"devportal.appTypeRequired\",\n \"Application type is required\",\n );\n if (!appCategory)\n errs.category = tr(\"devportal.categoryRequired\", \"Category is required\");\n if (!appShortDesc.trim())\n errs.shortDescription = tr(\n \"devportal.shortDescriptionRequired\",\n \"Short description is required\",\n );\n setAppErrors(errs);\n return Object.keys(errs).length === 0;\n };\n\n const handleAppSubmit = async () => {\n if (!validateApp()) return;\n try {\n const result = await createApp({\n variables: {\n input: {\n name: appName,\n slug: appSlug || undefined,\n appType: appType as AppType,\n category: appCategory || undefined,\n shortDescription: appShortDesc || undefined,\n visibility: \"PRIVATE\",\n developerOrganizationId: createdOrgId || undefined,\n },\n },\n });\n const id = result.data?.createApplication?.id;\n if (id) {\n bus.emit(\"devportal.app.created\", {\n appId: id,\n name: appName,\n appType,\n organizationId: createdOrgId,\n });\n setCreatedAppId(id);\n markSetupSeen();\n setStep(\"done\");\n }\n } catch {\n // error surfaced via Apollo cache\n }\n };\n\n const handleSkip = () => {\n markSetupSeen();\n handleNavigate(\"/\");\n };\n\n // ─── Render ─────────────────────────────────────────────────\n\n return (\n <main className=\"min-h-screen bg-surface-default\">\n {/* Header */}\n <div className=\"border-b border-border-default bg-surface-raised\">\n <div className=\"mx-auto max-w-2xl px-6 py-5\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Rocket size={20} />\n </div>\n <div>\n <p className=\"text-xs font-medium text-text-secondary\">\n {tr(\"devportal.developerPortal\", \"Developer Portal\")}\n </p>\n <h1 className=\"text-base font-semibold text-text-primary\">\n {tr(\"devportal.setup.title\", \"Get set up\")}\n </h1>\n </div>\n </div>\n {step !== \"done\" && (\n <button\n type=\"button\"\n onClick={handleSkip}\n className=\"text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.setup.skipForNow\", \"Skip for now\")}\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"mx-auto max-w-2xl px-6 py-10\">\n {/* Step indicator */}\n <div className=\"mb-10 flex justify-center\">\n <StepIndicator current={step} />\n </div>\n\n {/* Welcome / purpose — what this wizard sets up and why */}\n {step === \"org\" && (\n <div className=\"mb-8 flex flex-col items-center gap-3 text-center\">\n <Illustration\n name=\"onboarding\"\n alt={tr(\n \"devportal.setup.illustrationAlt\",\n \"Getting started with the developer portal\",\n )}\n size=\"md\"\n />\n <PagePurpose className=\"justify-center\">\n {tr(\n \"devportal.setup.purpose\",\n \"Two quick steps to start building: create the organization that owns your apps, then register your first app. You can change everything later — this just gets you to a working starting point.\",\n )}\n </PagePurpose>\n </div>\n )}\n\n {/* ── Step 1: Organization ─────────────────────── */}\n {step === \"org\" && (\n <div className=\"rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Building2 size={20} />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr(\"devportal.setup.orgTitle\", \"Set up your organization\")}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.setup.orgSubtitle\",\n \"This groups your apps and teams under one publisher identity.\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-5\">\n <InputField\n label={tr(\"devportal.organizationName\", \"Organization Name\")}\n value={orgName}\n onChange={(e) => {\n setOrgName(e.target.value);\n if (!orgSlug || orgSlug === generateSlug(orgName)) {\n setOrgSlug(generateSlug(e.target.value));\n }\n setOrgErrors((p) => ({ ...p, name: undefined }));\n }}\n placeholder={tr(\n \"devportal.organizationNamePlaceholder\",\n \"Acme Development\",\n )}\n required\n error={orgErrors.name}\n />\n\n <InputField\n label={tr(\"devportal.slug\", \"Slug\")}\n value={orgSlug}\n onChange={(e) => {\n setOrgSlug(e.target.value.toLowerCase());\n setOrgErrors((p) => ({ ...p, slug: undefined }));\n }}\n placeholder=\"acme-dev\"\n required\n error={orgErrors.slug}\n hint={tr(\n \"devportal.slugHint\",\n \"URL-friendly identifier — auto-generated from name\",\n )}\n />\n\n <TextareaField\n label={tr(\"devportal.description\", \"Description\")}\n value={orgDescription}\n onChange={(e) => setOrgDescription(e.target.value)}\n placeholder={tr(\n \"devportal.orgDescriptionPlaceholder\",\n \"Tell us about your organization…\",\n )}\n rows={2}\n />\n </div>\n\n <div className=\"mt-8 flex items-center justify-between border-t border-border-default pt-6\">\n <button\n type=\"button\"\n onClick={handleSkip}\n className=\"text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.setup.skipForNow\", \"Skip for now\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleOrgSubmit()}\n disabled={creatingOrg}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2.5 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-60 transition-colors\"\n >\n {creatingOrg ? (\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n ) : null}\n {tr(\"devportal.setup.createAndContinue\", \"Create & Continue\")}\n {!creatingOrg && <ChevronRight size={16} />}\n </button>\n </div>\n </div>\n )}\n\n {/* ── Step 2: First app ─────────────────────── */}\n {step === \"app\" && (\n <div className=\"rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <AppWindow size={20} />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr(\"devportal.setup.appTitle\", \"Create your first app\")}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.setup.appSubtitle\",\n \"Register the app you want to publish or integrate.\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-5\">\n <InputField\n label={tr(\"devportal.applicationName\", \"Application Name\")}\n value={appName}\n onChange={(e) => {\n setAppName(e.target.value);\n if (!appSlug || appSlug === generateSlug(appName)) {\n setAppSlug(generateSlug(e.target.value));\n }\n setAppErrors((p) => ({ ...p, name: undefined }));\n }}\n placeholder={tr(\n \"devportal.applicationNamePlaceholder\",\n \"My Awesome App\",\n )}\n required\n error={appErrors.name}\n />\n\n <div className=\"grid grid-cols-2 gap-4\">\n <SelectField\n label={tr(\"devportal.applicationType\", \"Application Type\")}\n value={appType}\n onChange={(e) => {\n setAppType(e.target.value as AppType);\n setAppErrors((p) => ({ ...p, appType: undefined }));\n }}\n options={APP_TYPE_OPTIONS}\n placeholder={tr(\"devportal.selectType\", \"Select type…\")}\n required\n error={appErrors.appType}\n />\n <SelectField\n label={tr(\"devportal.category\", \"Category\")}\n value={appCategory}\n onChange={(e) => {\n setAppCategory(e.target.value);\n setAppErrors((p) => ({ ...p, category: undefined }));\n }}\n options={CATEGORY_OPTIONS}\n placeholder={tr(\n \"devportal.selectCategory\",\n \"Select category…\",\n )}\n required\n error={appErrors.category}\n />\n </div>\n\n <TextareaField\n label={tr(\"devportal.shortDescription\", \"Short Description\")}\n value={appShortDesc}\n onChange={(e) => {\n setAppShortDesc(e.target.value);\n setAppErrors((p) => ({ ...p, shortDescription: undefined }));\n }}\n placeholder={tr(\n \"devportal.shortDescriptionPlaceholder\",\n \"A brief description of what your app does\",\n )}\n required\n error={appErrors.shortDescription}\n rows={2}\n hint={`${appShortDesc.length}/200`}\n />\n </div>\n\n <div className=\"mt-8 flex items-center justify-between border-t border-border-default pt-6\">\n <button\n type=\"button\"\n onClick={handleSkip}\n className=\"text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.setup.skipForNow\", \"Skip for now\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleAppSubmit()}\n disabled={creatingApp}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2.5 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-60 transition-colors\"\n >\n {creatingApp ? (\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n ) : null}\n {tr(\"devportal.setup.createApp\", \"Create App\")}\n {!creatingApp && <ChevronRight size={16} />}\n </button>\n </div>\n </div>\n )}\n\n {/* ── Step 3: Done ─────────────────────────── */}\n {step === \"done\" && (\n <div className=\"text-center\">\n <div className=\"mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-action-primary-bg/10 text-primary\">\n <Rocket size={32} />\n </div>\n <h2 className=\"text-2xl font-bold text-text-primary\">\n {tr(\"devportal.setup.doneTitle\", \"You're all set!\")}\n </h2>\n <p className=\"mt-2 text-text-secondary\">\n {tr(\n \"devportal.setup.doneSubtitle\",\n \"Your organization and first app are ready. Here's what to do next.\",\n )}\n </p>\n\n <div className=\"mt-8 grid gap-3 sm:grid-cols-2\">\n {createdAppId && (\n <NextStepCard\n icon={<AppWindow size={20} />}\n title={tr(\"devportal.setup.viewApp\", \"View your app\")}\n description={tr(\n \"devportal.setup.viewAppDesc\",\n \"Configure OAuth, webhooks, and versioning\",\n )}\n onClick={() => handleNavigate(`/apps/${createdAppId}`)}\n primary\n />\n )}\n <NextStepCard\n icon={<Key size={20} />}\n title={tr(\"devportal.setup.generateApiKey\", \"Generate API key\")}\n description={tr(\n \"devportal.setup.generateApiKeyDesc\",\n \"Create credentials to start calling APIs\",\n )}\n onClick={() => handleNavigate(\"/api-keys/new\")}\n />\n <NextStepCard\n icon={<BookOpen size={20} />}\n title={tr(\"devportal.setup.browseDocs\", \"Browse docs\")}\n description={tr(\n \"devportal.setup.browseDocsDesc\",\n \"Learn about publishing, SDKs, and webhooks\",\n )}\n onClick={() => handleNavigate(\"/docs\")}\n />\n {createdOrgId && (\n <NextStepCard\n icon={<Building2 size={20} />}\n title={tr(\"devportal.setup.inviteTeam\", \"Invite your team\")}\n description={tr(\n \"devportal.setup.inviteTeamDesc\",\n \"Add members to collaborate on your apps\",\n )}\n onClick={() =>\n handleNavigate(`/organizations/${createdOrgId}/members`)\n }\n />\n )}\n </div>\n\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/\")}\n className=\"mt-8 inline-flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.setup.goToDashboard\", \"Go to dashboard\")}\n <ArrowRight size={14} />\n </button>\n </div>\n )}\n </div>\n </main>\n );\n};\n\n// ─── Next-step card ────────────────────────────────────────────────────────\n\nconst NextStepCard: FC<{\n icon: React.ReactNode;\n title: string;\n description: string;\n onClick: () => void;\n primary?: boolean;\n}> = ({ icon, title, description, onClick, primary }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={[\n \"flex items-start gap-4 rounded-xl border p-5 text-left transition-colors\",\n primary\n ? \"border-primary bg-action-primary-bg/5 hover:bg-action-primary-bg/10\"\n : \"border-border-default bg-surface-raised hover:bg-surface-hover\",\n ].join(\" \")}\n >\n <div\n className={[\n \"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg\",\n primary\n ? \"bg-action-primary-bg/20 text-primary\"\n : \"bg-bg-sunken text-text-secondary\",\n ].join(\" \")}\n >\n {icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold text-text-primary\">{title}</p>\n <p className=\"mt-0.5 text-sm text-text-secondary\">{description}</p>\n </div>\n <ChevronRight\n size={16}\n className=\"mt-0.5 flex-shrink-0 text-text-secondary\"\n />\n </button>\n);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SetupPage.js","names":[],"sources":["../../../src/devportal/pages/SetupPage.tsx"],"sourcesContent":["import type { FC } from \"react\";\nimport { useState } from \"react\";\nimport {\n ArrowRight,\n Building2,\n AppWindow,\n Rocket,\n Check,\n Key,\n BookOpen,\n ChevronRight,\n Box,\n Puzzle,\n GitFork,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { Illustration, PagePurpose } from \"@burdenoff/fe-libs/ui\";\nimport {\n InputField,\n TextareaField,\n SelectField,\n} from \"../components/FormField\";\nimport { useDevportal } from \"../providers/DevportalProvider\";\nimport { useDevportalEventBus } from \"../events\";\nimport { cn } from \"../utils/cn\";\nimport {\n useCreateOrganizationMutation,\n useCreateApplicationMutation,\n} from \"../../generated/global-operations\";\nimport { useCreateBillingAccountMutation } from \"../hooks\";\n\ntype SetupStep = \"org\" | \"app\" | \"done\";\ntype BuilderType = \"app\" | \"node\" | \"workflow\";\n\nconst CATEGORY_OPTIONS = [\n { value: \"productivity\", label: \"Productivity\" },\n { value: \"communication\", label: \"Communication\" },\n { value: \"analytics\", label: \"Analytics\" },\n { value: \"automation\", label: \"Automation\" },\n { value: \"developer-tools\", label: \"Developer Tools\" },\n { value: \"finance\", label: \"Finance\" },\n { value: \"hr\", label: \"HR & People\" },\n { value: \"marketing\", label: \"Marketing\" },\n { value: \"project-management\", label: \"Project Management\" },\n { value: \"security\", label: \"Security\" },\n { value: \"support\", label: \"Support\" },\n { value: \"other\", label: \"Other\" },\n];\n\nfunction generateSlug(name: string): string {\n return name\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/** Mark that the user has interacted with setup so the dashboard gate won't redirect again. */\nfunction markSetupSeen() {\n try {\n localStorage.setItem(\"devportal_setup_dismissed\", \"1\");\n } catch {\n // localStorage unavailable — not critical\n }\n}\n\n// ─── Step indicator ────────────────────────────────────────────────────────\n\nconst STEPS: { key: SetupStep; label: string }[] = [\n { key: \"org\", label: \"Organization\" },\n { key: \"app\", label: \"First app\" },\n { key: \"done\", label: \"Done\" },\n];\n\nconst StepIndicator: FC<{ current: SetupStep }> = ({ current }) => {\n const currentIdx = STEPS.findIndex((s) => s.key === current);\n return (\n <div className=\"flex items-center gap-0\">\n {STEPS.map((step, idx) => {\n const isCompleted = idx < currentIdx;\n const isActive = idx === currentIdx;\n return (\n <div key={step.key} className=\"flex items-center\">\n <div className=\"flex flex-col items-center gap-1.5\">\n <div\n className={[\n \"flex h-8 w-8 items-center justify-center rounded-full text-sm font-semibold transition-colors\",\n isCompleted\n ? \"bg-action-primary-bg text-action-primary-text\"\n : isActive\n ? \"bg-action-primary-bg text-action-primary-text ring-4 ring-primary/20\"\n : \"bg-bg-sunken text-text-secondary\",\n ].join(\" \")}\n >\n {isCompleted ? <Check size={14} /> : idx + 1}\n </div>\n <span\n className={[\n \"text-xs font-medium whitespace-nowrap\",\n isActive ? \"text-text-primary\" : \"text-text-secondary\",\n ].join(\" \")}\n >\n {step.label}\n </span>\n </div>\n {idx < STEPS.length - 1 && (\n <div\n className={[\n \"mx-3 mb-5 h-px w-16 flex-shrink-0 transition-colors\",\n idx < currentIdx ? \"bg-action-primary-bg\" : \"bg-border\",\n ].join(\" \")}\n />\n )}\n </div>\n );\n })}\n </div>\n );\n};\n\n// ─── Main component ────────────────────────────────────────────────────────\n\nexport const SetupPage: FC = () => {\n const { navigate, basePath, currentUser } = useDevportal();\n const { t } = useI18n();\n const bus = useDevportalEventBus();\n const tr = (key: string, fallback: string): string => {\n const v = t(key);\n return v === key ? fallback : v;\n };\n\n const handleNavigate = (path: string) => navigate?.(`${basePath}${path}`);\n\n const [step, setStep] = useState<SetupStep>(\"org\");\n\n // IDs created during wizard — carried forward for success CTAs\n const [createdOrgId, setCreatedOrgId] = useState<string | null>(null);\n const [createdAppId, setCreatedAppId] = useState<string | null>(null);\n\n // ── Step 1: org ──────────────────────────────────────────────\n const [orgName, setOrgName] = useState(\"\");\n const [orgSlug, setOrgSlug] = useState(\"\");\n const [orgDescription, setOrgDescription] = useState(\"\");\n const [orgErrors, setOrgErrors] = useState<{ name?: string; slug?: string }>(\n {},\n );\n\n const [createOrg, { loading: creatingOrg }] = useCreateOrganizationMutation();\n const [createBillingAccountMutation] = useCreateBillingAccountMutation();\n\n const validateOrg = () => {\n const errs: { name?: string; slug?: string } = {};\n if (!orgName.trim())\n errs.name = tr(\n \"devportal.orgNameRequired\",\n \"Organization name is required\",\n );\n else if (orgName.length < 2)\n errs.name = tr(\n \"devportal.orgNameTooShort\",\n \"Name must be at least 2 characters\",\n );\n if (!orgSlug.trim())\n errs.slug = tr(\"devportal.slugRequired\", \"Slug is required\");\n else if (!/^[a-z0-9-]+$/.test(orgSlug))\n errs.slug = tr(\n \"devportal.slugInvalid\",\n \"Lowercase letters, numbers, and hyphens only\",\n );\n setOrgErrors(errs);\n return Object.keys(errs).length === 0;\n };\n\n const handleOrgSubmit = async () => {\n if (!validateOrg()) return;\n try {\n const result = await createOrg({\n variables: {\n input: {\n name: orgName,\n slug: orgSlug || undefined,\n description: orgDescription || undefined,\n },\n },\n });\n const id = result.data?.createDeveloperOrganization?.id;\n if (id) {\n bus.emit(\"devportal.org.created\", {\n organizationId: id,\n name: orgName,\n });\n setCreatedOrgId(id);\n // Fire-and-forget: create a billing account for the new org\n try {\n createBillingAccountMutation({\n variables: {\n input: {\n name: orgName,\n email: currentUser?.email ?? \"\",\n },\n },\n }).catch(() => {});\n } catch {\n // silent failure — billing account creation is non-blocking\n }\n setStep(\"app\");\n }\n } catch {\n // error surfaced via Apollo cache; leave the form open\n }\n };\n\n // ── Step 2: app ──────────────────────────────────────────────\n const [appName, setAppName] = useState(\"\");\n const [appSlug, setAppSlug] = useState(\"\");\n const [builderType, setBuilderType] = useState<BuilderType | null>(null);\n const [appCategory, setAppCategory] = useState(\"\");\n const [appShortDesc, setAppShortDesc] = useState(\"\");\n const [appErrors, setAppErrors] = useState<{\n name?: string;\n builderType?: string;\n category?: string;\n shortDescription?: string;\n }>({});\n\n const [createApp, { loading: creatingApp }] = useCreateApplicationMutation();\n\n const appTypeForBuilder = () => {\n if (builderType === \"node\") return \"NODE\" as const;\n if (builderType === \"workflow\") return \"WORKFLOW\" as const;\n return \"API_INTEGRATION\" as const;\n };\n\n const validateApp = () => {\n const errs: typeof appErrors = {};\n if (!appName.trim())\n errs.name = tr(\n \"devportal.appNameRequired\",\n \"Application name is required\",\n );\n else if (appName.length < 3)\n errs.name = tr(\n \"devportal.nameTooShort\",\n \"Name must be at least 3 characters\",\n );\n if (!builderType)\n errs.builderType = tr(\n \"devportal.appTypeRequired\",\n \"Application type is required\",\n );\n if (!appCategory)\n errs.category = tr(\"devportal.categoryRequired\", \"Category is required\");\n if (!appShortDesc.trim())\n errs.shortDescription = tr(\n \"devportal.shortDescriptionRequired\",\n \"Short description is required\",\n );\n setAppErrors(errs);\n return Object.keys(errs).length === 0;\n };\n\n const handleAppSubmit = async () => {\n if (!validateApp()) return;\n const resolvedType = appTypeForBuilder();\n try {\n const result = await createApp({\n variables: {\n input: {\n name: appName,\n slug: appSlug || undefined,\n appType: resolvedType,\n category: appCategory || undefined,\n shortDescription: appShortDesc || undefined,\n visibility: \"PRIVATE\",\n developerOrganizationId: createdOrgId || undefined,\n },\n },\n });\n const id = result.data?.createApplication?.id;\n if (id) {\n bus.emit(\"devportal.app.created\", {\n appId: id,\n name: appName,\n appType: resolvedType,\n organizationId: createdOrgId,\n });\n setCreatedAppId(id);\n markSetupSeen();\n setStep(\"done\");\n }\n } catch {\n // error surfaced via Apollo cache\n }\n };\n\n const handleSkip = () => {\n markSetupSeen();\n handleNavigate(\"/\");\n };\n\n // ─── Render ─────────────────────────────────────────────────\n\n return (\n <main className=\"min-h-screen bg-surface-default\">\n {/* Header */}\n <div className=\"border-b border-border-default bg-surface-raised\">\n <div className=\"mx-auto max-w-2xl px-6 py-5\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div className=\"flex h-9 w-9 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Rocket size={20} />\n </div>\n <div>\n <p className=\"text-xs font-medium text-text-secondary\">\n {tr(\"devportal.developerPortal\", \"Developer Portal\")}\n </p>\n <h1 className=\"text-base font-semibold text-text-primary\">\n {tr(\"devportal.setup.title\", \"Get set up\")}\n </h1>\n </div>\n </div>\n {step !== \"done\" && (\n <button\n type=\"button\"\n onClick={handleSkip}\n className=\"text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.setup.skipForNow\", \"Skip for now\")}\n </button>\n )}\n </div>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"mx-auto max-w-2xl px-6 py-10\">\n {/* Step indicator */}\n <div className=\"mb-10 flex justify-center\">\n <StepIndicator current={step} />\n </div>\n\n {/* Welcome / purpose — what this wizard sets up and why */}\n {step === \"org\" && (\n <div className=\"mb-8 flex flex-col items-center gap-3 text-center\">\n <Illustration\n name=\"onboarding\"\n alt={tr(\n \"devportal.setup.illustrationAlt\",\n \"Getting started with the developer portal\",\n )}\n size=\"md\"\n />\n <PagePurpose className=\"justify-center\">\n {tr(\n \"devportal.setup.purpose\",\n \"Two quick steps to start building: create the organization that owns your apps, then register your first app. You can change everything later — this just gets you to a working starting point.\",\n )}\n </PagePurpose>\n </div>\n )}\n\n {/* ── Step 1: Organization ─────────────────────── */}\n {step === \"org\" && (\n <div className=\"rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <Building2 size={20} />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr(\"devportal.setup.orgTitle\", \"Set up your organization\")}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.setup.orgSubtitle\",\n \"This groups your apps and teams under one publisher identity.\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-5\">\n <InputField\n label={tr(\"devportal.organizationName\", \"Organization Name\")}\n value={orgName}\n onChange={(e) => {\n setOrgName(e.target.value);\n if (!orgSlug || orgSlug === generateSlug(orgName)) {\n setOrgSlug(generateSlug(e.target.value));\n }\n setOrgErrors((p) => ({ ...p, name: undefined }));\n }}\n placeholder={tr(\n \"devportal.organizationNamePlaceholder\",\n \"Acme Development\",\n )}\n required\n error={orgErrors.name}\n />\n\n <InputField\n label={tr(\"devportal.slug\", \"Slug\")}\n value={orgSlug}\n onChange={(e) => {\n setOrgSlug(e.target.value.toLowerCase());\n setOrgErrors((p) => ({ ...p, slug: undefined }));\n }}\n placeholder=\"acme-dev\"\n required\n error={orgErrors.slug}\n hint={tr(\n \"devportal.slugHint\",\n \"URL-friendly identifier — auto-generated from name\",\n )}\n />\n\n <TextareaField\n label={tr(\"devportal.description\", \"Description\")}\n value={orgDescription}\n onChange={(e) => setOrgDescription(e.target.value)}\n placeholder={tr(\n \"devportal.orgDescriptionPlaceholder\",\n \"Tell us about your organization…\",\n )}\n rows={2}\n />\n </div>\n\n <div className=\"mt-8 flex items-center justify-between border-t border-border-default pt-6\">\n <button\n type=\"button\"\n onClick={handleSkip}\n className=\"text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.setup.skipForNow\", \"Skip for now\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleOrgSubmit()}\n disabled={creatingOrg}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2.5 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-60 transition-colors\"\n >\n {creatingOrg ? (\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n ) : null}\n {tr(\"devportal.setup.createAndContinue\", \"Create & Continue\")}\n {!creatingOrg && <ChevronRight size={16} />}\n </button>\n </div>\n </div>\n )}\n\n {/* ── Step 2: First app ─────────────────────── */}\n {step === \"app\" && (\n <div className=\"rounded-xl border border-border-default bg-surface-raised p-8 shadow-sm\">\n <div className=\"mb-6 flex items-center gap-3\">\n <div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-action-primary-bg/10 text-primary\">\n <AppWindow size={20} />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr(\"devportal.setup.appTitle\", \"Create your first app\")}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n \"devportal.setup.appSubtitle\",\n \"Register the app you want to publish or integrate.\",\n )}\n </p>\n </div>\n </div>\n\n <div className=\"space-y-5\">\n <InputField\n label={tr(\"devportal.applicationName\", \"Application Name\")}\n value={appName}\n onChange={(e) => {\n setAppName(e.target.value);\n if (!appSlug || appSlug === generateSlug(appName)) {\n setAppSlug(generateSlug(e.target.value));\n }\n setAppErrors((p) => ({ ...p, name: undefined }));\n }}\n placeholder={tr(\n \"devportal.applicationNamePlaceholder\",\n \"My Awesome App\",\n )}\n required\n error={appErrors.name}\n />\n\n {/* Builder type picker */}\n <div>\n <p className=\"mb-2 text-sm font-medium text-text-primary\">\n {tr(\"devportal.applicationType\", \"Application Type\")}{\" \"}\n <span className=\"text-status-error-text\">*</span>\n </p>\n <div className=\"grid grid-cols-3 gap-3\">\n <SetupTypeCard\n icon={Box}\n title=\"App\"\n description=\"OAuth integration for the platform\"\n selected={builderType === \"app\"}\n onClick={() => {\n setBuilderType(\"app\");\n setAppErrors((p) => ({ ...p, builderType: undefined }));\n }}\n />\n <SetupTypeCard\n icon={Puzzle}\n title=\"Node\"\n description=\"Custom FluidGrids workflow node\"\n selected={builderType === \"node\"}\n onClick={() => {\n setBuilderType(\"node\");\n setAppErrors((p) => ({ ...p, builderType: undefined }));\n }}\n />\n <SetupTypeCard\n icon={GitFork}\n title=\"Workflow\"\n description=\"Reusable automation template\"\n selected={builderType === \"workflow\"}\n onClick={() => {\n setBuilderType(\"workflow\");\n setAppErrors((p) => ({ ...p, builderType: undefined }));\n }}\n />\n </div>\n {appErrors.builderType && (\n <p className=\"mt-1.5 text-xs text-status-error-text\">\n {appErrors.builderType}\n </p>\n )}\n </div>\n\n <SelectField\n label={tr(\"devportal.category\", \"Category\")}\n value={appCategory}\n onChange={(e) => {\n setAppCategory(e.target.value);\n setAppErrors((p) => ({ ...p, category: undefined }));\n }}\n options={CATEGORY_OPTIONS}\n placeholder={tr(\"devportal.selectCategory\", \"Select category…\")}\n required\n error={appErrors.category}\n />\n\n <TextareaField\n label={tr(\"devportal.shortDescription\", \"Short Description\")}\n value={appShortDesc}\n onChange={(e) => {\n setAppShortDesc(e.target.value);\n setAppErrors((p) => ({ ...p, shortDescription: undefined }));\n }}\n placeholder={tr(\n \"devportal.shortDescriptionPlaceholder\",\n \"A brief description of what your app does\",\n )}\n required\n error={appErrors.shortDescription}\n rows={2}\n hint={`${appShortDesc.length}/200`}\n />\n </div>\n\n <div className=\"mt-8 flex items-center justify-between border-t border-border-default pt-6\">\n <button\n type=\"button\"\n onClick={handleSkip}\n className=\"text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.setup.skipForNow\", \"Skip for now\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleAppSubmit()}\n disabled={creatingApp}\n className=\"inline-flex items-center gap-2 rounded-lg bg-action-primary-bg px-5 py-2.5 text-sm font-semibold text-action-primary-text hover:bg-action-primary-bg/90 disabled:opacity-60 transition-colors\"\n >\n {creatingApp ? (\n <span className=\"h-4 w-4 animate-spin rounded-full border-2 border-current border-t-transparent\" />\n ) : null}\n {tr(\"devportal.setup.createApp\", \"Create App\")}\n {!creatingApp && <ChevronRight size={16} />}\n </button>\n </div>\n </div>\n )}\n\n {/* ── Step 3: Done ─────────────────────────── */}\n {step === \"done\" && (\n <div className=\"text-center\">\n <div className=\"mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-action-primary-bg/10 text-primary\">\n <Rocket size={32} />\n </div>\n <h2 className=\"text-2xl font-bold text-text-primary\">\n {tr(\"devportal.setup.doneTitle\", \"You're all set!\")}\n </h2>\n <p className=\"mt-2 text-text-secondary\">\n {tr(\n \"devportal.setup.doneSubtitle\",\n \"Your organization and first app are ready. Here's what to do next.\",\n )}\n </p>\n\n <div className=\"mt-8 grid gap-3 sm:grid-cols-2\">\n {createdAppId && (\n <NextStepCard\n icon={<AppWindow size={20} />}\n title={tr(\"devportal.setup.viewApp\", \"View your app\")}\n description={tr(\n \"devportal.setup.viewAppDesc\",\n \"Configure OAuth, webhooks, and versioning\",\n )}\n onClick={() => handleNavigate(`/apps/${createdAppId}`)}\n primary\n />\n )}\n <NextStepCard\n icon={<Key size={20} />}\n title={tr(\"devportal.setup.generateApiKey\", \"Generate API key\")}\n description={tr(\n \"devportal.setup.generateApiKeyDesc\",\n \"Create credentials to start calling APIs\",\n )}\n onClick={() => handleNavigate(\"/api-keys/new\")}\n />\n <NextStepCard\n icon={<BookOpen size={20} />}\n title={tr(\"devportal.setup.browseDocs\", \"Browse docs\")}\n description={tr(\n \"devportal.setup.browseDocsDesc\",\n \"Learn about publishing, SDKs, and webhooks\",\n )}\n onClick={() => handleNavigate(\"/docs\")}\n />\n {createdOrgId && (\n <NextStepCard\n icon={<Building2 size={20} />}\n title={tr(\"devportal.setup.inviteTeam\", \"Invite your team\")}\n description={tr(\n \"devportal.setup.inviteTeamDesc\",\n \"Add members to collaborate on your apps\",\n )}\n onClick={() =>\n handleNavigate(`/organizations/${createdOrgId}/members`)\n }\n />\n )}\n </div>\n\n <button\n type=\"button\"\n onClick={() => handleNavigate(\"/\")}\n className=\"mt-8 inline-flex items-center gap-2 text-sm text-text-secondary hover:text-text-primary transition-colors\"\n >\n {tr(\"devportal.setup.goToDashboard\", \"Go to dashboard\")}\n <ArrowRight size={14} />\n </button>\n </div>\n )}\n </div>\n </main>\n );\n};\n\n// ─── Setup type card ───────────────────────────────────────────────────────\n\nconst SetupTypeCard: FC<{\n icon: FC<{ size?: number; className?: string }>;\n title: string;\n description: string;\n selected: boolean;\n onClick: () => void;\n}> = ({ icon: Icon, title, description, selected, onClick }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={cn(\n \"relative flex flex-col gap-2 rounded-xl border-2 p-4 text-left transition-all hover:border-primary/50 hover:shadow-sm\",\n selected\n ? \"border-primary bg-action-primary-bg/5 shadow-sm\"\n : \"border-border-subtle bg-bg-surface\",\n )}\n >\n {selected && (\n <span className=\"absolute top-3 right-3 flex h-4 w-4 items-center justify-center rounded-full bg-action-primary-bg text-action-primary-text\">\n <Check size={10} />\n </span>\n )}\n <div className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-bg-sunken\">\n <Icon size={18} className=\"text-text-primary\" />\n </div>\n <div>\n <p className=\"text-sm font-semibold text-text-primary\">{title}</p>\n <p className=\"mt-0.5 text-xs text-text-secondary\">{description}</p>\n </div>\n </button>\n);\n\n// ─── Next-step card ────────────────────────────────────────────────────────\n\nconst NextStepCard: FC<{\n icon: React.ReactNode;\n title: string;\n description: string;\n onClick: () => void;\n primary?: boolean;\n}> = ({ icon, title, description, onClick, primary }) => (\n <button\n type=\"button\"\n onClick={onClick}\n className={[\n \"flex items-start gap-4 rounded-xl border p-5 text-left transition-colors\",\n primary\n ? \"border-primary bg-action-primary-bg/5 hover:bg-action-primary-bg/10\"\n : \"border-border-default bg-surface-raised hover:bg-surface-hover\",\n ].join(\" \")}\n >\n <div\n className={[\n \"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg\",\n primary\n ? \"bg-action-primary-bg/20 text-primary\"\n : \"bg-bg-sunken text-text-secondary\",\n ].join(\" \")}\n >\n {icon}\n </div>\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-semibold text-text-primary\">{title}</p>\n <p className=\"mt-0.5 text-sm text-text-secondary\">{description}</p>\n </div>\n <ChevronRight\n size={16}\n className=\"mt-0.5 flex-shrink-0 text-text-secondary\"\n />\n </button>\n);\n"],"mappings":"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"}
|