@stackframe/stack 2.5.33 → 2.5.35
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/CHANGELOG.md +20 -0
- package/dist/components/credential-sign-in.js +14 -12
- package/dist/components/credential-sign-in.js.map +1 -1
- package/dist/components/credential-sign-up.js +17 -15
- package/dist/components/credential-sign-up.js.map +1 -1
- package/dist/components/elements/maybe-full-page.d.mts +2 -2
- package/dist/components/elements/maybe-full-page.d.ts +2 -2
- package/dist/components/elements/sidebar-layout.d.mts +5 -5
- package/dist/components/elements/sidebar-layout.d.ts +5 -5
- package/dist/components/magic-link-sign-in.js +11 -9
- package/dist/components/magic-link-sign-in.js.map +1 -1
- package/dist/components/message-cards/message-card.d.mts +2 -2
- package/dist/components/message-cards/message-card.d.ts +2 -2
- package/dist/components/oauth-button-group.js.map +1 -1
- package/dist/components/oauth-button.js +48 -2
- package/dist/components/oauth-button.js.map +1 -1
- package/dist/components/profile-image-editor.js +5 -3
- package/dist/components/profile-image-editor.js.map +1 -1
- package/dist/components/selected-team-switcher.js +8 -5
- package/dist/components/selected-team-switcher.js.map +1 -1
- package/dist/components/team-icon.js.map +1 -1
- package/dist/components/user-button.d.mts +2 -2
- package/dist/components/user-button.d.ts +2 -2
- package/dist/components/user-button.js +11 -11
- package/dist/components/user-button.js.map +1 -1
- package/dist/components-page/account-settings.js +79 -66
- package/dist/components-page/account-settings.js.map +1 -1
- package/dist/components-page/auth-page.js +11 -7
- package/dist/components-page/auth-page.js.map +1 -1
- package/dist/components-page/email-verification.js +4 -2
- package/dist/components-page/email-verification.js.map +1 -1
- package/dist/components-page/error-page.js +6 -4
- package/dist/components-page/error-page.js.map +1 -1
- package/dist/components-page/forgot-password.js +12 -8
- package/dist/components-page/forgot-password.js.map +1 -1
- package/dist/components-page/magic-link-callback.js +5 -3
- package/dist/components-page/magic-link-callback.js.map +1 -1
- package/dist/components-page/oauth-callback.js +6 -5
- package/dist/components-page/oauth-callback.js.map +1 -1
- package/dist/components-page/password-reset.js +25 -22
- package/dist/components-page/password-reset.js.map +1 -1
- package/dist/components-page/team-creation.js +9 -7
- package/dist/components-page/team-creation.js.map +1 -1
- package/dist/components-page/team-invitation.js +14 -11
- package/dist/components-page/team-invitation.js.map +1 -1
- package/dist/esm/components/credential-sign-in.js +14 -12
- package/dist/esm/components/credential-sign-in.js.map +1 -1
- package/dist/esm/components/credential-sign-up.js +17 -15
- package/dist/esm/components/credential-sign-up.js.map +1 -1
- package/dist/esm/components/magic-link-sign-in.js +11 -9
- package/dist/esm/components/magic-link-sign-in.js.map +1 -1
- package/dist/esm/components/oauth-button-group.js.map +1 -1
- package/dist/esm/components/oauth-button.js +48 -2
- package/dist/esm/components/oauth-button.js.map +1 -1
- package/dist/esm/components/profile-image-editor.js +5 -3
- package/dist/esm/components/profile-image-editor.js.map +1 -1
- package/dist/esm/components/selected-team-switcher.js +8 -5
- package/dist/esm/components/selected-team-switcher.js.map +1 -1
- package/dist/esm/components/team-icon.js.map +1 -1
- package/dist/esm/components/user-button.js +12 -15
- package/dist/esm/components/user-button.js.map +1 -1
- package/dist/esm/components-page/account-settings.js +79 -66
- package/dist/esm/components-page/account-settings.js.map +1 -1
- package/dist/esm/components-page/auth-page.js +11 -7
- package/dist/esm/components-page/auth-page.js.map +1 -1
- package/dist/esm/components-page/email-verification.js +4 -2
- package/dist/esm/components-page/email-verification.js.map +1 -1
- package/dist/esm/components-page/error-page.js +6 -4
- package/dist/esm/components-page/error-page.js.map +1 -1
- package/dist/esm/components-page/forgot-password.js +12 -8
- package/dist/esm/components-page/forgot-password.js.map +1 -1
- package/dist/esm/components-page/magic-link-callback.js +5 -3
- package/dist/esm/components-page/magic-link-callback.js.map +1 -1
- package/dist/esm/components-page/oauth-callback.js +6 -5
- package/dist/esm/components-page/oauth-callback.js.map +1 -1
- package/dist/esm/components-page/password-reset.js +25 -22
- package/dist/esm/components-page/password-reset.js.map +1 -1
- package/dist/esm/components-page/team-creation.js +9 -7
- package/dist/esm/components-page/team-creation.js.map +1 -1
- package/dist/esm/components-page/team-invitation.js +14 -11
- package/dist/esm/components-page/team-invitation.js.map +1 -1
- package/dist/esm/generated/quetzal-translations.js +1425 -0
- package/dist/esm/generated/quetzal-translations.js.map +1 -0
- package/dist/esm/lib/stack-app.js +1 -1
- package/dist/esm/lib/translations.js +20 -0
- package/dist/esm/lib/translations.js.map +1 -0
- package/dist/esm/providers/stack-provider.js +4 -2
- package/dist/esm/providers/stack-provider.js.map +1 -1
- package/dist/esm/providers/theme-provider.js.map +1 -1
- package/dist/esm/providers/translation-provider-client.js +18 -0
- package/dist/esm/providers/translation-provider-client.js.map +1 -0
- package/dist/esm/providers/translation-provider.js +12 -0
- package/dist/esm/providers/translation-provider.js.map +1 -0
- package/dist/esm/utils/browser-script.js +38 -6
- package/dist/esm/utils/browser-script.js.map +1 -1
- package/dist/generated/quetzal-translations.d.mts +4 -0
- package/dist/generated/quetzal-translations.d.ts +4 -0
- package/dist/generated/quetzal-translations.js +1451 -0
- package/dist/generated/quetzal-translations.js.map +1 -0
- package/dist/index.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/lib/stack-app.js +1 -1
- package/dist/lib/translations.d.mts +5 -0
- package/dist/lib/translations.d.ts +5 -0
- package/dist/lib/translations.js +55 -0
- package/dist/lib/translations.js.map +1 -0
- package/dist/providers/stack-provider-client.d.mts +3 -3
- package/dist/providers/stack-provider-client.d.ts +3 -3
- package/dist/providers/stack-provider.d.mts +6 -3
- package/dist/providers/stack-provider.d.ts +6 -3
- package/dist/providers/stack-provider.js +4 -2
- package/dist/providers/stack-provider.js.map +1 -1
- package/dist/providers/theme-provider.d.mts +2 -2
- package/dist/providers/theme-provider.d.ts +2 -2
- package/dist/providers/theme-provider.js.map +1 -1
- package/dist/providers/translation-provider-client.d.mts +14 -0
- package/dist/providers/translation-provider-client.d.ts +14 -0
- package/dist/providers/translation-provider-client.js +43 -0
- package/dist/providers/translation-provider-client.js.map +1 -0
- package/dist/providers/translation-provider.d.mts +9 -0
- package/dist/providers/translation-provider.d.ts +9 -0
- package/dist/providers/translation-provider.js +37 -0
- package/dist/providers/translation-provider.js.map +1 -0
- package/dist/utils/browser-script.js +38 -6
- package/dist/utils/browser-script.js.map +1 -1
- package/package.json +20 -12
|
@@ -23,8 +23,10 @@ import { UserAvatar } from "../components/elements/user-avatar";
|
|
|
23
23
|
import { ProfileImageEditor } from "../components/profile-image-editor";
|
|
24
24
|
import { TeamIcon } from "../components/team-icon";
|
|
25
25
|
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
|
26
|
+
import { useTranslation } from "../lib/translations";
|
|
26
27
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
27
28
|
function AccountSettings(props) {
|
|
29
|
+
const { t } = useTranslation();
|
|
28
30
|
const user = useUser({ or: "redirect" });
|
|
29
31
|
const teams = user.useTeams();
|
|
30
32
|
const stackApp = useStackApp();
|
|
@@ -34,14 +36,14 @@ function AccountSettings(props) {
|
|
|
34
36
|
{
|
|
35
37
|
items: [
|
|
36
38
|
{
|
|
37
|
-
title: "My Profile",
|
|
39
|
+
title: t("My Profile"),
|
|
38
40
|
type: "item",
|
|
39
41
|
subpath: "/profile",
|
|
40
42
|
icon: Contact,
|
|
41
43
|
content: /* @__PURE__ */ jsx(ProfileSection, {})
|
|
42
44
|
},
|
|
43
45
|
{
|
|
44
|
-
title: "Security",
|
|
46
|
+
title: t("Security"),
|
|
45
47
|
type: "item",
|
|
46
48
|
icon: ShieldCheck,
|
|
47
49
|
subpath: "/security",
|
|
@@ -52,7 +54,7 @@ function AccountSettings(props) {
|
|
|
52
54
|
] })
|
|
53
55
|
},
|
|
54
56
|
{
|
|
55
|
-
title: "Sign Out",
|
|
57
|
+
title: t("Sign Out"),
|
|
56
58
|
subpath: "/sign-out",
|
|
57
59
|
type: "item",
|
|
58
60
|
icon: LogOut,
|
|
@@ -66,7 +68,7 @@ function AccountSettings(props) {
|
|
|
66
68
|
content: item.content
|
|
67
69
|
})) || [],
|
|
68
70
|
...teams.length > 0 || project.config.clientTeamCreationEnabled ? [{
|
|
69
|
-
title: "Teams",
|
|
71
|
+
title: t("Teams"),
|
|
70
72
|
type: "divider"
|
|
71
73
|
}] : [],
|
|
72
74
|
...teams.map((team) => ({
|
|
@@ -85,23 +87,24 @@ function AccountSettings(props) {
|
|
|
85
87
|
] })
|
|
86
88
|
})),
|
|
87
89
|
...project.config.clientTeamCreationEnabled ? [{
|
|
88
|
-
title: "Create a team",
|
|
90
|
+
title: t("Create a team"),
|
|
89
91
|
icon: CirclePlus,
|
|
90
92
|
type: "item",
|
|
91
93
|
subpath: "/team-creation",
|
|
92
94
|
content: /* @__PURE__ */ jsx(TeamCreation, {})
|
|
93
95
|
}] : []
|
|
94
96
|
].filter((p) => p.type === "divider" || p.content),
|
|
95
|
-
title: "Account Settings",
|
|
97
|
+
title: t("Account Settings"),
|
|
96
98
|
basePath: stackApp.urls.accountSettings
|
|
97
99
|
}
|
|
98
100
|
) }) });
|
|
99
101
|
}
|
|
100
102
|
function ProfileSection() {
|
|
103
|
+
const { t } = useTranslation();
|
|
101
104
|
const user = useUser({ or: "redirect" });
|
|
102
105
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-8", children: [
|
|
103
106
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-start", children: [
|
|
104
|
-
/* @__PURE__ */ jsx(Label, { className: "mb-2", children: "Profile image" }),
|
|
107
|
+
/* @__PURE__ */ jsx(Label, { className: "mb-2", children: t("Profile image") }),
|
|
105
108
|
/* @__PURE__ */ jsx(
|
|
106
109
|
ProfileImageEditor,
|
|
107
110
|
{
|
|
@@ -113,7 +116,7 @@ function ProfileSection() {
|
|
|
113
116
|
)
|
|
114
117
|
] }),
|
|
115
118
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
116
|
-
/* @__PURE__ */ jsx(Label, { children: "Display name" }),
|
|
119
|
+
/* @__PURE__ */ jsx(Label, { children: t("Display name") }),
|
|
117
120
|
/* @__PURE__ */ jsx(EditableText, { value: user.displayName || "", onSave: async (newDisplayName) => {
|
|
118
121
|
await user.update({ displayName: newDisplayName });
|
|
119
122
|
} })
|
|
@@ -121,15 +124,16 @@ function ProfileSection() {
|
|
|
121
124
|
] });
|
|
122
125
|
}
|
|
123
126
|
function EmailVerificationSection() {
|
|
127
|
+
const { t } = useTranslation();
|
|
124
128
|
const user = useUser({ or: "redirect" });
|
|
125
129
|
const [emailSent, setEmailSent] = useState(false);
|
|
126
130
|
if (!user.primaryEmail) {
|
|
127
131
|
return null;
|
|
128
132
|
}
|
|
129
133
|
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { children: [
|
|
130
|
-
/* @__PURE__ */ jsx(Label, { children: "Email Verification" }),
|
|
131
|
-
user.primaryEmailVerified ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: "Your email has been verified." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
132
|
-
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: "Your email has not been verified." }),
|
|
134
|
+
/* @__PURE__ */ jsx(Label, { children: t("Email Verification") }),
|
|
135
|
+
user.primaryEmailVerified ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: t("Your email has been verified.") }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
136
|
+
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Your email has not been verified.") }),
|
|
133
137
|
/* @__PURE__ */ jsx("div", { className: "flex mt-4", children: /* @__PURE__ */ jsx(
|
|
134
138
|
Button,
|
|
135
139
|
{
|
|
@@ -138,28 +142,29 @@ function EmailVerificationSection() {
|
|
|
138
142
|
await user.sendVerificationEmail();
|
|
139
143
|
setEmailSent(true);
|
|
140
144
|
},
|
|
141
|
-
children: emailSent ? "Email sent!" : "Send Verification Email"
|
|
145
|
+
children: emailSent ? t("Email sent!") : t("Send Verification Email")
|
|
142
146
|
}
|
|
143
147
|
) })
|
|
144
148
|
] })
|
|
145
149
|
] }) });
|
|
146
150
|
}
|
|
147
|
-
var passwordSchema = yupObject({
|
|
148
|
-
oldPassword: yupString().required("Please enter your old password"),
|
|
149
|
-
newPassword: yupString().required("Please enter your password").test({
|
|
150
|
-
name: "is-valid-password",
|
|
151
|
-
test: (value, ctx) => {
|
|
152
|
-
const error = getPasswordError(value);
|
|
153
|
-
if (error) {
|
|
154
|
-
return ctx.createError({ message: error.message });
|
|
155
|
-
} else {
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
}),
|
|
160
|
-
newPasswordRepeat: yupString().nullable().oneOf([yup.ref("newPassword"), "", null], "Passwords do not match").required("Please repeat your password")
|
|
161
|
-
});
|
|
162
151
|
function PasswordSection() {
|
|
152
|
+
const { t } = useTranslation();
|
|
153
|
+
const passwordSchema = yupObject({
|
|
154
|
+
oldPassword: yupString().required(t("Please enter your old password")),
|
|
155
|
+
newPassword: yupString().required(t("Please enter your password")).test({
|
|
156
|
+
name: "is-valid-password",
|
|
157
|
+
test: (value, ctx) => {
|
|
158
|
+
const error = getPasswordError(value);
|
|
159
|
+
if (error) {
|
|
160
|
+
return ctx.createError({ message: error.message });
|
|
161
|
+
} else {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}),
|
|
166
|
+
newPasswordRepeat: yupString().nullable().oneOf([yup.ref("newPassword"), "", null], t("Passwords do not match")).required(t("Please repeat your password"))
|
|
167
|
+
});
|
|
163
168
|
const user = useUser({ or: "throw" });
|
|
164
169
|
const [changingPassword, setChangingPassword] = useState(false);
|
|
165
170
|
const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = useForm({
|
|
@@ -173,7 +178,7 @@ function PasswordSection() {
|
|
|
173
178
|
const { oldPassword, newPassword } = data;
|
|
174
179
|
const error = await user.updatePassword({ oldPassword, newPassword });
|
|
175
180
|
if (error) {
|
|
176
|
-
setError("oldPassword", { type: "manual", message: "Incorrect password" });
|
|
181
|
+
setError("oldPassword", { type: "manual", message: t("Incorrect password") });
|
|
177
182
|
} else {
|
|
178
183
|
reset();
|
|
179
184
|
setAlreadyReset(true);
|
|
@@ -188,15 +193,15 @@ function PasswordSection() {
|
|
|
188
193
|
return null;
|
|
189
194
|
}
|
|
190
195
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
191
|
-
/* @__PURE__ */ jsx(Label, { children: "Change password" }),
|
|
192
|
-
/* @__PURE__ */ jsx("div", { children: alreadyReset ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: "Password changed successfully!" }) : !changingPassword ? /* @__PURE__ */ jsx(
|
|
196
|
+
/* @__PURE__ */ jsx(Label, { children: t("Change password") }),
|
|
197
|
+
/* @__PURE__ */ jsx("div", { children: alreadyReset ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: t("Password changed successfully!") }) : !changingPassword ? /* @__PURE__ */ jsx(
|
|
193
198
|
Button,
|
|
194
199
|
{
|
|
195
200
|
variant: "secondary",
|
|
196
201
|
onClick: async () => {
|
|
197
202
|
setChangingPassword(true);
|
|
198
203
|
},
|
|
199
|
-
children: "Change Password"
|
|
204
|
+
children: t("Change Password")
|
|
200
205
|
}
|
|
201
206
|
) : /* @__PURE__ */ jsxs(
|
|
202
207
|
"form",
|
|
@@ -204,7 +209,7 @@ function PasswordSection() {
|
|
|
204
209
|
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
|
205
210
|
noValidate: true,
|
|
206
211
|
children: [
|
|
207
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "old-password", className: "mb-1", children: "Old password" }),
|
|
212
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "old-password", className: "mb-1", children: t("Old password") }),
|
|
208
213
|
/* @__PURE__ */ jsx(
|
|
209
214
|
Input,
|
|
210
215
|
{
|
|
@@ -214,7 +219,7 @@ function PasswordSection() {
|
|
|
214
219
|
}
|
|
215
220
|
),
|
|
216
221
|
/* @__PURE__ */ jsx(FormWarningText, { text: errors.oldPassword?.message?.toString() }),
|
|
217
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "new-password", className: "mt-4 mb-1", children: "Password" }),
|
|
222
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "new-password", className: "mt-4 mb-1", children: t("Password") }),
|
|
218
223
|
/* @__PURE__ */ jsx(
|
|
219
224
|
PasswordInput,
|
|
220
225
|
{
|
|
@@ -228,7 +233,7 @@ function PasswordSection() {
|
|
|
228
233
|
}
|
|
229
234
|
),
|
|
230
235
|
/* @__PURE__ */ jsx(FormWarningText, { text: errors.newPassword?.message?.toString() }),
|
|
231
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: "Repeat password" }),
|
|
236
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: t("Repeat password") }),
|
|
232
237
|
/* @__PURE__ */ jsx(
|
|
233
238
|
PasswordInput,
|
|
234
239
|
{
|
|
@@ -242,13 +247,14 @@ function PasswordSection() {
|
|
|
242
247
|
}
|
|
243
248
|
),
|
|
244
249
|
/* @__PURE__ */ jsx(FormWarningText, { text: errors.newPasswordRepeat?.message?.toString() }),
|
|
245
|
-
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: "Change Password" })
|
|
250
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: t("Change Password") })
|
|
246
251
|
]
|
|
247
252
|
}
|
|
248
253
|
) })
|
|
249
254
|
] });
|
|
250
255
|
}
|
|
251
256
|
function MfaSection() {
|
|
257
|
+
const { t } = useTranslation();
|
|
252
258
|
const project = useStackApp().useProject();
|
|
253
259
|
const user = useUser({ or: "throw" });
|
|
254
260
|
const [generatedSecret, setGeneratedSecret] = useState(null);
|
|
@@ -274,12 +280,12 @@ function MfaSection() {
|
|
|
274
280
|
});
|
|
275
281
|
}, [mfaCode, generatedSecret, handleSubmit]);
|
|
276
282
|
return /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs("div", { children: [
|
|
277
|
-
/* @__PURE__ */ jsx(Label, { children: "Multi-factor Authentication" }),
|
|
283
|
+
/* @__PURE__ */ jsx(Label, { children: t("Multi-factor Authentication") }),
|
|
278
284
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
279
|
-
isEnabled ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: "Multi-factor authentication is currently enabled." }) : generatedSecret ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 items-center", children: [
|
|
280
|
-
/* @__PURE__ */ jsx(Typography, { children: "Scan this QR code with your authenticator app:" }),
|
|
281
|
-
/* @__PURE__ */ jsx("img", { width: 200, height: 200, src: qrCodeUrl ?? throwErr("TOTP QR code failed to generate"), alt: "TOTP multi-factor authentication QR code" }),
|
|
282
|
-
/* @__PURE__ */ jsx(Typography, { children: "Then, enter your six-digit MFA code:" }),
|
|
285
|
+
isEnabled ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: t("Multi-factor authentication is currently enabled.") }) : generatedSecret ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 items-center", children: [
|
|
286
|
+
/* @__PURE__ */ jsx(Typography, { children: t("Scan this QR code with your authenticator app:") }),
|
|
287
|
+
/* @__PURE__ */ jsx("img", { width: 200, height: 200, src: qrCodeUrl ?? throwErr("TOTP QR code failed to generate"), alt: t("TOTP multi-factor authentication QR code") }),
|
|
288
|
+
/* @__PURE__ */ jsx(Typography, { children: t("Then, enter your six-digit MFA code:") }),
|
|
283
289
|
/* @__PURE__ */ jsx(
|
|
284
290
|
Input,
|
|
285
291
|
{
|
|
@@ -293,8 +299,8 @@ function MfaSection() {
|
|
|
293
299
|
disabled: isLoading
|
|
294
300
|
}
|
|
295
301
|
),
|
|
296
|
-
isMaybeWrong && mfaCode.length === 6 && /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: "Incorrect code. Please try again." })
|
|
297
|
-
] }) : /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: "Multi-factor authentication is currently disabled." }),
|
|
302
|
+
isMaybeWrong && mfaCode.length === 6 && /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Incorrect code. Please try again.") })
|
|
303
|
+
] }) : /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Multi-factor authentication is currently disabled.") }),
|
|
298
304
|
/* @__PURE__ */ jsx(
|
|
299
305
|
Button,
|
|
300
306
|
{
|
|
@@ -315,7 +321,7 @@ function MfaSection() {
|
|
|
315
321
|
setMfaCode("");
|
|
316
322
|
}
|
|
317
323
|
},
|
|
318
|
-
children: isEnabled ? "Disable" : generatedSecret ? "Cancel" : "Enable"
|
|
324
|
+
children: isEnabled ? t("Disable") : generatedSecret ? t("Cancel") : t("Enable")
|
|
319
325
|
}
|
|
320
326
|
)
|
|
321
327
|
] })
|
|
@@ -326,17 +332,19 @@ async function generateTotpQrCode(project, user, secret) {
|
|
|
326
332
|
return await QRCode.toDataURL(uri);
|
|
327
333
|
}
|
|
328
334
|
function SignOutSection() {
|
|
335
|
+
const { t } = useTranslation();
|
|
329
336
|
const user = useUser({ or: "throw" });
|
|
330
337
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
|
331
338
|
Button,
|
|
332
339
|
{
|
|
333
340
|
variant: "secondary",
|
|
334
341
|
onClick: () => user.signOut(),
|
|
335
|
-
children: "Sign Out"
|
|
342
|
+
children: t("Sign Out")
|
|
336
343
|
}
|
|
337
344
|
) }) });
|
|
338
345
|
}
|
|
339
346
|
function UserSettings(props) {
|
|
347
|
+
const { t } = useTranslation();
|
|
340
348
|
const user = useUser({ or: "redirect" });
|
|
341
349
|
const [leaving, setLeaving] = useState(false);
|
|
342
350
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2", children: /* @__PURE__ */ jsx("div", { children: !leaving ? /* @__PURE__ */ jsx(
|
|
@@ -344,20 +352,21 @@ function UserSettings(props) {
|
|
|
344
352
|
{
|
|
345
353
|
variant: "secondary",
|
|
346
354
|
onClick: async () => setLeaving(true),
|
|
347
|
-
children: "Leave team"
|
|
355
|
+
children: t("Leave team")
|
|
348
356
|
}
|
|
349
357
|
) : /* @__PURE__ */ jsxs("div", { className: "", children: [
|
|
350
|
-
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: "Are you sure you want to leave the team?" }),
|
|
358
|
+
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Are you sure you want to leave the team?") }),
|
|
351
359
|
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
352
360
|
/* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: async () => {
|
|
353
361
|
await user.leaveTeam(props.team);
|
|
354
362
|
window.location.reload();
|
|
355
|
-
}, children: "Leave" }),
|
|
356
|
-
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => setLeaving(false), children: "Cancel" })
|
|
363
|
+
}, children: t("Leave") }),
|
|
364
|
+
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => setLeaving(false), children: t("Cancel") })
|
|
357
365
|
] })
|
|
358
366
|
] }) }) });
|
|
359
367
|
}
|
|
360
368
|
function ManagementSettings(props) {
|
|
369
|
+
const { t } = useTranslation();
|
|
361
370
|
const user = useUser({ or: "redirect" });
|
|
362
371
|
const updateTeamPermission = user.usePermission(props.team, "$update_team");
|
|
363
372
|
if (!updateTeamPermission) {
|
|
@@ -365,7 +374,7 @@ function ManagementSettings(props) {
|
|
|
365
374
|
}
|
|
366
375
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
367
376
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
368
|
-
/* @__PURE__ */ jsx(Label, { children: "Team display name" }),
|
|
377
|
+
/* @__PURE__ */ jsx(Label, { children: t("Team display name") }),
|
|
369
378
|
/* @__PURE__ */ jsx(
|
|
370
379
|
ProfileImageEditor,
|
|
371
380
|
{
|
|
@@ -377,7 +386,7 @@ function ManagementSettings(props) {
|
|
|
377
386
|
)
|
|
378
387
|
] }),
|
|
379
388
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
380
|
-
/* @__PURE__ */ jsx(Label, { children: "Team display name" }),
|
|
389
|
+
/* @__PURE__ */ jsx(Label, { children: t("Team display name") }),
|
|
381
390
|
/* @__PURE__ */ jsx(
|
|
382
391
|
EditableText,
|
|
383
392
|
{
|
|
@@ -389,11 +398,12 @@ function ManagementSettings(props) {
|
|
|
389
398
|
] });
|
|
390
399
|
}
|
|
391
400
|
function ProfileSettings(props) {
|
|
401
|
+
const { t } = useTranslation();
|
|
392
402
|
const user = useUser({ or: "redirect" });
|
|
393
403
|
const profile = user.useTeamProfile(props.team);
|
|
394
404
|
return /* @__PURE__ */ jsx("div", { className: "flex flex-col", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
395
405
|
/* @__PURE__ */ jsxs(Label, { className: "flex gap-2", children: [
|
|
396
|
-
"User display name
|
|
406
|
+
t("User display name"),
|
|
397
407
|
/* @__PURE__ */ jsx(SimpleTooltip, { tooltip: "This overwrites your user display name in the account setting", type: "info" })
|
|
398
408
|
] }),
|
|
399
409
|
/* @__PURE__ */ jsx(
|
|
@@ -407,10 +417,11 @@ function ProfileSettings(props) {
|
|
|
407
417
|
)
|
|
408
418
|
] }) });
|
|
409
419
|
}
|
|
410
|
-
var invitationSchema = yupObject({
|
|
411
|
-
email: yupString().email().required("Please enter an email address")
|
|
412
|
-
});
|
|
413
420
|
function MemberInvitation(props) {
|
|
421
|
+
const { t } = useTranslation();
|
|
422
|
+
const invitationSchema = yupObject({
|
|
423
|
+
email: yupString().email().required(t("Please enter an email address"))
|
|
424
|
+
});
|
|
414
425
|
const user = useUser({ or: "redirect" });
|
|
415
426
|
const inviteMemberPermission = user.usePermission(props.team, "$invite_members");
|
|
416
427
|
if (!inviteMemberPermission) {
|
|
@@ -434,7 +445,7 @@ function MemberInvitation(props) {
|
|
|
434
445
|
setInvitedEmail(null);
|
|
435
446
|
}, [watch("email")]);
|
|
436
447
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
437
|
-
/* @__PURE__ */ jsx(Label, { children: "Invite a user to team" }),
|
|
448
|
+
/* @__PURE__ */ jsx(Label, { children: t("Invite a user to team") }),
|
|
438
449
|
/* @__PURE__ */ jsxs(
|
|
439
450
|
"form",
|
|
440
451
|
{
|
|
@@ -445,11 +456,11 @@ function MemberInvitation(props) {
|
|
|
445
456
|
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
|
446
457
|
Input,
|
|
447
458
|
{
|
|
448
|
-
placeholder: "Email",
|
|
459
|
+
placeholder: t("Email"),
|
|
449
460
|
...register("email")
|
|
450
461
|
}
|
|
451
462
|
) }),
|
|
452
|
-
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: "Invite User" })
|
|
463
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: t("Invite User") })
|
|
453
464
|
] }),
|
|
454
465
|
/* @__PURE__ */ jsx(FormWarningText, { text: errors.email?.message?.toString() }),
|
|
455
466
|
invitedEmail && /* @__PURE__ */ jsxs(Typography, { type: "label", variant: "secondary", children: [
|
|
@@ -462,6 +473,7 @@ function MemberInvitation(props) {
|
|
|
462
473
|
] });
|
|
463
474
|
}
|
|
464
475
|
function MembersSettings(props) {
|
|
476
|
+
const { t } = useTranslation();
|
|
465
477
|
const user = useUser({ or: "redirect" });
|
|
466
478
|
const readMemberPermission = user.usePermission(props.team, "$read_members");
|
|
467
479
|
const inviteMemberPermission = user.usePermission(props.team, "$invite_members");
|
|
@@ -473,11 +485,11 @@ function MembersSettings(props) {
|
|
|
473
485
|
return null;
|
|
474
486
|
}
|
|
475
487
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
476
|
-
/* @__PURE__ */ jsx(Label, { children: "Members" }),
|
|
488
|
+
/* @__PURE__ */ jsx(Label, { children: t("Members") }),
|
|
477
489
|
/* @__PURE__ */ jsxs(Table, { children: [
|
|
478
490
|
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
479
|
-
/* @__PURE__ */ jsx(TableHead, { className: "w-[100px]", children: "User" }),
|
|
480
|
-
/* @__PURE__ */ jsx(TableHead, { className: "w-[200px]", children: "Name" })
|
|
491
|
+
/* @__PURE__ */ jsx(TableHead, { className: "w-[100px]", children: t("User") }),
|
|
492
|
+
/* @__PURE__ */ jsx(TableHead, { className: "w-[200px]", children: t("Name") })
|
|
481
493
|
] }) }),
|
|
482
494
|
/* @__PURE__ */ jsx(TableBody, { children: users.map(({ id, teamProfile }, i) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
483
495
|
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(UserAvatar, { user: teamProfile }) }),
|
|
@@ -486,10 +498,11 @@ function MembersSettings(props) {
|
|
|
486
498
|
] })
|
|
487
499
|
] });
|
|
488
500
|
}
|
|
489
|
-
var teamCreationSchema = yupObject({
|
|
490
|
-
displayName: yupString().required("Please enter a team name")
|
|
491
|
-
});
|
|
492
501
|
function TeamCreation() {
|
|
502
|
+
const { t } = useTranslation();
|
|
503
|
+
const teamCreationSchema = yupObject({
|
|
504
|
+
displayName: yupString().required(t("Please enter a team name"))
|
|
505
|
+
});
|
|
493
506
|
const { register, handleSubmit, formState: { errors } } = useForm({
|
|
494
507
|
resolver: yupResolver(teamCreationSchema)
|
|
495
508
|
});
|
|
@@ -498,7 +511,7 @@ function TeamCreation() {
|
|
|
498
511
|
const user = useUser({ or: "redirect" });
|
|
499
512
|
const [loading, setLoading] = useState(false);
|
|
500
513
|
if (!project.config.clientTeamCreationEnabled) {
|
|
501
|
-
return /* @__PURE__ */ jsx(MessageCard, { title: "Team creation is not enabled" });
|
|
514
|
+
return /* @__PURE__ */ jsx(MessageCard, { title: t("Team creation is not enabled") });
|
|
502
515
|
}
|
|
503
516
|
const onSubmit = async (data) => {
|
|
504
517
|
setLoading(true);
|
|
@@ -516,7 +529,7 @@ function TeamCreation() {
|
|
|
516
529
|
noValidate: true,
|
|
517
530
|
children: /* @__PURE__ */ jsxs("div", { className: "flex items-end gap-4", children: [
|
|
518
531
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
519
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "email", className: "mb-1", children: "Display name" }),
|
|
532
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "email", className: "mb-1", children: t("Display name") }),
|
|
520
533
|
/* @__PURE__ */ jsx(
|
|
521
534
|
Input,
|
|
522
535
|
{
|
|
@@ -527,7 +540,7 @@ function TeamCreation() {
|
|
|
527
540
|
)
|
|
528
541
|
] }),
|
|
529
542
|
/* @__PURE__ */ jsx(FormWarningText, { text: errors.displayName?.message?.toString() }),
|
|
530
|
-
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: "Create" })
|
|
543
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: t("Create") })
|
|
531
544
|
] })
|
|
532
545
|
}
|
|
533
546
|
) }) });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components-page/account-settings.tsx"],"sourcesContent":["'use client';\n\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { getPasswordError } from '@stackframe/stack-shared/dist/helpers/password';\nimport { useAsyncCallback } from '@stackframe/stack-shared/dist/hooks/use-async-callback';\nimport { yupObject, yupString } from '@stackframe/stack-shared/dist/schema-fields';\nimport { generateRandomValues } from '@stackframe/stack-shared/dist/utils/crypto';\nimport { throwErr } from '@stackframe/stack-shared/dist/utils/errors';\nimport { runAsynchronously, runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/promises';\nimport { Button, EditableText, Input, Label, PasswordInput, SimpleTooltip, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from '@stackframe/stack-ui';\nimport { CirclePlus, Contact, LogOut, ShieldCheck, LucideIcon } from 'lucide-react';\nimport { TOTPController, createTOTPKeyURI } from \"oslo/otp\";\nimport * as QRCode from 'qrcode';\nimport { useEffect, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport * as yup from \"yup\";\nimport { CurrentUser, MessageCard, Project, Team, useStackApp, useUser } from '..';\nimport { FormWarningText } from '../components/elements/form-warning';\nimport { SidebarLayout } from '../components/elements/sidebar-layout';\nimport { UserAvatar } from '../components/elements/user-avatar';\nimport { ProfileImageEditor } from \"../components/profile-image-editor\";\nimport { TeamIcon } from '../components/team-icon';\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\n\n\nexport function AccountSettings(props: {\n fullPage?: boolean,\n extraItems?: {\n title: string,\n icon: LucideIcon,\n content: React.ReactNode,\n subpath: string,\n }[],\n}) {\n const user = useUser({ or: 'redirect' });\n const teams = user.useTeams();\n const stackApp = useStackApp();\n const project = stackApp.useProject();\n\n return (\n <MaybeFullPage fullPage={!!props.fullPage}>\n <div style={{ alignSelf: 'stretch', flexGrow: 1 }}>\n <SidebarLayout\n items={([\n {\n title: 'My Profile',\n type: 'item',\n subpath: '/profile',\n icon: Contact,\n content: <ProfileSection/>,\n },\n {\n title: 'Security',\n type: 'item',\n icon: ShieldCheck,\n subpath: '/security',\n content: (\n <div className='flex flex-col gap-8'>\n <EmailVerificationSection />\n <PasswordSection />\n <MfaSection />\n </div>\n ),\n },\n {\n title: 'Sign Out',\n subpath: '/sign-out',\n type: 'item',\n icon: LogOut,\n content: <SignOutSection />,\n },\n ...(props.extraItems?.map(item => ({\n title: item.title,\n type: 'item',\n subpath: item.subpath,\n icon: item.icon,\n content: item.content,\n } as const)) || []),\n ...(teams.length > 0 || project.config.clientTeamCreationEnabled) ? [{\n title: 'Teams',\n type: 'divider',\n }] as const : [],\n ...teams.map(team => ({\n title: <div className='flex gap-2 items-center'>\n <TeamIcon team={team}/>\n {team.displayName}\n </div>,\n type: 'item',\n subpath: `/teams/${team.id}`,\n content: (\n <div className=\"flex flex-col gap-8\">\n <ProfileSettings team={team}/>\n <ManagementSettings team={team}/>\n <MemberInvitation team={team}/>\n <MembersSettings team={team}/>\n <UserSettings team={team}/>\n </div>\n ),\n } as const)),\n ...project.config.clientTeamCreationEnabled ? [{\n title: 'Create a team',\n icon: CirclePlus,\n type: 'item',\n subpath: '/team-creation',\n content: <TeamCreation />,\n }] as const : [],\n ] as const).filter((p) => p.type === 'divider' || (p as any).content )}\n title='Account Settings'\n basePath={stackApp.urls.accountSettings}\n />\n </div>\n </MaybeFullPage>\n );\n}\n\nfunction ProfileSection() {\n const user = useUser({ or: 'redirect' });\n\n return (\n <div className='flex flex-col gap-8'>\n <div className='flex flex-col items-start'>\n <Label className=\"mb-2\">Profile image</Label>\n <ProfileImageEditor\n user={user}\n onProfileImageUrlChange={async (profileImageUrl) => {\n await user.update({ profileImageUrl });\n }}\n />\n </div>\n <div className='flex flex-col'>\n <Label>Display name</Label>\n <EditableText value={user.displayName || ''} onSave={async (newDisplayName) => {\n await user.update({ displayName: newDisplayName });\n }}/>\n </div>\n </div>\n );\n}\n\nfunction EmailVerificationSection() {\n const user = useUser({ or: 'redirect' });\n const [emailSent, setEmailSent] = useState(false);\n\n if (!user.primaryEmail) {\n return null;\n }\n\n return (\n <>\n <div>\n <Label>Email Verification</Label>\n {user.primaryEmailVerified ? (\n <Typography variant='success'>Your email has been verified.</Typography>\n ) : (\n <>\n <Typography variant='destructive'>Your email has not been verified.</Typography>\n <div className='flex mt-4'>\n <Button\n disabled={emailSent}\n onClick={async () => {\n await user.sendVerificationEmail();\n setEmailSent(true);\n }}\n >\n {emailSent ? 'Email sent!' : 'Send Verification Email'}\n </Button>\n </div>\n </>\n )}\n\n </div>\n </>\n );\n}\n\nconst passwordSchema = yupObject({\n oldPassword: yupString().required('Please enter your old password'),\n newPassword: yupString().required('Please enter your password').test({\n name: 'is-valid-password',\n test: (value, ctx) => {\n const error = getPasswordError(value);\n if (error) {\n return ctx.createError({ message: error.message });\n } else {\n return true;\n }\n }\n }),\n newPasswordRepeat: yupString().nullable().oneOf([yup.ref('newPassword'), \"\", null], 'Passwords do not match').required('Please repeat your password')\n});\n\nfunction PasswordSection() {\n const user = useUser({ or: \"throw\" });\n const [changingPassword, setChangingPassword] = useState(false);\n const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = useForm({\n resolver: yupResolver(passwordSchema)\n });\n const [alreadyReset, setAlreadyReset] = useState(false);\n const [loading, setLoading] = useState(false);\n\n const onSubmit = async (data: yup.InferType<typeof passwordSchema>) => {\n setLoading(true);\n try {\n const { oldPassword, newPassword } = data;\n const error = await user.updatePassword({ oldPassword, newPassword });\n if (error) {\n setError('oldPassword', { type: 'manual', message: 'Incorrect password' });\n } else {\n reset();\n setAlreadyReset(true);\n }\n } finally {\n setLoading(false);\n }\n };\n\n const registerPassword = register('newPassword');\n const registerPasswordRepeat = register('newPasswordRepeat');\n\n if (!user.hasPassword) {\n return null;\n }\n\n return (\n <div>\n <Label>Change password</Label>\n <div>\n {\n alreadyReset ?\n <Typography variant='success'>Password changed successfully!</Typography> :\n !changingPassword ?\n <Button\n variant='secondary'\n onClick={async () => {\n setChangingPassword(true);\n }}\n >\n Change Password\n </Button> :\n <form\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <Label htmlFor=\"old-password\" className=\"mb-1\">Old password</Label>\n <Input\n id=\"old-password\"\n type=\"password\"\n {...register('oldPassword')}\n />\n <FormWarningText text={errors.oldPassword?.message?.toString()} />\n\n <Label htmlFor=\"new-password\" className=\"mt-4 mb-1\">Password</Label>\n <PasswordInput\n id=\"new-password\"\n {...registerPassword}\n onChange={(e) => {\n clearErrors('newPassword');\n clearErrors('newPasswordRepeat');\n runAsynchronously(registerPassword.onChange(e));\n }}\n />\n <FormWarningText text={errors.newPassword?.message?.toString()} />\n\n <Label htmlFor=\"repeat-password\" className=\"mt-4 mb-1\">Repeat password</Label>\n <PasswordInput\n id=\"repeat-password\"\n {...registerPasswordRepeat}\n onChange={(e) => {\n clearErrors('newPassword');\n clearErrors('newPasswordRepeat');\n runAsynchronously(registerPasswordRepeat.onChange(e));\n }}\n />\n <FormWarningText text={errors.newPasswordRepeat?.message?.toString()} />\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>\n Change Password\n </Button>\n </form>\n }\n </div>\n </div>\n );\n}\n\nfunction MfaSection() {\n const project = useStackApp().useProject();\n const user = useUser({ or: \"throw\" });\n const [generatedSecret, setGeneratedSecret] = useState<Uint8Array | null>(null);\n const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);\n const [mfaCode, setMfaCode] = useState<string>(\"\");\n const [isMaybeWrong, setIsMaybeWrong] = useState(false);\n const isEnabled = user.isMultiFactorRequired;\n\n const [handleSubmit, isLoading] = useAsyncCallback(async () => {\n await user.update({\n totpMultiFactorSecret: generatedSecret,\n });\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }, [generatedSecret, user]);\n\n useEffect(() => {\n setIsMaybeWrong(false);\n runAsynchronouslyWithAlert(async () => {\n if (generatedSecret && await new TOTPController().verify(mfaCode, generatedSecret)) {\n await handleSubmit();\n }\n setIsMaybeWrong(true);\n });\n }, [mfaCode, generatedSecret, handleSubmit]);\n\n return (\n <div>\n <div>\n <Label>Multi-factor Authentication</Label>\n\n <div>\n {isEnabled ? (\n <Typography variant=\"success\">Multi-factor authentication is currently enabled.</Typography>\n ) : (\n generatedSecret ? (\n <div className='flex flex-col gap-4 items-center'>\n <Typography>Scan this QR code with your authenticator app:</Typography>\n <img width={200} height={200} src={qrCodeUrl ?? throwErr(\"TOTP QR code failed to generate\")} alt=\"TOTP multi-factor authentication QR code\" />\n <Typography>Then, enter your six-digit MFA code:</Typography>\n <Input\n value={mfaCode}\n onChange={(e) => {\n setIsMaybeWrong(false);\n setMfaCode(e.target.value);\n }}\n placeholder=\"123456\"\n maxLength={6}\n disabled={isLoading}\n />\n {isMaybeWrong && mfaCode.length === 6 && (\n <Typography variant=\"destructive\">Incorrect code. Please try again.</Typography>\n )}\n </div>\n ) : (\n <Typography variant=\"destructive\">Multi-factor authentication is currently disabled.</Typography>\n )\n )}\n\n <Button\n className=\"mt-4\"\n variant={isEnabled ? 'secondary' : 'default'}\n onClick={async () => {\n if (isEnabled) {\n await user.update({\n totpMultiFactorSecret: null,\n });\n } else if (!generatedSecret) {\n const secret = generateRandomValues(new Uint8Array(20));\n setQrCodeUrl(await generateTotpQrCode(project, user, secret));\n setGeneratedSecret(secret);\n } else {\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }\n }}\n >\n {isEnabled ? 'Disable' : (generatedSecret ? 'Cancel' : 'Enable')}\n </Button>\n </div>\n </div>\n </div>\n );\n}\n\nasync function generateTotpQrCode(project: Project, user: CurrentUser, secret: Uint8Array) {\n const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret);\n return await QRCode.toDataURL(uri) as any;\n}\n\nfunction SignOutSection() {\n const user = useUser({ or: \"throw\" });\n return (\n <div className='flex flex-col gap-2'>\n <div>\n <Button\n variant='secondary'\n onClick={() => user.signOut()}\n >\n Sign Out\n </Button>\n </div>\n </div>\n );\n}\n\nfunction UserSettings(props: { team: Team }) {\n const user = useUser({ or: 'redirect' });\n const [leaving, setLeaving] = useState(false);\n\n return (\n <div className='flex flex-col gap-2'>\n <div>\n { !leaving ?\n <Button\n variant='secondary'\n onClick={async () => setLeaving(true)}\n >\n Leave team\n </Button> :\n <div className=''>\n <Typography variant='destructive'>Are you sure you want to leave the team?</Typography>\n <div className='flex gap-2'>\n <Button variant='destructive' onClick={async () => {\n await user.leaveTeam(props.team);\n window.location.reload();\n }}>\n Leave\n </Button>\n <Button variant='secondary' onClick={() => setLeaving(false)}>\n Cancel\n </Button>\n </div>\n </div>}\n </div>\n </div>\n );\n}\n\nfunction ManagementSettings(props: { team: Team }) {\n const user = useUser({ or: 'redirect' });\n const updateTeamPermission = user.usePermission(props.team, '$update_team');\n\n if (!updateTeamPermission) {\n return null;\n }\n\n return (\n <>\n <div className='flex flex-col'>\n <Label>Team display name</Label>\n <ProfileImageEditor\n user={props.team}\n onProfileImageUrlChange={async (profileImageUrl) => {\n await props.team.update({ profileImageUrl });\n }}\n />\n </div>\n\n <div className='flex flex-col'>\n <Label>Team display name</Label>\n <EditableText\n value={props.team.displayName}\n onSave={async (newDisplayName) => await props.team.update({ displayName: newDisplayName })}\n />\n </div>\n </>\n );\n}\n\nfunction ProfileSettings(props: { team: Team }) {\n const user = useUser({ or: 'redirect' });\n const profile = user.useTeamProfile(props.team);\n\n return (\n <div className=\"flex flex-col\">\n <div className=\"flex flex-col\">\n <Label className=\"flex gap-2\">User display name <SimpleTooltip tooltip=\"This overwrites your user display name in the account setting\" type='info'/></Label>\n <EditableText\n value={profile.displayName || ''}\n onSave={async (newDisplayName) => {\n await profile.update({ displayName: newDisplayName });\n }}/>\n </div>\n </div>\n );\n}\n\nconst invitationSchema = yupObject({\n email: yupString().email().required('Please enter an email address'),\n});\n\nfunction MemberInvitation(props: { team: Team }) {\n const user = useUser({ or: 'redirect' });\n const inviteMemberPermission = user.usePermission(props.team, '$invite_members');\n\n if (!inviteMemberPermission) {\n return null;\n }\n\n const { register, handleSubmit, formState: { errors }, watch } = useForm({\n resolver: yupResolver(invitationSchema)\n });\n const [loading, setLoading] = useState(false);\n const [invitedEmail, setInvitedEmail] = useState<string | null>(null);\n\n const onSubmit = async (data: yup.InferType<typeof invitationSchema>) => {\n setLoading(true);\n\n try {\n await props.team.inviteUser({ email: data.email });\n setInvitedEmail(data.email);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n setInvitedEmail(null);\n }, [watch('email')]);\n\n return (\n <div>\n <Label>Invite a user to team</Label>\n <form\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <div className=\"flex flex-col gap-4 md:flex-row\">\n <div>\n <Input\n placeholder=\"Email\"\n {...register('email')}\n />\n </div>\n <Button type=\"submit\" loading={loading}>\n Invite User\n </Button>\n </div>\n <FormWarningText text={errors.email?.message?.toString()} />\n {invitedEmail && <Typography type='label' variant='secondary'>Invited {invitedEmail}</Typography>}\n </form>\n </div>\n );\n}\n\n\nfunction MembersSettings(props: { team: Team }) {\n const user = useUser({ or: 'redirect' });\n const readMemberPermission = user.usePermission(props.team, '$read_members');\n const inviteMemberPermission = user.usePermission(props.team, '$invite_members');\n\n if (!readMemberPermission && !inviteMemberPermission) {\n return null;\n }\n\n const users = props.team.useUsers();\n\n if (!readMemberPermission) {\n return null;\n }\n\n return (\n <div>\n <Label>Members</Label>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[100px]\">User</TableHead>\n <TableHead className=\"w-[200px]\">Name</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {users.map(({ id, teamProfile }, i) => (\n <TableRow key={id}>\n <TableCell>\n <UserAvatar user={teamProfile}/>\n </TableCell>\n <TableCell>\n <Typography>{teamProfile.displayName}</Typography>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n );\n}\n\nconst teamCreationSchema = yupObject({\n displayName: yupString().required('Please enter a team name'),\n});\n\nexport function TeamCreation() {\n const { register, handleSubmit, formState: { errors } } = useForm({\n resolver: yupResolver(teamCreationSchema)\n });\n const app = useStackApp();\n const project = app.useProject();\n const user = useUser({ or: 'redirect' });\n const [loading, setLoading] = useState(false);\n\n if (!project.config.clientTeamCreationEnabled) {\n return <MessageCard title='Team creation is not enabled' />;\n }\n\n const onSubmit = async (data: yup.InferType<typeof teamCreationSchema>) => {\n setLoading(true);\n\n try {\n const team = await user.createTeam({ displayName: data.displayName });\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div className='stack-scope flex flex-col items-stretch'>\n <div className=\"mb-6\">\n <form\n className=\"flex flex-col items-stretch stack-scope\"\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <div className=\"flex items-end gap-4\">\n <div>\n <Label htmlFor=\"email\" className=\"mb-1\">Display name</Label>\n <Input\n id=\"email\"\n type=\"email\"\n {...register('displayName')}\n />\n </div>\n <FormWarningText text={errors.displayName?.message?.toString()} />\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>\n Create\n </Button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../src/components-page/account-settings.tsx"],"sourcesContent":["'use client';\n\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { getPasswordError } from '@stackframe/stack-shared/dist/helpers/password';\nimport { useAsyncCallback } from '@stackframe/stack-shared/dist/hooks/use-async-callback';\nimport { yupObject, yupString } from '@stackframe/stack-shared/dist/schema-fields';\nimport { generateRandomValues } from '@stackframe/stack-shared/dist/utils/crypto';\nimport { throwErr } from '@stackframe/stack-shared/dist/utils/errors';\nimport { runAsynchronously, runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/promises';\nimport { Button, EditableText, Input, Label, PasswordInput, SimpleTooltip, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from '@stackframe/stack-ui';\nimport { CirclePlus, Contact, LogOut, ShieldCheck, LucideIcon } from 'lucide-react';\nimport { TOTPController, createTOTPKeyURI } from \"oslo/otp\";\nimport * as QRCode from 'qrcode';\nimport { useEffect, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport * as yup from \"yup\";\nimport { CurrentUser, MessageCard, Project, Team, useStackApp, useUser } from '..';\nimport { FormWarningText } from '../components/elements/form-warning';\nimport { SidebarLayout } from '../components/elements/sidebar-layout';\nimport { UserAvatar } from '../components/elements/user-avatar';\nimport { ProfileImageEditor } from \"../components/profile-image-editor\";\nimport { TeamIcon } from '../components/team-icon';\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\nimport { useTranslation } from \"../lib/translations\";\n\n\nexport function AccountSettings(props: {\n fullPage?: boolean,\n extraItems?: {\n title: string,\n icon: LucideIcon,\n content: React.ReactNode,\n subpath: string,\n }[],\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const teams = user.useTeams();\n const stackApp = useStackApp();\n const project = stackApp.useProject();\n\n return (\n <MaybeFullPage fullPage={!!props.fullPage}>\n <div style={{ alignSelf: 'stretch', flexGrow: 1 }}>\n <SidebarLayout\n items={([\n {\n title: t('My Profile'),\n type: 'item',\n subpath: '/profile',\n icon: Contact,\n content: <ProfileSection/>,\n },\n {\n title: t('Security'),\n type: 'item',\n icon: ShieldCheck,\n subpath: '/security',\n content: (\n <div className='flex flex-col gap-8'>\n <EmailVerificationSection />\n <PasswordSection />\n <MfaSection />\n </div>\n ),\n },\n {\n title: t('Sign Out'),\n subpath: '/sign-out',\n type: 'item',\n icon: LogOut,\n content: <SignOutSection />,\n },\n ...(props.extraItems?.map(item => ({\n title: item.title,\n type: 'item',\n subpath: item.subpath,\n icon: item.icon,\n content: item.content,\n } as const)) || []),\n ...(teams.length > 0 || project.config.clientTeamCreationEnabled) ? [{\n title: t('Teams'),\n type: 'divider',\n }] as const : [],\n ...teams.map(team => ({\n title: <div className='flex gap-2 items-center'>\n <TeamIcon team={team}/>\n {team.displayName}\n </div>,\n type: 'item',\n subpath: `/teams/${team.id}`,\n content: (\n <div className=\"flex flex-col gap-8\">\n <ProfileSettings team={team}/>\n <ManagementSettings team={team}/>\n <MemberInvitation team={team}/>\n <MembersSettings team={team}/>\n <UserSettings team={team}/>\n </div>\n ),\n } as const)),\n ...project.config.clientTeamCreationEnabled ? [{\n title: t('Create a team'),\n icon: CirclePlus,\n type: 'item',\n subpath: '/team-creation',\n content: <TeamCreation />,\n }] as const : [],\n ] as const).filter((p) => p.type === 'divider' || (p as any).content )}\n title={t(\"Account Settings\")}\n basePath={stackApp.urls.accountSettings}\n />\n </div>\n </MaybeFullPage>\n );\n}\n\nfunction ProfileSection() {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n\n return (\n <div className='flex flex-col gap-8'>\n <div className='flex flex-col items-start'>\n <Label className=\"mb-2\">{t(\"Profile image\")}</Label>\n <ProfileImageEditor\n user={user}\n onProfileImageUrlChange={async (profileImageUrl) => {\n await user.update({ profileImageUrl });\n }}\n />\n </div>\n <div className='flex flex-col'>\n <Label>{t(\"Display name\")}</Label>\n <EditableText value={user.displayName || ''} onSave={async (newDisplayName) => {\n await user.update({ displayName: newDisplayName });\n }}/>\n </div>\n </div>\n );\n}\n\nfunction EmailVerificationSection() {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const [emailSent, setEmailSent] = useState(false);\n\n if (!user.primaryEmail) {\n return null;\n }\n\n return (\n <>\n <div>\n <Label>{t(\"Email Verification\")}</Label>\n {user.primaryEmailVerified ? (\n <Typography variant='success'>{t(\"Your email has been verified.\")}</Typography>\n ) : (\n <>\n <Typography variant='destructive'>{t(\"Your email has not been verified.\")}</Typography>\n <div className='flex mt-4'>\n <Button\n disabled={emailSent}\n onClick={async () => {\n await user.sendVerificationEmail();\n setEmailSent(true);\n }}\n >\n {emailSent ? t(\"Email sent!\") : t(\"Send Verification Email\")}\n </Button>\n </div>\n </>\n )}\n\n </div>\n </>\n );\n}\n\nfunction PasswordSection() {\n const { t } = useTranslation();\n\n const passwordSchema = yupObject({\n oldPassword: yupString().required(t('Please enter your old password')),\n newPassword: yupString().required(t('Please enter your password')).test({\n name: 'is-valid-password',\n test: (value, ctx) => {\n const error = getPasswordError(value);\n if (error) {\n return ctx.createError({ message: error.message });\n } else {\n return true;\n }\n }\n }),\n newPasswordRepeat: yupString().nullable().oneOf([yup.ref('newPassword'), \"\", null], t('Passwords do not match')).required(t('Please repeat your password'))\n });\n\n const user = useUser({ or: \"throw\" });\n const [changingPassword, setChangingPassword] = useState(false);\n const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = useForm({\n resolver: yupResolver(passwordSchema)\n });\n const [alreadyReset, setAlreadyReset] = useState(false);\n const [loading, setLoading] = useState(false);\n\n const onSubmit = async (data: yup.InferType<typeof passwordSchema>) => {\n setLoading(true);\n try {\n const { oldPassword, newPassword } = data;\n const error = await user.updatePassword({ oldPassword, newPassword });\n if (error) {\n setError('oldPassword', { type: 'manual', message: t('Incorrect password') });\n } else {\n reset();\n setAlreadyReset(true);\n }\n } finally {\n setLoading(false);\n }\n };\n\n const registerPassword = register('newPassword');\n const registerPasswordRepeat = register('newPasswordRepeat');\n\n if (!user.hasPassword) {\n return null;\n }\n\n return (\n <div>\n <Label>{t(\"Change password\")}</Label>\n <div>\n {\n alreadyReset ?\n <Typography variant='success'>{t(\"Password changed successfully!\")}</Typography> :\n !changingPassword ?\n <Button\n variant='secondary'\n onClick={async () => {\n setChangingPassword(true);\n }}\n >{t(\"Change Password\")}</Button> :\n <form\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <Label htmlFor=\"old-password\" className=\"mb-1\">{t(\"Old password\")}</Label>\n <Input\n id=\"old-password\"\n type=\"password\"\n {...register(\"oldPassword\")}\n />\n <FormWarningText text={errors.oldPassword?.message?.toString()} />\n\n <Label htmlFor=\"new-password\" className=\"mt-4 mb-1\">{t(\"Password\")}</Label>\n <PasswordInput\n id=\"new-password\"\n {...registerPassword}\n onChange={(e) => {\n clearErrors('newPassword');\n clearErrors('newPasswordRepeat');\n runAsynchronously(registerPassword.onChange(e));\n }}\n />\n <FormWarningText text={errors.newPassword?.message?.toString()} />\n\n <Label htmlFor=\"repeat-password\" className=\"mt-4 mb-1\">{t(\"Repeat password\")}</Label>\n <PasswordInput\n id=\"repeat-password\"\n {...registerPasswordRepeat}\n onChange={(e) => {\n clearErrors('newPassword');\n clearErrors('newPasswordRepeat');\n runAsynchronously(registerPasswordRepeat.onChange(e));\n }}\n />\n <FormWarningText text={errors.newPasswordRepeat?.message?.toString()} />\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>{t(\"Change Password\")}</Button>\n </form>\n }\n </div>\n </div>\n );\n}\n\nfunction MfaSection() {\n const { t } = useTranslation();\n const project = useStackApp().useProject();\n const user = useUser({ or: \"throw\" });\n const [generatedSecret, setGeneratedSecret] = useState<Uint8Array | null>(null);\n const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);\n const [mfaCode, setMfaCode] = useState<string>(\"\");\n const [isMaybeWrong, setIsMaybeWrong] = useState(false);\n const isEnabled = user.isMultiFactorRequired;\n\n const [handleSubmit, isLoading] = useAsyncCallback(async () => {\n await user.update({\n totpMultiFactorSecret: generatedSecret,\n });\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }, [generatedSecret, user]);\n\n useEffect(() => {\n setIsMaybeWrong(false);\n runAsynchronouslyWithAlert(async () => {\n if (generatedSecret && await new TOTPController().verify(mfaCode, generatedSecret)) {\n await handleSubmit();\n }\n setIsMaybeWrong(true);\n });\n }, [mfaCode, generatedSecret, handleSubmit]);\n\n return (\n <div>\n <div>\n <Label>{t(\"Multi-factor Authentication\")}</Label>\n\n <div>\n {isEnabled ? (\n <Typography variant=\"success\">{t(\"Multi-factor authentication is currently enabled.\")}</Typography>\n ) : (\n generatedSecret ? (\n <div className='flex flex-col gap-4 items-center'>\n <Typography>{t(\"Scan this QR code with your authenticator app:\")}</Typography>\n <img width={200} height={200} src={qrCodeUrl ?? throwErr(\"TOTP QR code failed to generate\")} alt={t(\"TOTP multi-factor authentication QR code\")} />\n <Typography>{t(\"Then, enter your six-digit MFA code:\")}</Typography>\n <Input\n value={mfaCode}\n onChange={(e) => {\n setIsMaybeWrong(false);\n setMfaCode(e.target.value);\n }}\n placeholder=\"123456\"\n maxLength={6}\n disabled={isLoading}\n />\n {isMaybeWrong && mfaCode.length === 6 && (\n <Typography variant=\"destructive\">{t(\"Incorrect code. Please try again.\")}</Typography>\n )}\n </div>\n ) : (\n <Typography variant=\"destructive\">{t(\"Multi-factor authentication is currently disabled.\")}</Typography>\n )\n )}\n\n <Button\n className=\"mt-4\"\n variant={isEnabled ? 'secondary' : 'default'}\n onClick={async () => {\n if (isEnabled) {\n await user.update({\n totpMultiFactorSecret: null,\n });\n } else if (!generatedSecret) {\n const secret = generateRandomValues(new Uint8Array(20));\n setQrCodeUrl(await generateTotpQrCode(project, user, secret));\n setGeneratedSecret(secret);\n } else {\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }\n }}\n >\n {isEnabled ? t(\"Disable\") : (generatedSecret ? t(\"Cancel\") : t(\"Enable\"))}\n </Button>\n </div>\n </div>\n </div>\n );\n}\n\nasync function generateTotpQrCode(project: Project, user: CurrentUser, secret: Uint8Array) {\n const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret);\n return await QRCode.toDataURL(uri) as any;\n}\n\nfunction SignOutSection() {\n const { t } = useTranslation();\n const user = useUser({ or: \"throw\" });\n return (\n <div className='flex flex-col gap-2'>\n <div>\n <Button\n variant='secondary'\n onClick={() => user.signOut()}\n >{t(\"Sign Out\")}</Button>\n </div>\n </div>\n );\n}\n\nfunction UserSettings(props: { team: Team }) {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const [leaving, setLeaving] = useState(false);\n\n return (\n <div className='flex flex-col gap-2'>\n <div>\n { !leaving ?\n <Button\n variant='secondary'\n onClick={async () => setLeaving(true)}\n >{t(\"Leave team\")}</Button> :\n <div className=''>\n <Typography variant='destructive'>{t(\"Are you sure you want to leave the team?\")}</Typography>\n <div className='flex gap-2'>\n <Button variant='destructive' onClick={async () => {\n await user.leaveTeam(props.team);\n window.location.reload();\n }}>{t(\"Leave\")}</Button>\n <Button variant='secondary' onClick={() => setLeaving(false)}>{t(\"Cancel\")}</Button>\n </div>\n </div>}\n </div>\n </div>\n );\n}\n\nfunction ManagementSettings(props: { team: Team }) {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const updateTeamPermission = user.usePermission(props.team, '$update_team');\n\n if (!updateTeamPermission) {\n return null;\n }\n\n return (\n <>\n <div className='flex flex-col'>\n <Label>{t(\"Team display name\")}</Label>\n <ProfileImageEditor\n user={props.team}\n onProfileImageUrlChange={async (profileImageUrl) => {\n await props.team.update({ profileImageUrl });\n }}\n />\n </div>\n\n <div className='flex flex-col'>\n <Label>{t(\"Team display name\")}</Label>\n <EditableText\n value={props.team.displayName}\n onSave={async (newDisplayName) => await props.team.update({ displayName: newDisplayName })}\n />\n </div>\n </>\n );\n}\n\nfunction ProfileSettings(props: { team: Team }) {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const profile = user.useTeamProfile(props.team);\n\n return (\n <div className=\"flex flex-col\">\n <div className=\"flex flex-col\">\n <Label className=\"flex gap-2\">{t(\"User display name\")}<SimpleTooltip tooltip=\"This overwrites your user display name in the account setting\" type='info'/></Label>\n <EditableText\n value={profile.displayName || ''}\n onSave={async (newDisplayName) => {\n await profile.update({ displayName: newDisplayName });\n }}/>\n </div>\n </div>\n );\n}\n\nfunction MemberInvitation(props: { team: Team }) {\n const { t } = useTranslation();\n\n const invitationSchema = yupObject({\n email: yupString().email().required(t('Please enter an email address')),\n });\n\n const user = useUser({ or: 'redirect' });\n const inviteMemberPermission = user.usePermission(props.team, '$invite_members');\n\n if (!inviteMemberPermission) {\n return null;\n }\n\n const { register, handleSubmit, formState: { errors }, watch } = useForm({\n resolver: yupResolver(invitationSchema)\n });\n const [loading, setLoading] = useState(false);\n const [invitedEmail, setInvitedEmail] = useState<string | null>(null);\n\n const onSubmit = async (data: yup.InferType<typeof invitationSchema>) => {\n setLoading(true);\n\n try {\n await props.team.inviteUser({ email: data.email });\n setInvitedEmail(data.email);\n } finally {\n setLoading(false);\n }\n };\n\n useEffect(() => {\n setInvitedEmail(null);\n }, [watch('email')]);\n\n return (\n <div>\n <Label>{t(\"Invite a user to team\")}</Label>\n <form\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <div className=\"flex flex-col gap-4 md:flex-row\">\n <div>\n <Input\n placeholder={t(\"Email\")}\n {...register(\"email\")}\n />\n </div>\n <Button type=\"submit\" loading={loading}>{t(\"Invite User\")}</Button>\n </div>\n <FormWarningText text={errors.email?.message?.toString()} />\n {invitedEmail && <Typography type='label' variant='secondary'>Invited {invitedEmail}</Typography>}\n </form>\n </div>\n );\n}\n\n\nfunction MembersSettings(props: { team: Team }) {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const readMemberPermission = user.usePermission(props.team, '$read_members');\n const inviteMemberPermission = user.usePermission(props.team, '$invite_members');\n\n if (!readMemberPermission && !inviteMemberPermission) {\n return null;\n }\n\n const users = props.team.useUsers();\n\n if (!readMemberPermission) {\n return null;\n }\n\n return (\n <div>\n <Label>{t(\"Members\")}</Label>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[100px]\">{t(\"User\")}</TableHead>\n <TableHead className=\"w-[200px]\">{t(\"Name\")}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {users.map(({ id, teamProfile }, i) => (\n <TableRow key={id}>\n <TableCell>\n <UserAvatar user={teamProfile}/>\n </TableCell>\n <TableCell>\n <Typography>{teamProfile.displayName}</Typography>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n );\n}\n\nexport function TeamCreation() {\n const { t } = useTranslation();\n\n const teamCreationSchema = yupObject({\n displayName: yupString().required(t(\"Please enter a team name\")),\n });\n\n const { register, handleSubmit, formState: { errors } } = useForm({\n resolver: yupResolver(teamCreationSchema)\n });\n const app = useStackApp();\n const project = app.useProject();\n const user = useUser({ or: 'redirect' });\n const [loading, setLoading] = useState(false);\n\n if (!project.config.clientTeamCreationEnabled) {\n return <MessageCard title={t(\"Team creation is not enabled\")} />;\n }\n\n const onSubmit = async (data: yup.InferType<typeof teamCreationSchema>) => {\n setLoading(true);\n\n try {\n const team = await user.createTeam({ displayName: data.displayName });\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <div className='stack-scope flex flex-col items-stretch'>\n <div className=\"mb-6\">\n <form\n className=\"flex flex-col items-stretch stack-scope\"\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <div className=\"flex items-end gap-4\">\n <div>\n <Label htmlFor=\"email\" className=\"mb-1\">{t(\"Display name\")}</Label>\n <Input\n id=\"email\"\n type=\"email\"\n {...register(\"displayName\")}\n />\n </div>\n <FormWarningText text={errors.displayName?.message?.toString()} />\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>{t(\"Create\")}</Button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|