@stackframe/stack 2.5.35 → 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 +20 -0
- 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/oauth-button.js +19 -21
- package/dist/components/oauth-button.js.map +1 -1
- package/dist/components/profile-image-editor.js +2 -2
- package/dist/components/profile-image-editor.js.map +1 -1
- package/dist/components/selected-team-switcher.js +1 -1
- package/dist/components/selected-team-switcher.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 +356 -184
- package/dist/components-page/account-settings.js.map +1 -1
- package/dist/components-page/oauth-callback.js +1 -1
- package/dist/components-page/oauth-callback.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/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/oauth-button.js +19 -21
- package/dist/esm/components/oauth-button.js.map +1 -1
- package/dist/esm/components/profile-image-editor.js +2 -2
- package/dist/esm/components/profile-image-editor.js.map +1 -1
- package/dist/esm/components/selected-team-switcher.js +1 -1
- package/dist/esm/components/selected-team-switcher.js.map +1 -1
- package/dist/esm/components-page/account-settings.js +356 -185
- package/dist/esm/components-page/account-settings.js.map +1 -1
- package/dist/esm/components-page/oauth-callback.js +1 -1
- package/dist/esm/components-page/oauth-callback.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 +1555 -1351
- package/dist/esm/generated/quetzal-translations.js.map +1 -1
- package/dist/esm/lib/stack-app.js +8 -1
- package/dist/esm/lib/stack-app.js.map +1 -1
- package/dist/esm/utils/browser-script.js +10 -7
- 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 +2 -2
- package/dist/generated/quetzal-translations.d.ts +2 -2
- package/dist/generated/quetzal-translations.js +1555 -1351
- package/dist/generated/quetzal-translations.js.map +1 -1
- 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/utils/browser-script.js +10 -7
- package/dist/utils/browser-script.js.map +1 -1
- package/package.json +4 -4
|
@@ -9,20 +9,20 @@ 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 { MaybeFullPage } from "../components/elements/maybe-full-page";
|
|
26
26
|
import { useTranslation } from "../lib/translations";
|
|
27
27
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
28
28
|
function AccountSettings(props) {
|
|
@@ -40,25 +40,21 @@ function AccountSettings(props) {
|
|
|
40
40
|
type: "item",
|
|
41
41
|
subpath: "/profile",
|
|
42
42
|
icon: Contact,
|
|
43
|
-
content: /* @__PURE__ */ jsx(
|
|
43
|
+
content: /* @__PURE__ */ jsx(ProfilePage, {})
|
|
44
44
|
},
|
|
45
45
|
{
|
|
46
46
|
title: t("Security"),
|
|
47
47
|
type: "item",
|
|
48
|
-
icon: ShieldCheck,
|
|
49
48
|
subpath: "/security",
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
/* @__PURE__ */ jsx(PasswordSection, {}),
|
|
53
|
-
/* @__PURE__ */ jsx(MfaSection, {})
|
|
54
|
-
] })
|
|
49
|
+
icon: ShieldCheck,
|
|
50
|
+
content: /* @__PURE__ */ jsx(SecurityPage, {})
|
|
55
51
|
},
|
|
56
52
|
{
|
|
57
|
-
title: t("
|
|
58
|
-
subpath: "/sign-out",
|
|
53
|
+
title: t("Settings"),
|
|
59
54
|
type: "item",
|
|
60
|
-
|
|
61
|
-
|
|
55
|
+
subpath: "/settings",
|
|
56
|
+
icon: Settings,
|
|
57
|
+
content: /* @__PURE__ */ jsx(SettingsPage, {})
|
|
62
58
|
},
|
|
63
59
|
...props.extraItems?.map((item) => ({
|
|
64
60
|
title: item.title,
|
|
@@ -78,13 +74,7 @@ function AccountSettings(props) {
|
|
|
78
74
|
] }),
|
|
79
75
|
type: "item",
|
|
80
76
|
subpath: `/teams/${team.id}`,
|
|
81
|
-
content: /* @__PURE__ */
|
|
82
|
-
/* @__PURE__ */ jsx(ProfileSettings, { team }),
|
|
83
|
-
/* @__PURE__ */ jsx(ManagementSettings, { team }),
|
|
84
|
-
/* @__PURE__ */ jsx(MemberInvitation, { team }),
|
|
85
|
-
/* @__PURE__ */ jsx(MembersSettings, { team }),
|
|
86
|
-
/* @__PURE__ */ jsx(UserSettings, { team })
|
|
87
|
-
] })
|
|
77
|
+
content: /* @__PURE__ */ jsx(TeamPage, { team })
|
|
88
78
|
})),
|
|
89
79
|
...project.config.clientTeamCreationEnabled ? [{
|
|
90
80
|
title: t("Create a team"),
|
|
@@ -99,42 +89,93 @@ function AccountSettings(props) {
|
|
|
99
89
|
}
|
|
100
90
|
) }) });
|
|
101
91
|
}
|
|
102
|
-
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() {
|
|
103
111
|
const { t } = useTranslation();
|
|
104
112
|
const user = useUser({ or: "redirect" });
|
|
105
|
-
return /* @__PURE__ */ jsxs(
|
|
106
|
-
/* @__PURE__ */
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
+
}
|
|
114
126
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
/* @__PURE__ */
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
+
)
|
|
124
146
|
] });
|
|
125
147
|
}
|
|
126
|
-
function
|
|
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
|
|
164
|
+
] });
|
|
165
|
+
}
|
|
166
|
+
function useEmailVerificationSection() {
|
|
127
167
|
const { t } = useTranslation();
|
|
128
168
|
const user = useUser({ or: "redirect" });
|
|
129
169
|
const [emailSent, setEmailSent] = useState(false);
|
|
130
170
|
if (!user.primaryEmail) {
|
|
131
171
|
return null;
|
|
132
172
|
}
|
|
133
|
-
return /* @__PURE__ */ jsx(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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(
|
|
138
179
|
Button,
|
|
139
180
|
{
|
|
140
181
|
disabled: emailSent,
|
|
@@ -144,11 +185,11 @@ function EmailVerificationSection() {
|
|
|
144
185
|
},
|
|
145
186
|
children: emailSent ? t("Email sent!") : t("Send Verification Email")
|
|
146
187
|
}
|
|
147
|
-
) })
|
|
148
|
-
|
|
149
|
-
|
|
188
|
+
) }) })
|
|
189
|
+
}
|
|
190
|
+
);
|
|
150
191
|
}
|
|
151
|
-
function
|
|
192
|
+
function usePasswordSection() {
|
|
152
193
|
const { t } = useTranslation();
|
|
153
194
|
const passwordSchema = yupObject({
|
|
154
195
|
oldPassword: yupString().required(t("Please enter your old password")),
|
|
@@ -253,7 +294,7 @@ function PasswordSection() {
|
|
|
253
294
|
) })
|
|
254
295
|
] });
|
|
255
296
|
}
|
|
256
|
-
function
|
|
297
|
+
function useMfaSection() {
|
|
257
298
|
const { t } = useTranslation();
|
|
258
299
|
const project = useStackApp().useProject();
|
|
259
300
|
const user = useUser({ or: "throw" });
|
|
@@ -279,103 +320,165 @@ function MfaSection() {
|
|
|
279
320
|
setIsMaybeWrong(true);
|
|
280
321
|
});
|
|
281
322
|
}, [mfaCode, generatedSecret, handleSubmit]);
|
|
282
|
-
return /* @__PURE__ */ jsx(
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
/* @__PURE__ */
|
|
289
|
-
|
|
290
|
-
|
|
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,
|
|
291
362
|
{
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
setIsMaybeWrong(false);
|
|
295
|
-
setMfaCode(e.target.value);
|
|
296
|
-
},
|
|
297
|
-
placeholder: "123456",
|
|
298
|
-
maxLength: 6,
|
|
299
|
-
disabled: isLoading
|
|
300
|
-
}
|
|
301
|
-
),
|
|
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.") }),
|
|
304
|
-
/* @__PURE__ */ jsx(
|
|
305
|
-
Button,
|
|
306
|
-
{
|
|
307
|
-
className: "mt-4",
|
|
308
|
-
variant: isEnabled ? "secondary" : "default",
|
|
309
|
-
onClick: async () => {
|
|
310
|
-
if (isEnabled) {
|
|
363
|
+
variant: "secondary",
|
|
364
|
+
onClick: async () => {
|
|
311
365
|
await user.update({
|
|
312
366
|
totpMultiFactorSecret: null
|
|
313
367
|
});
|
|
314
|
-
}
|
|
368
|
+
},
|
|
369
|
+
children: t("Disable")
|
|
370
|
+
}
|
|
371
|
+
) : !generatedSecret && /* @__PURE__ */ jsx(
|
|
372
|
+
Button,
|
|
373
|
+
{
|
|
374
|
+
variant: "default",
|
|
375
|
+
onClick: async () => {
|
|
315
376
|
const secret = generateRandomValues(new Uint8Array(20));
|
|
316
377
|
setQrCodeUrl(await generateTotpQrCode(project, user, secret));
|
|
317
378
|
setGeneratedSecret(secret);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
326
|
-
)
|
|
327
|
-
] })
|
|
328
|
-
] }) });
|
|
379
|
+
},
|
|
380
|
+
children: t("Enable")
|
|
381
|
+
}
|
|
382
|
+
) })
|
|
383
|
+
] })
|
|
384
|
+
}
|
|
385
|
+
);
|
|
329
386
|
}
|
|
330
387
|
async function generateTotpQrCode(project, user, secret) {
|
|
331
388
|
const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret);
|
|
332
389
|
return await QRCode.toDataURL(uri);
|
|
333
390
|
}
|
|
334
|
-
function
|
|
391
|
+
function useSignOutSection() {
|
|
335
392
|
const { t } = useTranslation();
|
|
336
393
|
const user = useUser({ or: "throw" });
|
|
337
|
-
return /* @__PURE__ */ jsx(
|
|
338
|
-
|
|
394
|
+
return /* @__PURE__ */ jsx(
|
|
395
|
+
Section,
|
|
339
396
|
{
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
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
|
+
) })
|
|
343
407
|
}
|
|
344
|
-
)
|
|
408
|
+
);
|
|
345
409
|
}
|
|
346
|
-
function
|
|
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
|
+
] });
|
|
425
|
+
}
|
|
426
|
+
function useLeaveTeamSection(props) {
|
|
347
427
|
const { t } = useTranslation();
|
|
348
428
|
const user = useUser({ or: "redirect" });
|
|
349
429
|
const [leaving, setLeaving] = useState(false);
|
|
350
|
-
return /* @__PURE__ */ jsx(
|
|
351
|
-
|
|
430
|
+
return /* @__PURE__ */ jsx(
|
|
431
|
+
Section,
|
|
352
432
|
{
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
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
|
+
] })
|
|
356
466
|
}
|
|
357
|
-
)
|
|
358
|
-
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Are you sure you want to leave the team?") }),
|
|
359
|
-
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
360
|
-
/* @__PURE__ */ jsx(Button, { variant: "destructive", onClick: async () => {
|
|
361
|
-
await user.leaveTeam(props.team);
|
|
362
|
-
window.location.reload();
|
|
363
|
-
}, children: t("Leave") }),
|
|
364
|
-
/* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => setLeaving(false), children: t("Cancel") })
|
|
365
|
-
] })
|
|
366
|
-
] }) }) });
|
|
467
|
+
);
|
|
367
468
|
}
|
|
368
|
-
function
|
|
469
|
+
function useTeamProfileImageSection(props) {
|
|
369
470
|
const { t } = useTranslation();
|
|
370
471
|
const user = useUser({ or: "redirect" });
|
|
371
472
|
const updateTeamPermission = user.usePermission(props.team, "$update_team");
|
|
372
473
|
if (!updateTeamPermission) {
|
|
373
474
|
return null;
|
|
374
475
|
}
|
|
375
|
-
return /* @__PURE__ */
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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(
|
|
379
482
|
ProfileImageEditor,
|
|
380
483
|
{
|
|
381
484
|
user: props.team,
|
|
@@ -384,40 +487,53 @@ function ManagementSettings(props) {
|
|
|
384
487
|
}
|
|
385
488
|
}
|
|
386
489
|
)
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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(
|
|
391
506
|
EditableText,
|
|
392
507
|
{
|
|
393
508
|
value: props.team.displayName,
|
|
394
509
|
onSave: async (newDisplayName) => await props.team.update({ displayName: newDisplayName })
|
|
395
510
|
}
|
|
396
511
|
)
|
|
397
|
-
|
|
398
|
-
|
|
512
|
+
}
|
|
513
|
+
);
|
|
399
514
|
}
|
|
400
|
-
function
|
|
515
|
+
function useTeamUserProfileSection(props) {
|
|
401
516
|
const { t } = useTranslation();
|
|
402
517
|
const user = useUser({ or: "redirect" });
|
|
403
518
|
const profile = user.useTeamProfile(props.team);
|
|
404
|
-
return /* @__PURE__ */ jsx(
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
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
|
+
}
|
|
415
531
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
532
|
+
)
|
|
533
|
+
}
|
|
534
|
+
);
|
|
419
535
|
}
|
|
420
|
-
function
|
|
536
|
+
function useMemberInvitationSection(props) {
|
|
421
537
|
const { t } = useTranslation();
|
|
422
538
|
const invitationSchema = yupObject({
|
|
423
539
|
email: yupString().email().required(t("Please enter an email address"))
|
|
@@ -444,35 +560,39 @@ function MemberInvitation(props) {
|
|
|
444
560
|
useEffect(() => {
|
|
445
561
|
setInvitedEmail(null);
|
|
446
562
|
}, [watch("email")]);
|
|
447
|
-
return /* @__PURE__ */
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
"
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
+
);
|
|
474
594
|
}
|
|
475
|
-
function
|
|
595
|
+
function useMemberListSection(props) {
|
|
476
596
|
const { t } = useTranslation();
|
|
477
597
|
const user = useUser({ or: "redirect" });
|
|
478
598
|
const readMemberPermission = user.usePermission(props.team, "$read_members");
|
|
@@ -485,8 +605,8 @@ function MembersSettings(props) {
|
|
|
485
605
|
return null;
|
|
486
606
|
}
|
|
487
607
|
return /* @__PURE__ */ jsxs("div", { children: [
|
|
488
|
-
/* @__PURE__ */ jsx(
|
|
489
|
-
/* @__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: [
|
|
490
610
|
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
491
611
|
/* @__PURE__ */ jsx(TableHead, { className: "w-[100px]", children: t("User") }),
|
|
492
612
|
/* @__PURE__ */ jsx(TableHead, { className: "w-[200px]", children: t("Name") })
|
|
@@ -495,7 +615,7 @@ function MembersSettings(props) {
|
|
|
495
615
|
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(UserAvatar, { user: teamProfile }) }),
|
|
496
616
|
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { children: teamProfile.displayName }) })
|
|
497
617
|
] }, id)) })
|
|
498
|
-
] })
|
|
618
|
+
] }) })
|
|
499
619
|
] });
|
|
500
620
|
}
|
|
501
621
|
function TeamCreation() {
|
|
@@ -521,32 +641,83 @@ function TeamCreation() {
|
|
|
521
641
|
setLoading(false);
|
|
522
642
|
}
|
|
523
643
|
};
|
|
524
|
-
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(
|
|
525
645
|
"form",
|
|
526
646
|
{
|
|
527
|
-
className: "flex flex-col items-stretch stack-scope",
|
|
528
647
|
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
|
529
648
|
noValidate: true,
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
649
|
+
className: "flex gap-2",
|
|
650
|
+
children: [
|
|
651
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1", children: [
|
|
533
652
|
/* @__PURE__ */ jsx(
|
|
534
653
|
Input,
|
|
535
654
|
{
|
|
536
|
-
id: "
|
|
537
|
-
type: "
|
|
655
|
+
id: "displayName",
|
|
656
|
+
type: "text",
|
|
538
657
|
...register("displayName")
|
|
539
658
|
}
|
|
540
|
-
)
|
|
659
|
+
),
|
|
660
|
+
/* @__PURE__ */ jsx(FormWarningText, { text: errors.displayName?.message?.toString() })
|
|
541
661
|
] }),
|
|
542
|
-
/* @__PURE__ */ jsx(
|
|
543
|
-
|
|
544
|
-
] })
|
|
662
|
+
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: t("Create") })
|
|
663
|
+
]
|
|
545
664
|
}
|
|
546
665
|
) }) });
|
|
547
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
|
+
}
|
|
548
718
|
export {
|
|
549
719
|
AccountSettings,
|
|
550
|
-
TeamCreation
|
|
720
|
+
TeamCreation,
|
|
721
|
+
useDeleteAccountSection
|
|
551
722
|
};
|
|
552
723
|
//# sourceMappingURL=account-settings.js.map
|