@stackframe/stack 2.5.33 → 2.5.37
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 +40 -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/elements/sidebar-layout.js +15 -7
- package/dist/components/elements/sidebar-layout.js.map +1 -1
- package/dist/components/elements/user-avatar.js +1 -1
- package/dist/components/elements/user-avatar.js.map +1 -1
- 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 +46 -2
- package/dist/components/oauth-button.js.map +1 -1
- package/dist/components/profile-image-editor.js +7 -5
- package/dist/components/profile-image-editor.js.map +1 -1
- package/dist/components/selected-team-switcher.js +7 -4
- 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.d.mts +4 -2
- package/dist/components-page/account-settings.d.ts +4 -2
- package/dist/components-page/account-settings.js +406 -221
- 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/stack-handler.d.mts +1 -0
- package/dist/components-page/stack-handler.d.ts +1 -0
- 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/elements/sidebar-layout.js +15 -7
- package/dist/esm/components/elements/sidebar-layout.js.map +1 -1
- package/dist/esm/components/elements/user-avatar.js +1 -1
- package/dist/esm/components/elements/user-avatar.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 +46 -2
- package/dist/esm/components/oauth-button.js.map +1 -1
- package/dist/esm/components/profile-image-editor.js +7 -5
- package/dist/esm/components/profile-image-editor.js.map +1 -1
- package/dist/esm/components/selected-team-switcher.js +7 -4
- 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 +406 -222
- 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/global-css.js +1 -1
- package/dist/esm/generated/global-css.js.map +1 -1
- package/dist/esm/generated/quetzal-translations.js +1629 -0
- package/dist/esm/generated/quetzal-translations.js.map +1 -0
- package/dist/esm/lib/stack-app.js +8 -1
- package/dist/esm/lib/stack-app.js.map +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 +43 -8
- package/dist/esm/utils/browser-script.js.map +1 -1
- package/dist/generated/global-css.d.mts +1 -1
- package/dist/generated/global-css.d.ts +1 -1
- package/dist/generated/global-css.js +1 -1
- package/dist/generated/global-css.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 +1655 -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.d.mts +4 -1
- package/dist/lib/stack-app.d.ts +4 -1
- package/dist/lib/stack-app.js +8 -1
- package/dist/lib/stack-app.js.map +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 +43 -8
- package/dist/utils/browser-script.js.map +1 -1
- package/package.json +20 -12
|
@@ -9,22 +9,24 @@ import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-field
|
|
|
9
9
|
import { generateRandomValues } from "@stackframe/stack-shared/dist/utils/crypto";
|
|
10
10
|
import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
|
11
11
|
import { runAsynchronously, runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
|
12
|
-
import { Button, EditableText, Input, Label, PasswordInput,
|
|
13
|
-
import { CirclePlus, Contact,
|
|
12
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, EditableText, Input, Label, PasswordInput, Separator, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from "@stackframe/stack-ui";
|
|
13
|
+
import { CirclePlus, Contact, Settings, ShieldCheck } from "lucide-react";
|
|
14
14
|
import { TOTPController, createTOTPKeyURI } from "oslo/otp";
|
|
15
15
|
import * as QRCode from "qrcode";
|
|
16
|
-
import { useEffect, useState } from "react";
|
|
16
|
+
import React, { useEffect, useState } from "react";
|
|
17
17
|
import { useForm } from "react-hook-form";
|
|
18
18
|
import * as yup from "yup";
|
|
19
19
|
import { MessageCard, useStackApp, useUser } from "..";
|
|
20
20
|
import { FormWarningText } from "../components/elements/form-warning";
|
|
21
|
+
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
|
21
22
|
import { SidebarLayout } from "../components/elements/sidebar-layout";
|
|
22
23
|
import { UserAvatar } from "../components/elements/user-avatar";
|
|
23
24
|
import { ProfileImageEditor } from "../components/profile-image-editor";
|
|
24
25
|
import { TeamIcon } from "../components/team-icon";
|
|
25
|
-
import {
|
|
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,29 +36,25 @@ 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
|
-
content: /* @__PURE__ */ jsx(
|
|
43
|
+
content: /* @__PURE__ */ jsx(ProfilePage, {})
|
|
42
44
|
},
|
|
43
45
|
{
|
|
44
|
-
title: "Security",
|
|
46
|
+
title: t("Security"),
|
|
45
47
|
type: "item",
|
|
46
|
-
icon: ShieldCheck,
|
|
47
48
|
subpath: "/security",
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
/* @__PURE__ */ jsx(PasswordSection, {}),
|
|
51
|
-
/* @__PURE__ */ jsx(MfaSection, {})
|
|
52
|
-
] })
|
|
49
|
+
icon: ShieldCheck,
|
|
50
|
+
content: /* @__PURE__ */ jsx(SecurityPage, {})
|
|
53
51
|
},
|
|
54
52
|
{
|
|
55
|
-
title: "
|
|
56
|
-
subpath: "/sign-out",
|
|
53
|
+
title: t("Settings"),
|
|
57
54
|
type: "item",
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
subpath: "/settings",
|
|
56
|
+
icon: Settings,
|
|
57
|
+
content: /* @__PURE__ */ jsx(SettingsPage, {})
|
|
60
58
|
},
|
|
61
59
|
...props.extraItems?.map((item) => ({
|
|
62
60
|
title: item.title,
|
|
@@ -66,7 +64,7 @@ function AccountSettings(props) {
|
|
|
66
64
|
content: item.content
|
|
67
65
|
})) || [],
|
|
68
66
|
...teams.length > 0 || project.config.clientTeamCreationEnabled ? [{
|
|
69
|
-
title: "Teams",
|
|
67
|
+
title: t("Teams"),
|
|
70
68
|
type: "divider"
|
|
71
69
|
}] : [],
|
|
72
70
|
...teams.map((team) => ({
|
|
@@ -76,61 +74,108 @@ function AccountSettings(props) {
|
|
|
76
74
|
] }),
|
|
77
75
|
type: "item",
|
|
78
76
|
subpath: `/teams/${team.id}`,
|
|
79
|
-
content: /* @__PURE__ */
|
|
80
|
-
/* @__PURE__ */ jsx(ProfileSettings, { team }),
|
|
81
|
-
/* @__PURE__ */ jsx(ManagementSettings, { team }),
|
|
82
|
-
/* @__PURE__ */ jsx(MemberInvitation, { team }),
|
|
83
|
-
/* @__PURE__ */ jsx(MembersSettings, { team }),
|
|
84
|
-
/* @__PURE__ */ jsx(UserSettings, { team })
|
|
85
|
-
] })
|
|
77
|
+
content: /* @__PURE__ */ jsx(TeamPage, { team })
|
|
86
78
|
})),
|
|
87
79
|
...project.config.clientTeamCreationEnabled ? [{
|
|
88
|
-
title: "Create a team",
|
|
80
|
+
title: t("Create a team"),
|
|
89
81
|
icon: CirclePlus,
|
|
90
82
|
type: "item",
|
|
91
83
|
subpath: "/team-creation",
|
|
92
84
|
content: /* @__PURE__ */ jsx(TeamCreation, {})
|
|
93
85
|
}] : []
|
|
94
86
|
].filter((p) => p.type === "divider" || p.content),
|
|
95
|
-
title: "Account Settings",
|
|
87
|
+
title: t("Account Settings"),
|
|
96
88
|
basePath: stackApp.urls.accountSettings
|
|
97
89
|
}
|
|
98
90
|
) }) });
|
|
99
91
|
}
|
|
100
|
-
function
|
|
92
|
+
function Section(props) {
|
|
93
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex", children: [
|
|
94
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col justify-center", children: [
|
|
95
|
+
/* @__PURE__ */ jsx(Typography, { className: "font-medium", children: props.title }),
|
|
96
|
+
props.description && /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "footnote", children: props.description })
|
|
97
|
+
] }),
|
|
98
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col gap-2", children: props.children })
|
|
99
|
+
] });
|
|
100
|
+
}
|
|
101
|
+
function PageLayout(props) {
|
|
102
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6", children: [
|
|
103
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
104
|
+
React.Children.map(props.children, (child) => child && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
105
|
+
child,
|
|
106
|
+
/* @__PURE__ */ jsx(Separator, {})
|
|
107
|
+
] }))
|
|
108
|
+
] });
|
|
109
|
+
}
|
|
110
|
+
function ProfilePage() {
|
|
111
|
+
const { t } = useTranslation();
|
|
101
112
|
const user = useUser({ or: "redirect" });
|
|
102
|
-
return /* @__PURE__ */ jsxs(
|
|
103
|
-
/* @__PURE__ */
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
114
|
+
/* @__PURE__ */ jsx(
|
|
115
|
+
Section,
|
|
116
|
+
{
|
|
117
|
+
title: t("User name"),
|
|
118
|
+
description: t("This is a display name and is not used for authentication"),
|
|
119
|
+
children: /* @__PURE__ */ jsx(
|
|
120
|
+
EditableText,
|
|
121
|
+
{
|
|
122
|
+
value: user.displayName || "",
|
|
123
|
+
onSave: async (newDisplayName) => {
|
|
124
|
+
await user.update({ displayName: newDisplayName });
|
|
125
|
+
}
|
|
111
126
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
/* @__PURE__ */
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
),
|
|
130
|
+
/* @__PURE__ */ jsx(
|
|
131
|
+
Section,
|
|
132
|
+
{
|
|
133
|
+
title: t("Profile image"),
|
|
134
|
+
description: t("Upload your own image as your avatar"),
|
|
135
|
+
children: /* @__PURE__ */ jsx(
|
|
136
|
+
ProfileImageEditor,
|
|
137
|
+
{
|
|
138
|
+
user,
|
|
139
|
+
onProfileImageUrlChange: async (profileImageUrl) => {
|
|
140
|
+
await user.update({ profileImageUrl });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
] });
|
|
147
|
+
}
|
|
148
|
+
function SecurityPage() {
|
|
149
|
+
const emailVerificationSection = useEmailVerificationSection();
|
|
150
|
+
const passwordSection = usePasswordSection();
|
|
151
|
+
const mfaSection = useMfaSection();
|
|
152
|
+
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
153
|
+
emailVerificationSection,
|
|
154
|
+
passwordSection,
|
|
155
|
+
mfaSection
|
|
156
|
+
] });
|
|
157
|
+
}
|
|
158
|
+
function SettingsPage() {
|
|
159
|
+
const deleteAccountSection = useDeleteAccountSection();
|
|
160
|
+
const signOutSection = useSignOutSection();
|
|
161
|
+
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
162
|
+
deleteAccountSection,
|
|
163
|
+
signOutSection
|
|
121
164
|
] });
|
|
122
165
|
}
|
|
123
|
-
function
|
|
166
|
+
function useEmailVerificationSection() {
|
|
167
|
+
const { t } = useTranslation();
|
|
124
168
|
const user = useUser({ or: "redirect" });
|
|
125
169
|
const [emailSent, setEmailSent] = useState(false);
|
|
126
170
|
if (!user.primaryEmail) {
|
|
127
171
|
return null;
|
|
128
172
|
}
|
|
129
|
-
return /* @__PURE__ */ jsx(
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
173
|
+
return /* @__PURE__ */ jsx(
|
|
174
|
+
Section,
|
|
175
|
+
{
|
|
176
|
+
title: t("Email Verification"),
|
|
177
|
+
description: t("Verify your email address to secure your account"),
|
|
178
|
+
children: /* @__PURE__ */ jsx("div", { children: user.primaryEmailVerified ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: t("Your email has been verified.") }) : /* @__PURE__ */ jsx("div", { className: "flex", children: /* @__PURE__ */ jsx(
|
|
134
179
|
Button,
|
|
135
180
|
{
|
|
136
181
|
disabled: emailSent,
|
|
@@ -138,28 +183,29 @@ function EmailVerificationSection() {
|
|
|
138
183
|
await user.sendVerificationEmail();
|
|
139
184
|
setEmailSent(true);
|
|
140
185
|
},
|
|
141
|
-
children: emailSent ? "Email sent!" : "Send Verification Email"
|
|
186
|
+
children: emailSent ? t("Email sent!") : t("Send Verification Email")
|
|
142
187
|
}
|
|
143
|
-
) })
|
|
144
|
-
|
|
145
|
-
|
|
188
|
+
) }) })
|
|
189
|
+
}
|
|
190
|
+
);
|
|
146
191
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
192
|
+
function usePasswordSection() {
|
|
193
|
+
const { t } = useTranslation();
|
|
194
|
+
const passwordSchema = yupObject({
|
|
195
|
+
oldPassword: yupString().required(t("Please enter your old password")),
|
|
196
|
+
newPassword: yupString().required(t("Please enter your password")).test({
|
|
197
|
+
name: "is-valid-password",
|
|
198
|
+
test: (value, ctx) => {
|
|
199
|
+
const error = getPasswordError(value);
|
|
200
|
+
if (error) {
|
|
201
|
+
return ctx.createError({ message: error.message });
|
|
202
|
+
} else {
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
157
205
|
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
});
|
|
162
|
-
function PasswordSection() {
|
|
206
|
+
}),
|
|
207
|
+
newPasswordRepeat: yupString().nullable().oneOf([yup.ref("newPassword"), "", null], t("Passwords do not match")).required(t("Please repeat your password"))
|
|
208
|
+
});
|
|
163
209
|
const user = useUser({ or: "throw" });
|
|
164
210
|
const [changingPassword, setChangingPassword] = useState(false);
|
|
165
211
|
const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = useForm({
|
|
@@ -173,7 +219,7 @@ function PasswordSection() {
|
|
|
173
219
|
const { oldPassword, newPassword } = data;
|
|
174
220
|
const error = await user.updatePassword({ oldPassword, newPassword });
|
|
175
221
|
if (error) {
|
|
176
|
-
setError("oldPassword", { type: "manual", message: "Incorrect password" });
|
|
222
|
+
setError("oldPassword", { type: "manual", message: t("Incorrect password") });
|
|
177
223
|
} else {
|
|
178
224
|
reset();
|
|
179
225
|
setAlreadyReset(true);
|
|
@@ -188,15 +234,15 @@ function PasswordSection() {
|
|
|
188
234
|
return null;
|
|
189
235
|
}
|
|
190
236
|
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(
|
|
237
|
+
/* @__PURE__ */ jsx(Label, { children: t("Change password") }),
|
|
238
|
+
/* @__PURE__ */ jsx("div", { children: alreadyReset ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: t("Password changed successfully!") }) : !changingPassword ? /* @__PURE__ */ jsx(
|
|
193
239
|
Button,
|
|
194
240
|
{
|
|
195
241
|
variant: "secondary",
|
|
196
242
|
onClick: async () => {
|
|
197
243
|
setChangingPassword(true);
|
|
198
244
|
},
|
|
199
|
-
children: "Change Password"
|
|
245
|
+
children: t("Change Password")
|
|
200
246
|
}
|
|
201
247
|
) : /* @__PURE__ */ jsxs(
|
|
202
248
|
"form",
|
|
@@ -204,7 +250,7 @@ function PasswordSection() {
|
|
|
204
250
|
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
|
205
251
|
noValidate: true,
|
|
206
252
|
children: [
|
|
207
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "old-password", className: "mb-1", children: "Old password" }),
|
|
253
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "old-password", className: "mb-1", children: t("Old password") }),
|
|
208
254
|
/* @__PURE__ */ jsx(
|
|
209
255
|
Input,
|
|
210
256
|
{
|
|
@@ -214,7 +260,7 @@ function PasswordSection() {
|
|
|
214
260
|
}
|
|
215
261
|
),
|
|
216
262
|
/* @__PURE__ */ jsx(FormWarningText, { text: errors.oldPassword?.message?.toString() }),
|
|
217
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "new-password", className: "mt-4 mb-1", children: "Password" }),
|
|
263
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "new-password", className: "mt-4 mb-1", children: t("Password") }),
|
|
218
264
|
/* @__PURE__ */ jsx(
|
|
219
265
|
PasswordInput,
|
|
220
266
|
{
|
|
@@ -228,7 +274,7 @@ function PasswordSection() {
|
|
|
228
274
|
}
|
|
229
275
|
),
|
|
230
276
|
/* @__PURE__ */ jsx(FormWarningText, { text: errors.newPassword?.message?.toString() }),
|
|
231
|
-
/* @__PURE__ */ jsx(Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: "Repeat password" }),
|
|
277
|
+
/* @__PURE__ */ jsx(Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: t("Repeat password") }),
|
|
232
278
|
/* @__PURE__ */ jsx(
|
|
233
279
|
PasswordInput,
|
|
234
280
|
{
|
|
@@ -242,13 +288,14 @@ function PasswordSection() {
|
|
|
242
288
|
}
|
|
243
289
|
),
|
|
244
290
|
/* @__PURE__ */ jsx(FormWarningText, { text: errors.newPasswordRepeat?.message?.toString() }),
|
|
245
|
-
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: "Change Password" })
|
|
291
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: t("Change Password") })
|
|
246
292
|
]
|
|
247
293
|
}
|
|
248
294
|
) })
|
|
249
295
|
] });
|
|
250
296
|
}
|
|
251
|
-
function
|
|
297
|
+
function useMfaSection() {
|
|
298
|
+
const { t } = useTranslation();
|
|
252
299
|
const project = useStackApp().useProject();
|
|
253
300
|
const user = useUser({ or: "throw" });
|
|
254
301
|
const [generatedSecret, setGeneratedSecret] = useState(null);
|
|
@@ -273,100 +320,165 @@ function MfaSection() {
|
|
|
273
320
|
setIsMaybeWrong(true);
|
|
274
321
|
});
|
|
275
322
|
}, [mfaCode, generatedSecret, handleSubmit]);
|
|
276
|
-
return /* @__PURE__ */ jsx(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
/* @__PURE__ */
|
|
283
|
-
|
|
284
|
-
|
|
323
|
+
return /* @__PURE__ */ jsx(
|
|
324
|
+
Section,
|
|
325
|
+
{
|
|
326
|
+
title: t("Multi-factor Authentication"),
|
|
327
|
+
description: isEnabled ? t("Multi-factor authentication is currently enabled.") : t("Multi-factor authentication is currently disabled."),
|
|
328
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
|
329
|
+
!isEnabled && generatedSecret && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
330
|
+
/* @__PURE__ */ jsx(Typography, { children: t("Scan this QR code with your authenticator app:") }),
|
|
331
|
+
/* @__PURE__ */ jsx("img", { width: 200, height: 200, src: qrCodeUrl ?? throwErr("TOTP QR code failed to generate"), alt: t("TOTP multi-factor authentication QR code") }),
|
|
332
|
+
/* @__PURE__ */ jsx(Typography, { children: t("Then, enter your six-digit MFA code:") }),
|
|
333
|
+
/* @__PURE__ */ jsx(
|
|
334
|
+
Input,
|
|
335
|
+
{
|
|
336
|
+
value: mfaCode,
|
|
337
|
+
onChange: (e) => {
|
|
338
|
+
setIsMaybeWrong(false);
|
|
339
|
+
setMfaCode(e.target.value);
|
|
340
|
+
},
|
|
341
|
+
placeholder: "123456",
|
|
342
|
+
maxLength: 6,
|
|
343
|
+
disabled: isLoading
|
|
344
|
+
}
|
|
345
|
+
),
|
|
346
|
+
isMaybeWrong && mfaCode.length === 6 && /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Incorrect code. Please try again.") }),
|
|
347
|
+
/* @__PURE__ */ jsx("div", { className: "flex", children: /* @__PURE__ */ jsx(
|
|
348
|
+
Button,
|
|
349
|
+
{
|
|
350
|
+
variant: "secondary",
|
|
351
|
+
onClick: () => {
|
|
352
|
+
setGeneratedSecret(null);
|
|
353
|
+
setQrCodeUrl(null);
|
|
354
|
+
setMfaCode("");
|
|
355
|
+
},
|
|
356
|
+
children: t("Cancel")
|
|
357
|
+
}
|
|
358
|
+
) })
|
|
359
|
+
] }),
|
|
360
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2", children: isEnabled ? /* @__PURE__ */ jsx(
|
|
361
|
+
Button,
|
|
285
362
|
{
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
setIsMaybeWrong(false);
|
|
289
|
-
setMfaCode(e.target.value);
|
|
290
|
-
},
|
|
291
|
-
placeholder: "123456",
|
|
292
|
-
maxLength: 6,
|
|
293
|
-
disabled: isLoading
|
|
294
|
-
}
|
|
295
|
-
),
|
|
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." }),
|
|
298
|
-
/* @__PURE__ */ jsx(
|
|
299
|
-
Button,
|
|
300
|
-
{
|
|
301
|
-
className: "mt-4",
|
|
302
|
-
variant: isEnabled ? "secondary" : "default",
|
|
303
|
-
onClick: async () => {
|
|
304
|
-
if (isEnabled) {
|
|
363
|
+
variant: "secondary",
|
|
364
|
+
onClick: async () => {
|
|
305
365
|
await user.update({
|
|
306
366
|
totpMultiFactorSecret: null
|
|
307
367
|
});
|
|
308
|
-
}
|
|
368
|
+
},
|
|
369
|
+
children: t("Disable")
|
|
370
|
+
}
|
|
371
|
+
) : !generatedSecret && /* @__PURE__ */ jsx(
|
|
372
|
+
Button,
|
|
373
|
+
{
|
|
374
|
+
variant: "default",
|
|
375
|
+
onClick: async () => {
|
|
309
376
|
const secret = generateRandomValues(new Uint8Array(20));
|
|
310
377
|
setQrCodeUrl(await generateTotpQrCode(project, user, secret));
|
|
311
378
|
setGeneratedSecret(secret);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
)
|
|
321
|
-
] })
|
|
322
|
-
] }) });
|
|
379
|
+
},
|
|
380
|
+
children: t("Enable")
|
|
381
|
+
}
|
|
382
|
+
) })
|
|
383
|
+
] })
|
|
384
|
+
}
|
|
385
|
+
);
|
|
323
386
|
}
|
|
324
387
|
async function generateTotpQrCode(project, user, secret) {
|
|
325
388
|
const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret);
|
|
326
389
|
return await QRCode.toDataURL(uri);
|
|
327
390
|
}
|
|
328
|
-
function
|
|
391
|
+
function useSignOutSection() {
|
|
392
|
+
const { t } = useTranslation();
|
|
329
393
|
const user = useUser({ or: "throw" });
|
|
330
|
-
return /* @__PURE__ */ jsx(
|
|
331
|
-
|
|
394
|
+
return /* @__PURE__ */ jsx(
|
|
395
|
+
Section,
|
|
332
396
|
{
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
children: "
|
|
397
|
+
title: t("Sign out"),
|
|
398
|
+
description: t("End your current session"),
|
|
399
|
+
children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
|
400
|
+
Button,
|
|
401
|
+
{
|
|
402
|
+
variant: "secondary",
|
|
403
|
+
onClick: () => user.signOut(),
|
|
404
|
+
children: t("Sign out")
|
|
405
|
+
}
|
|
406
|
+
) })
|
|
336
407
|
}
|
|
337
|
-
)
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
function TeamPage(props) {
|
|
411
|
+
const teamUserProfileSection = useTeamUserProfileSection(props);
|
|
412
|
+
const teamProfileImageSection = useTeamProfileImageSection(props);
|
|
413
|
+
const teamDisplayNameSection = useTeamDisplayNameSection(props);
|
|
414
|
+
const leaveTeamSection = useLeaveTeamSection(props);
|
|
415
|
+
const memberInvitationSection = useMemberInvitationSection(props);
|
|
416
|
+
const memberListSection = useMemberListSection(props);
|
|
417
|
+
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
|
418
|
+
teamUserProfileSection,
|
|
419
|
+
memberInvitationSection,
|
|
420
|
+
memberListSection,
|
|
421
|
+
teamProfileImageSection,
|
|
422
|
+
teamDisplayNameSection,
|
|
423
|
+
leaveTeamSection
|
|
424
|
+
] });
|
|
338
425
|
}
|
|
339
|
-
function
|
|
426
|
+
function useLeaveTeamSection(props) {
|
|
427
|
+
const { t } = useTranslation();
|
|
340
428
|
const user = useUser({ or: "redirect" });
|
|
341
429
|
const [leaving, setLeaving] = useState(false);
|
|
342
|
-
return /* @__PURE__ */ jsx(
|
|
343
|
-
|
|
430
|
+
return /* @__PURE__ */ jsx(
|
|
431
|
+
Section,
|
|
344
432
|
{
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
children: "
|
|
433
|
+
title: t("Leave Team"),
|
|
434
|
+
description: t("leave this team and remove your team profile"),
|
|
435
|
+
children: !leaving ? /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
|
436
|
+
Button,
|
|
437
|
+
{
|
|
438
|
+
variant: "secondary",
|
|
439
|
+
onClick: () => setLeaving(true),
|
|
440
|
+
children: t("Leave team")
|
|
441
|
+
}
|
|
442
|
+
) }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
443
|
+
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Are you sure you want to leave the team?") }),
|
|
444
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
445
|
+
/* @__PURE__ */ jsx(
|
|
446
|
+
Button,
|
|
447
|
+
{
|
|
448
|
+
variant: "destructive",
|
|
449
|
+
onClick: async () => {
|
|
450
|
+
await user.leaveTeam(props.team);
|
|
451
|
+
window.location.reload();
|
|
452
|
+
},
|
|
453
|
+
children: t("Leave")
|
|
454
|
+
}
|
|
455
|
+
),
|
|
456
|
+
/* @__PURE__ */ jsx(
|
|
457
|
+
Button,
|
|
458
|
+
{
|
|
459
|
+
variant: "secondary",
|
|
460
|
+
onClick: () => setLeaving(false),
|
|
461
|
+
children: t("Cancel")
|
|
462
|
+
}
|
|
463
|
+
)
|
|
464
|
+
] })
|
|
465
|
+
] })
|
|
348
466
|
}
|
|
349
|
-
)
|
|
350
|
-
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: "Are you sure you want to leave the team?" }),
|
|
351
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
352
|
-
/* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: async () => {
|
|
353
|
-
await user.leaveTeam(props.team);
|
|
354
|
-
window.location.reload();
|
|
355
|
-
}, children: "Leave" }),
|
|
356
|
-
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => setLeaving(false), children: "Cancel" })
|
|
357
|
-
] })
|
|
358
|
-
] }) }) });
|
|
467
|
+
);
|
|
359
468
|
}
|
|
360
|
-
function
|
|
469
|
+
function useTeamProfileImageSection(props) {
|
|
470
|
+
const { t } = useTranslation();
|
|
361
471
|
const user = useUser({ or: "redirect" });
|
|
362
472
|
const updateTeamPermission = user.usePermission(props.team, "$update_team");
|
|
363
473
|
if (!updateTeamPermission) {
|
|
364
474
|
return null;
|
|
365
475
|
}
|
|
366
|
-
return /* @__PURE__ */
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
476
|
+
return /* @__PURE__ */ jsx(
|
|
477
|
+
Section,
|
|
478
|
+
{
|
|
479
|
+
title: t("Team profile image"),
|
|
480
|
+
description: t("Upload an image for your team"),
|
|
481
|
+
children: /* @__PURE__ */ jsx(
|
|
370
482
|
ProfileImageEditor,
|
|
371
483
|
{
|
|
372
484
|
user: props.team,
|
|
@@ -375,42 +487,57 @@ function ManagementSettings(props) {
|
|
|
375
487
|
}
|
|
376
488
|
}
|
|
377
489
|
)
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
490
|
+
}
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
function useTeamDisplayNameSection(props) {
|
|
494
|
+
const { t } = useTranslation();
|
|
495
|
+
const user = useUser({ or: "redirect" });
|
|
496
|
+
const updateTeamPermission = user.usePermission(props.team, "$update_team");
|
|
497
|
+
if (!updateTeamPermission) {
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
return /* @__PURE__ */ jsx(
|
|
501
|
+
Section,
|
|
502
|
+
{
|
|
503
|
+
title: t("Team display name"),
|
|
504
|
+
description: t("Change the display name of your team"),
|
|
505
|
+
children: /* @__PURE__ */ jsx(
|
|
382
506
|
EditableText,
|
|
383
507
|
{
|
|
384
508
|
value: props.team.displayName,
|
|
385
509
|
onSave: async (newDisplayName) => await props.team.update({ displayName: newDisplayName })
|
|
386
510
|
}
|
|
387
511
|
)
|
|
388
|
-
|
|
389
|
-
|
|
512
|
+
}
|
|
513
|
+
);
|
|
390
514
|
}
|
|
391
|
-
function
|
|
515
|
+
function useTeamUserProfileSection(props) {
|
|
516
|
+
const { t } = useTranslation();
|
|
392
517
|
const user = useUser({ or: "redirect" });
|
|
393
518
|
const profile = user.useTeamProfile(props.team);
|
|
394
|
-
return /* @__PURE__ */ jsx(
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
519
|
+
return /* @__PURE__ */ jsx(
|
|
520
|
+
Section,
|
|
521
|
+
{
|
|
522
|
+
title: t("Team user name"),
|
|
523
|
+
description: t("Overwrite your user display name in this team"),
|
|
524
|
+
children: /* @__PURE__ */ jsx(
|
|
525
|
+
EditableText,
|
|
526
|
+
{
|
|
527
|
+
value: profile.displayName || "",
|
|
528
|
+
onSave: async (newDisplayName) => {
|
|
529
|
+
await profile.update({ displayName: newDisplayName });
|
|
530
|
+
}
|
|
405
531
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
532
|
+
)
|
|
533
|
+
}
|
|
534
|
+
);
|
|
409
535
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
536
|
+
function useMemberInvitationSection(props) {
|
|
537
|
+
const { t } = useTranslation();
|
|
538
|
+
const invitationSchema = yupObject({
|
|
539
|
+
email: yupString().email().required(t("Please enter an email address"))
|
|
540
|
+
});
|
|
414
541
|
const user = useUser({ or: "redirect" });
|
|
415
542
|
const inviteMemberPermission = user.usePermission(props.team, "$invite_members");
|
|
416
543
|
if (!inviteMemberPermission) {
|
|
@@ -433,35 +560,40 @@ function MemberInvitation(props) {
|
|
|
433
560
|
useEffect(() => {
|
|
434
561
|
setInvitedEmail(null);
|
|
435
562
|
}, [watch("email")]);
|
|
436
|
-
return /* @__PURE__ */
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
"
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
563
|
+
return /* @__PURE__ */ jsx(
|
|
564
|
+
Section,
|
|
565
|
+
{
|
|
566
|
+
title: t("Invite member"),
|
|
567
|
+
description: t("Invite a user to your team through email"),
|
|
568
|
+
children: /* @__PURE__ */ jsxs(
|
|
569
|
+
"form",
|
|
570
|
+
{
|
|
571
|
+
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
|
572
|
+
noValidate: true,
|
|
573
|
+
children: [
|
|
574
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 md:flex-row", children: [
|
|
575
|
+
/* @__PURE__ */ jsx(
|
|
576
|
+
Input,
|
|
577
|
+
{
|
|
578
|
+
placeholder: t("Email"),
|
|
579
|
+
...register("email")
|
|
580
|
+
}
|
|
581
|
+
),
|
|
582
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: t("Invite User") })
|
|
583
|
+
] }),
|
|
584
|
+
/* @__PURE__ */ jsx(FormWarningText, { text: errors.email?.message?.toString() }),
|
|
585
|
+
invitedEmail && /* @__PURE__ */ jsxs(Typography, { type: "label", variant: "secondary", children: [
|
|
586
|
+
"Invited ",
|
|
587
|
+
invitedEmail
|
|
588
|
+
] })
|
|
589
|
+
]
|
|
590
|
+
}
|
|
591
|
+
)
|
|
592
|
+
}
|
|
593
|
+
);
|
|
463
594
|
}
|
|
464
|
-
function
|
|
595
|
+
function useMemberListSection(props) {
|
|
596
|
+
const { t } = useTranslation();
|
|
465
597
|
const user = useUser({ or: "redirect" });
|
|
466
598
|
const readMemberPermission = user.usePermission(props.team, "$read_members");
|
|
467
599
|
const inviteMemberPermission = user.usePermission(props.team, "$invite_members");
|
|
@@ -473,23 +605,24 @@ function MembersSettings(props) {
|
|
|
473
605
|
return null;
|
|
474
606
|
}
|
|
475
607
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
476
|
-
/* @__PURE__ */ jsx(
|
|
477
|
-
/* @__PURE__ */ jsxs(Table, { children: [
|
|
608
|
+
/* @__PURE__ */ jsx(Typography, { className: "font-medium mb-2", children: t("Members") }),
|
|
609
|
+
/* @__PURE__ */ jsx("div", { className: "border rounded-md", children: /* @__PURE__ */ jsxs(Table, { children: [
|
|
478
610
|
/* @__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" })
|
|
611
|
+
/* @__PURE__ */ jsx(TableHead, { className: "w-[100px]", children: t("User") }),
|
|
612
|
+
/* @__PURE__ */ jsx(TableHead, { className: "w-[200px]", children: t("Name") })
|
|
481
613
|
] }) }),
|
|
482
614
|
/* @__PURE__ */ jsx(TableBody, { children: users.map(({ id, teamProfile }, i) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
483
615
|
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(UserAvatar, { user: teamProfile }) }),
|
|
484
616
|
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { children: teamProfile.displayName }) })
|
|
485
617
|
] }, id)) })
|
|
486
|
-
] })
|
|
618
|
+
] }) })
|
|
487
619
|
] });
|
|
488
620
|
}
|
|
489
|
-
var teamCreationSchema = yupObject({
|
|
490
|
-
displayName: yupString().required("Please enter a team name")
|
|
491
|
-
});
|
|
492
621
|
function TeamCreation() {
|
|
622
|
+
const { t } = useTranslation();
|
|
623
|
+
const teamCreationSchema = yupObject({
|
|
624
|
+
displayName: yupString().required(t("Please enter a team name"))
|
|
625
|
+
});
|
|
493
626
|
const { register, handleSubmit, formState: { errors } } = useForm({
|
|
494
627
|
resolver: yupResolver(teamCreationSchema)
|
|
495
628
|
});
|
|
@@ -498,7 +631,7 @@ function TeamCreation() {
|
|
|
498
631
|
const user = useUser({ or: "redirect" });
|
|
499
632
|
const [loading, setLoading] = useState(false);
|
|
500
633
|
if (!project.config.clientTeamCreationEnabled) {
|
|
501
|
-
return /* @__PURE__ */ jsx(MessageCard, { title: "Team creation is not enabled" });
|
|
634
|
+
return /* @__PURE__ */ jsx(MessageCard, { title: t("Team creation is not enabled") });
|
|
502
635
|
}
|
|
503
636
|
const onSubmit = async (data) => {
|
|
504
637
|
setLoading(true);
|
|
@@ -508,32 +641,83 @@ function TeamCreation() {
|
|
|
508
641
|
setLoading(false);
|
|
509
642
|
}
|
|
510
643
|
};
|
|
511
|
-
return /* @__PURE__ */ jsx(
|
|
644
|
+
return /* @__PURE__ */ jsx(PageLayout, { children: /* @__PURE__ */ jsx(Section, { title: t("Create a Team"), description: t("Enter a display name for your new team"), children: /* @__PURE__ */ jsxs(
|
|
512
645
|
"form",
|
|
513
646
|
{
|
|
514
|
-
className: "flex flex-col items-stretch stack-scope",
|
|
515
647
|
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
|
516
648
|
noValidate: true,
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
649
|
+
className: "flex gap-2",
|
|
650
|
+
children: [
|
|
651
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1", children: [
|
|
520
652
|
/* @__PURE__ */ jsx(
|
|
521
653
|
Input,
|
|
522
654
|
{
|
|
523
|
-
id: "
|
|
524
|
-
type: "
|
|
655
|
+
id: "displayName",
|
|
656
|
+
type: "text",
|
|
525
657
|
...register("displayName")
|
|
526
658
|
}
|
|
527
|
-
)
|
|
659
|
+
),
|
|
660
|
+
/* @__PURE__ */ jsx(FormWarningText, { text: errors.displayName?.message?.toString() })
|
|
528
661
|
] }),
|
|
529
|
-
/* @__PURE__ */ jsx(
|
|
530
|
-
|
|
531
|
-
] })
|
|
662
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: t("Create") })
|
|
663
|
+
]
|
|
532
664
|
}
|
|
533
665
|
) }) });
|
|
534
666
|
}
|
|
667
|
+
function useDeleteAccountSection() {
|
|
668
|
+
const { t } = useTranslation();
|
|
669
|
+
const user = useUser({ or: "redirect" });
|
|
670
|
+
const app = useStackApp();
|
|
671
|
+
const project = app.useProject();
|
|
672
|
+
const [deleting, setDeleting] = useState(false);
|
|
673
|
+
if (!project.config.clientUserDeletionEnabled) {
|
|
674
|
+
return null;
|
|
675
|
+
}
|
|
676
|
+
return /* @__PURE__ */ jsx(
|
|
677
|
+
Section,
|
|
678
|
+
{
|
|
679
|
+
title: t("Delete Account"),
|
|
680
|
+
description: t("Permanently remove your account and all associated data"),
|
|
681
|
+
children: /* @__PURE__ */ jsx("div", { className: "stack-scope flex flex-col items-stretch", children: /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxs(AccordionItem, { value: "item-1", children: [
|
|
682
|
+
/* @__PURE__ */ jsx(AccordionTrigger, { children: t("Danger zone") }),
|
|
683
|
+
/* @__PURE__ */ jsx(AccordionContent, { children: !deleting ? /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
|
684
|
+
Button,
|
|
685
|
+
{
|
|
686
|
+
variant: "destructive",
|
|
687
|
+
onClick: () => setDeleting(true),
|
|
688
|
+
children: t("Delete account")
|
|
689
|
+
}
|
|
690
|
+
) }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
691
|
+
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Are you sure you want to delete your account? This action is IRREVERSIBLE and will delete ALL associated data.") }),
|
|
692
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
693
|
+
/* @__PURE__ */ jsx(
|
|
694
|
+
Button,
|
|
695
|
+
{
|
|
696
|
+
variant: "destructive",
|
|
697
|
+
onClick: async () => {
|
|
698
|
+
await user.delete();
|
|
699
|
+
await app.redirectToHome();
|
|
700
|
+
},
|
|
701
|
+
children: t("Delete Account")
|
|
702
|
+
}
|
|
703
|
+
),
|
|
704
|
+
/* @__PURE__ */ jsx(
|
|
705
|
+
Button,
|
|
706
|
+
{
|
|
707
|
+
variant: "secondary",
|
|
708
|
+
onClick: () => setDeleting(false),
|
|
709
|
+
children: t("Cancel")
|
|
710
|
+
}
|
|
711
|
+
)
|
|
712
|
+
] })
|
|
713
|
+
] }) })
|
|
714
|
+
] }) }) })
|
|
715
|
+
}
|
|
716
|
+
);
|
|
717
|
+
}
|
|
535
718
|
export {
|
|
536
719
|
AccountSettings,
|
|
537
|
-
TeamCreation
|
|
720
|
+
TeamCreation,
|
|
721
|
+
useDeleteAccountSection
|
|
538
722
|
};
|
|
539
723
|
//# sourceMappingURL=account-settings.js.map
|