@stackframe/stack 2.5.17 → 2.5.19

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.
Files changed (108) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/components/credential-sign-in.js +4 -1
  3. package/dist/components/credential-sign-in.js.map +1 -1
  4. package/dist/components/elements/sidebar-layout.d.mts +21 -0
  5. package/dist/components/elements/sidebar-layout.d.ts +21 -0
  6. package/dist/components/elements/sidebar-layout.js +125 -0
  7. package/dist/components/elements/sidebar-layout.js.map +1 -0
  8. package/dist/components/elements/user-avatar.d.mts +5 -11
  9. package/dist/components/elements/user-avatar.d.ts +5 -11
  10. package/dist/components/elements/user-avatar.js.map +1 -1
  11. package/dist/components/message-cards/predefined-message-card.d.mts +1 -1
  12. package/dist/components/message-cards/predefined-message-card.d.ts +1 -1
  13. package/dist/components/message-cards/predefined-message-card.js +8 -0
  14. package/dist/components/message-cards/predefined-message-card.js.map +1 -1
  15. package/dist/components/selected-team-switcher.d.mts +1 -0
  16. package/dist/components/selected-team-switcher.d.ts +1 -0
  17. package/dist/components/selected-team-switcher.js +35 -5
  18. package/dist/components/selected-team-switcher.js.map +1 -1
  19. package/dist/components-page/account-settings.js +123 -31
  20. package/dist/components-page/account-settings.js.map +1 -1
  21. package/dist/components-page/auth-page.d.mts +1 -0
  22. package/dist/components-page/auth-page.d.ts +1 -0
  23. package/dist/components-page/auth-page.js +4 -1
  24. package/dist/components-page/auth-page.js.map +1 -1
  25. package/dist/components-page/forgot-password.d.mts +4 -1
  26. package/dist/components-page/forgot-password.d.ts +4 -1
  27. package/dist/components-page/forgot-password.js +55 -7
  28. package/dist/components-page/forgot-password.js.map +1 -1
  29. package/dist/components-page/password-reset.d.mts +6 -2
  30. package/dist/components-page/password-reset.d.ts +6 -2
  31. package/dist/components-page/password-reset.js +100 -8
  32. package/dist/components-page/password-reset.js.map +1 -1
  33. package/dist/components-page/stack-handler.d.mts +1 -0
  34. package/dist/components-page/stack-handler.d.ts +1 -0
  35. package/dist/components-page/stack-handler.js +32 -6
  36. package/dist/components-page/stack-handler.js.map +1 -1
  37. package/dist/{components/password-reset-form.d.mts → components-page/team-creation.d.mts} +2 -3
  38. package/dist/{components/password-reset-form.d.ts → components-page/team-creation.d.ts} +2 -3
  39. package/dist/components-page/team-creation.js +92 -0
  40. package/dist/components-page/team-creation.js.map +1 -0
  41. package/dist/components-page/team-invitation.d.mts +8 -0
  42. package/dist/components-page/team-invitation.d.ts +8 -0
  43. package/dist/components-page/team-invitation.js +141 -0
  44. package/dist/components-page/team-invitation.js.map +1 -0
  45. package/dist/components-page/team-settings.d.mts +8 -0
  46. package/dist/components-page/team-settings.d.ts +8 -0
  47. package/dist/components-page/team-settings.js +139 -0
  48. package/dist/components-page/team-settings.js.map +1 -0
  49. package/dist/esm/components/credential-sign-in.js +4 -1
  50. package/dist/esm/components/credential-sign-in.js.map +1 -1
  51. package/dist/esm/components/elements/sidebar-layout.js +90 -0
  52. package/dist/esm/components/elements/sidebar-layout.js.map +1 -0
  53. package/dist/esm/components/elements/user-avatar.js.map +1 -1
  54. package/dist/esm/components/message-cards/predefined-message-card.js +8 -0
  55. package/dist/esm/components/message-cards/predefined-message-card.js.map +1 -1
  56. package/dist/esm/components/selected-team-switcher.js +39 -7
  57. package/dist/esm/components/selected-team-switcher.js.map +1 -1
  58. package/dist/esm/components-page/account-settings.js +114 -32
  59. package/dist/esm/components-page/account-settings.js.map +1 -1
  60. package/dist/esm/components-page/auth-page.js +4 -1
  61. package/dist/esm/components-page/auth-page.js.map +1 -1
  62. package/dist/esm/components-page/forgot-password.js +52 -5
  63. package/dist/esm/components-page/forgot-password.js.map +1 -1
  64. package/dist/esm/components-page/password-reset.js +101 -9
  65. package/dist/esm/components-page/password-reset.js.map +1 -1
  66. package/dist/esm/components-page/stack-handler.js +32 -6
  67. package/dist/esm/components-page/stack-handler.js.map +1 -1
  68. package/dist/esm/components-page/team-creation.js +68 -0
  69. package/dist/esm/components-page/team-creation.js.map +1 -0
  70. package/dist/esm/components-page/team-invitation.js +107 -0
  71. package/dist/esm/components-page/team-invitation.js.map +1 -0
  72. package/dist/esm/components-page/team-settings.js +115 -0
  73. package/dist/esm/components-page/team-settings.js.map +1 -0
  74. package/dist/esm/generated/global-css.js +1 -1
  75. package/dist/esm/generated/global-css.js.map +1 -1
  76. package/dist/esm/lib/auth.js +5 -1
  77. package/dist/esm/lib/auth.js.map +1 -1
  78. package/dist/esm/lib/stack-app.js +272 -25
  79. package/dist/esm/lib/stack-app.js.map +1 -1
  80. package/dist/generated/global-css.d.mts +1 -1
  81. package/dist/generated/global-css.d.ts +1 -1
  82. package/dist/generated/global-css.js +1 -1
  83. package/dist/generated/global-css.js.map +1 -1
  84. package/dist/index.d.mts +2 -1
  85. package/dist/index.d.ts +2 -1
  86. package/dist/lib/auth.js +5 -1
  87. package/dist/lib/auth.js.map +1 -1
  88. package/dist/lib/hooks.d.mts +1 -0
  89. package/dist/lib/hooks.d.ts +1 -0
  90. package/dist/lib/stack-app.d.mts +64 -5
  91. package/dist/lib/stack-app.d.ts +64 -5
  92. package/dist/lib/stack-app.js +272 -25
  93. package/dist/lib/stack-app.js.map +1 -1
  94. package/dist/providers/stack-provider-client.d.mts +1 -0
  95. package/dist/providers/stack-provider-client.d.ts +1 -0
  96. package/dist/providers/stack-provider.d.mts +1 -0
  97. package/dist/providers/stack-provider.d.ts +1 -0
  98. package/package.json +10 -6
  99. package/dist/components/forgot-password-form.d.mts +0 -7
  100. package/dist/components/forgot-password-form.d.ts +0 -7
  101. package/dist/components/forgot-password-form.js +0 -83
  102. package/dist/components/forgot-password-form.js.map +0 -1
  103. package/dist/components/password-reset-form.js +0 -135
  104. package/dist/components/password-reset-form.js.map +0 -1
  105. package/dist/esm/components/forgot-password-form.js +0 -59
  106. package/dist/esm/components/forgot-password-form.js.map +0 -1
  107. package/dist/esm/components/password-reset-form.js +0 -105
  108. package/dist/esm/components/password-reset-form.js.map +0 -1
@@ -2,14 +2,44 @@
2
2
  "use client";
3
3
 
4
4
  // src/components-page/account-settings.tsx
5
- import { useUser } from "..";
6
- import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
7
- import { UserAvatar } from "../components/elements/user-avatar";
8
- import { useState } from "react";
9
- import { FormWarningText } from "../components/elements/form-warning";
10
5
  import { getPasswordError } from "@stackframe/stack-shared/dist/helpers/password";
11
- import { Button, Card, CardContent, CardFooter, CardHeader, Container, Input, Label, PasswordInput, Typography, cn } from "@stackframe/stack-ui";
6
+ import { useAsyncCallback } from "@stackframe/stack-shared/dist/hooks/use-async-callback";
7
+ import { generateRandomValues } from "@stackframe/stack-shared/dist/utils/crypto";
8
+ import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
9
+ import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
10
+ import { Button, Card, CardContent, CardFooter, CardHeader, Container, Input, Label, PasswordInput, Typography } from "@stackframe/stack-ui";
11
+ import { Contact, Settings, ShieldCheck } from "lucide-react";
12
+ import { TOTPController, createTOTPKeyURI } from "oslo/otp";
13
+ import * as QRCode from "qrcode";
14
+ import { useEffect, useState } from "react";
15
+ import { useStackApp, useUser } from "..";
16
+ import { FormWarningText } from "../components/elements/form-warning";
17
+ import { SidebarLayout } from "../components/elements/sidebar-layout";
18
+ import { UserAvatar } from "../components/elements/user-avatar";
12
19
  import { jsx, jsxs } from "react/jsx-runtime";
20
+ function AccountSettings({ fullPage = false }) {
21
+ const user = useUser({ or: "redirect" });
22
+ const inner = /* @__PURE__ */ jsx(
23
+ SidebarLayout,
24
+ {
25
+ items: [
26
+ { title: "My Profile", content: /* @__PURE__ */ jsx(ProfileSection, {}), icon: Contact },
27
+ { title: "Security", content: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
28
+ /* @__PURE__ */ jsx(EmailVerificationSection, {}),
29
+ /* @__PURE__ */ jsx(PasswordSection, {}),
30
+ /* @__PURE__ */ jsx(MfaSection, {})
31
+ ] }), icon: ShieldCheck },
32
+ { title: "Settings", content: /* @__PURE__ */ jsx(SignOutSection, {}), icon: Settings }
33
+ ].filter(({ content }) => content),
34
+ title: "Team Settings"
35
+ }
36
+ );
37
+ if (fullPage) {
38
+ return /* @__PURE__ */ jsx(Container, { size: 800, className: "stack-scope", children: inner });
39
+ } else {
40
+ return inner;
41
+ }
42
+ }
13
43
  function SettingSection(props) {
14
44
  return /* @__PURE__ */ jsxs(Card, { children: [
15
45
  /* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { children: [
@@ -83,12 +113,12 @@ function EmailVerificationSection() {
83
113
  await user?.sendVerificationEmail();
84
114
  setEmailSent(true);
85
115
  },
86
- children: user?.primaryEmailVerified ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: "Your email has been verified" }) : /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: "Your email has not been verified" })
116
+ children: user?.primaryEmailVerified ? /* @__PURE__ */ jsx(Typography, { variant: "success", children: "Your email has been verified." }) : /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: "Your email has not been verified." })
87
117
  }
88
118
  );
89
119
  }
90
120
  function PasswordSection() {
91
- const user = useUser();
121
+ const user = useUser({ or: "throw" });
92
122
  const [oldPassword, setOldPassword] = useState("");
93
123
  const [oldPasswordError, setOldPasswordError] = useState("");
94
124
  const [newPassword, setNewPassword] = useState("");
@@ -96,7 +126,7 @@ function PasswordSection() {
96
126
  const [repeatNewPassword, setRepeatNewPassword] = useState("");
97
127
  const [repeatNewPasswordError, setRepeatNewPasswordError] = useState("");
98
128
  const [passwordChanged, setPasswordChanged] = useState(false);
99
- if (!user?.hasPassword) {
129
+ if (!user.hasPassword) {
100
130
  return null;
101
131
  }
102
132
  return /* @__PURE__ */ jsxs(
@@ -193,8 +223,81 @@ function PasswordSection() {
193
223
  }
194
224
  );
195
225
  }
226
+ function MfaSection() {
227
+ const project = useStackApp().useProject();
228
+ const user = useUser({ or: "throw" });
229
+ const [generatedSecret, setGeneratedSecret] = useState(null);
230
+ const [qrCodeUrl, setQrCodeUrl] = useState(null);
231
+ const [mfaCode, setMfaCode] = useState("");
232
+ const [isMaybeWrong, setIsMaybeWrong] = useState(false);
233
+ const isEnabled = user.isMultiFactorRequired;
234
+ const [handleSubmit, isLoading] = useAsyncCallback(async () => {
235
+ await user.update({
236
+ totpMultiFactorSecret: generatedSecret
237
+ });
238
+ setGeneratedSecret(null);
239
+ setQrCodeUrl(null);
240
+ setMfaCode("");
241
+ }, [generatedSecret, user]);
242
+ useEffect(() => {
243
+ setIsMaybeWrong(false);
244
+ runAsynchronouslyWithAlert(async () => {
245
+ if (generatedSecret && await new TOTPController().verify(mfaCode, generatedSecret)) {
246
+ await handleSubmit();
247
+ }
248
+ setIsMaybeWrong(true);
249
+ });
250
+ }, [mfaCode, generatedSecret, handleSubmit]);
251
+ return /* @__PURE__ */ jsx(
252
+ SettingSection,
253
+ {
254
+ title: "Multi-factor Authentication",
255
+ desc: "Secure your account with an additional layer of security.",
256
+ buttonVariant: "secondary",
257
+ buttonText: isEnabled ? "Disable" : generatedSecret ? "Cancel" : "Enable",
258
+ onButtonClick: async () => {
259
+ if (isEnabled) {
260
+ await user.update({
261
+ totpMultiFactorSecret: null
262
+ });
263
+ } else if (!generatedSecret) {
264
+ const secret = generateRandomValues(new Uint8Array(20));
265
+ setQrCodeUrl(await generateTotpQrCode(project, user, secret));
266
+ setGeneratedSecret(secret);
267
+ } else {
268
+ setGeneratedSecret(null);
269
+ setQrCodeUrl(null);
270
+ setMfaCode("");
271
+ }
272
+ },
273
+ children: 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: [
274
+ /* @__PURE__ */ jsx(Typography, { children: "Scan this QR code with your authenticator app:" }),
275
+ /* @__PURE__ */ jsx("img", { width: 200, height: 200, src: qrCodeUrl ?? throwErr("TOTP QR code failed to generate"), alt: "TOTP multi-factor authentication QR code" }),
276
+ /* @__PURE__ */ jsx(Typography, { children: "Then, enter your six-digit MFA code:" }),
277
+ /* @__PURE__ */ jsx(
278
+ Input,
279
+ {
280
+ value: mfaCode,
281
+ onChange: (e) => {
282
+ setIsMaybeWrong(false);
283
+ setMfaCode(e.target.value);
284
+ },
285
+ placeholder: "123456",
286
+ maxLength: 6,
287
+ disabled: isLoading
288
+ }
289
+ ),
290
+ isMaybeWrong && mfaCode.length === 6 && /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: "Incorrect code. Please try again." })
291
+ ] }) : /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: "Multi-factor authentication is currently disabled." })
292
+ }
293
+ );
294
+ }
295
+ async function generateTotpQrCode(project, user, secret) {
296
+ const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret);
297
+ return await QRCode.toDataURL(uri);
298
+ }
196
299
  function SignOutSection() {
197
- const user = useUser();
300
+ const user = useUser({ or: "throw" });
198
301
  return /* @__PURE__ */ jsx(
199
302
  SettingSection,
200
303
  {
@@ -202,31 +305,10 @@ function SignOutSection() {
202
305
  desc: "Sign out of your account on this device.",
203
306
  buttonVariant: "secondary",
204
307
  buttonText: "Sign Out",
205
- onButtonClick: () => user?.signOut()
308
+ onButtonClick: () => user.signOut()
206
309
  }
207
310
  );
208
311
  }
209
- function AccountSettings({ fullPage = false }) {
210
- const user = useUser();
211
- if (!user) {
212
- return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "signedOut", fullPage });
213
- }
214
- const inner = /* @__PURE__ */ jsxs("div", { className: cn(fullPage ? "p-4" : "", "flex flex-col gap-4"), children: [
215
- /* @__PURE__ */ jsxs("div", { children: [
216
- /* @__PURE__ */ jsx(Typography, { type: "h2", children: "Account Settings" }),
217
- /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: "Manage your account" })
218
- ] }),
219
- /* @__PURE__ */ jsx(ProfileSection, {}),
220
- /* @__PURE__ */ jsx(EmailVerificationSection, {}),
221
- /* @__PURE__ */ jsx(PasswordSection, {}),
222
- /* @__PURE__ */ jsx(SignOutSection, {})
223
- ] });
224
- if (fullPage) {
225
- return /* @__PURE__ */ jsx(Container, { size: 600, className: "stack-scope", children: inner });
226
- } else {
227
- return inner;
228
- }
229
- }
230
312
  export {
231
313
  AccountSettings
232
314
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components-page/account-settings.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\nimport { useUser } from '..';\nimport { PredefinedMessageCard } from '../components/message-cards/predefined-message-card';\nimport { UserAvatar } from '../components/elements/user-avatar';\nimport { useState } from 'react';\nimport { FormWarningText } from '../components/elements/form-warning';\nimport { getPasswordError } from '@stackframe/stack-shared/dist/helpers/password';\nimport { Button, Card, CardContent, CardFooter, CardHeader, Container, Input, Label, PasswordInput, Typography, cn } from '@stackframe/stack-ui';\n\nfunction SettingSection(props: {\n title: string,\n desc: string,\n buttonText?: string,\n buttonDisabled?: boolean,\n onButtonClick?: React.ComponentProps<typeof Button>[\"onClick\"],\n buttonVariant?: 'default' | 'secondary',\n children?: React.ReactNode,\n}) {\n return (\n <Card>\n <CardHeader>\n <div>\n <Typography type='h4'>{props.title}</Typography>\n <Typography type='label' variant='secondary'>{props.desc}</Typography>\n </div>\n </CardHeader>\n {props.children && <CardContent>\n <div className='flex flex-col gap-4'>\n {props.children}\n </div>\n </CardContent>}\n {props.buttonText && <CardFooter>\n <div className='flex justify-end w-full'>\n <Button\n disabled={props.buttonDisabled}\n onClick={props.onButtonClick}\n variant={props.buttonVariant}\n >\n {props.buttonText}\n </Button>\n </div>\n </CardFooter>}\n </Card>\n );\n}\n\nfunction ProfileSection() {\n const user = useUser()!;\n const [userInfo, setUserInfo] = useState<{ displayName: string }>({ displayName: user.displayName || '' });\n const [changed, setChanged] = useState(false);\n\n return (\n <SettingSection\n title='Profile'\n desc='Your profile information'\n buttonDisabled={!changed}\n buttonText='Save'\n onButtonClick={async () => {\n await user.update(userInfo);\n setChanged(false);\n }}\n >\n <div className='flex gap-4 items-center'>\n <UserAvatar user={user} size={50}/>\n <div className='flex flex-col'>\n <Typography>{user.displayName}</Typography>\n <Typography variant='secondary' type='label'>{user.primaryEmail}</Typography>\n </div>\n </div>\n\n <div className='flex flex-col'>\n <Label htmlFor='display-name' className='mb-1'>Display Name</Label>\n <Input\n id='display-name'\n value={userInfo.displayName}\n onChange={(e) => {\n setUserInfo((i) => ({...i, displayName: e.target.value }));\n setChanged(true);\n }}\n />\n </div>\n </SettingSection>\n );\n}\n\nfunction EmailVerificationSection() {\n const user = useUser();\n const [emailSent, setEmailSent] = useState(false);\n\n return (\n <SettingSection\n title='Email Verification'\n desc='We want to make sure that you own the email address.'\n buttonDisabled={emailSent}\n buttonText={\n !user?.primaryEmailVerified ?\n emailSent ?\n 'Email sent!' :\n 'Send Email'\n : undefined\n }\n onButtonClick={async () => {\n await user?.sendVerificationEmail();\n setEmailSent(true);\n }}\n >\n {user?.primaryEmailVerified ?\n <Typography variant='success'>Your email has been verified</Typography> :\n <Typography variant='destructive'>Your email has not been verified</Typography>}\n </SettingSection>\n );\n}\n\nfunction PasswordSection() {\n const user = useUser();\n const [oldPassword, setOldPassword] = useState<string>('');\n const [oldPasswordError, setOldPasswordError] = useState<string>('');\n const [newPassword, setNewPassword] = useState<string>('');\n const [newPasswordError, setNewPasswordError] = useState<string>('');\n const [repeatNewPassword, setRepeatNewPassword] = useState<string>('');\n const [repeatNewPasswordError, setRepeatNewPasswordError] = useState<string>('');\n const [passwordChanged, setPasswordChanged] = useState(false);\n\n if (!user?.hasPassword) {\n return null;\n }\n\n return (\n <SettingSection\n title='Password'\n desc='Change your password here.'\n buttonDisabled={passwordChanged || (!oldPassword && !newPassword && !repeatNewPassword)}\n buttonText={passwordChanged ? \"Password changed!\" : 'Update Password'}\n onButtonClick={async () => {\n setOldPasswordError('');\n setNewPasswordError('');\n setRepeatNewPasswordError('');\n if (!oldPassword) {\n setOldPasswordError('Please enter your old password');\n return;\n } else if (!newPassword) {\n setNewPasswordError('Please enter a new password');\n return;\n } else if (!repeatNewPassword) {\n setRepeatNewPasswordError('Please repeat your new password');\n return;\n } else {\n const errorMessage = getPasswordError(newPassword);\n if (errorMessage) {\n setNewPasswordError(errorMessage.message);\n } else {\n if (newPassword !== repeatNewPassword) {\n setRepeatNewPasswordError('Passwords do not match');\n return;\n }\n const errorCode = await user.updatePassword({ oldPassword, newPassword });\n if (errorCode) {\n setOldPasswordError('Incorrect password');\n } else {\n setOldPassword('');\n setNewPassword('');\n setRepeatNewPassword('');\n setPasswordChanged(true);\n }\n }\n }\n }}\n >\n <div className='flex flex-col'>\n <Label htmlFor='old-password' className='mb-1'>Old Password</Label>\n <PasswordInput\n id='old-password'\n value={oldPassword}\n onChange={(e) => {\n setOldPassword(e.target.value);\n setOldPasswordError('');\n setPasswordChanged(false);\n }}\n />\n <FormWarningText text={oldPasswordError} />\n </div>\n <div className='flex flex-col'>\n <Label htmlFor='new-password' className='mb-1'>New Password</Label>\n <PasswordInput\n id='new-password'\n value={newPassword}\n onChange={(e) => {\n setNewPassword(e.target.value);\n setNewPasswordError('');\n setPasswordChanged(false);\n }}\n />\n <FormWarningText text={newPasswordError} />\n </div>\n <div className='flex flex-col'>\n <Label htmlFor='repeat-new-password' className='mb-1'>Repeat New Password</Label>\n <PasswordInput\n id='repeat-new-password'\n value={repeatNewPassword}\n onChange={(e) => {\n setRepeatNewPassword(e.target.value);\n setRepeatNewPasswordError('');\n setPasswordChanged(false);\n }}\n />\n <FormWarningText text={repeatNewPasswordError} />\n </div>\n </SettingSection>\n );\n}\n\nfunction SignOutSection() {\n const user = useUser();\n return (\n <SettingSection\n title='Sign out'\n desc='Sign out of your account on this device.'\n buttonVariant='secondary'\n buttonText='Sign Out'\n onButtonClick={() => user?.signOut()}\n >\n </SettingSection>\n );\n}\n\nexport function AccountSettings({ fullPage=false }: { fullPage?: boolean }) {\n const user = useUser();\n if (!user) {\n return <PredefinedMessageCard type='signedOut' fullPage={fullPage} />;\n }\n\n const inner = (\n <div className={cn(fullPage ? 'p-4' : '', 'flex flex-col gap-4')}>\n <div>\n <Typography type='h2'>Account Settings</Typography>\n <Typography variant='secondary' type='label'>Manage your account</Typography>\n </div>\n\n <ProfileSection />\n <EmailVerificationSection />\n <PasswordSection />\n <SignOutSection />\n </div>\n );\n\n if (fullPage) {\n return (\n <Container size={600} className='stack-scope'>\n {inner}\n </Container>\n );\n } else {\n return inner;\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components-page/account-settings.tsx"],"sourcesContent":["'use client';\n\nimport { getPasswordError } from '@stackframe/stack-shared/dist/helpers/password';\nimport { useAsyncCallback } from '@stackframe/stack-shared/dist/hooks/use-async-callback';\nimport { generateRandomValues } from '@stackframe/stack-shared/dist/utils/crypto';\nimport { throwErr } from '@stackframe/stack-shared/dist/utils/errors';\nimport { runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/promises';\nimport { Button, Card, CardContent, CardFooter, CardHeader, Container, Input, Label, PasswordInput, Typography } from '@stackframe/stack-ui';\nimport { Contact, Settings, Shield, ShieldCheck } from 'lucide-react';\nimport { TOTPController, createTOTPKeyURI } from \"oslo/otp\";\nimport * as QRCode from 'qrcode';\nimport React, { useEffect, useState } from 'react';\nimport { CurrentUser, Project, useStackApp, useUser } from '..';\nimport { FormWarningText } from '../components/elements/form-warning';\nimport { SidebarLayout } from '../components/elements/sidebar-layout';\nimport { UserAvatar } from '../components/elements/user-avatar';\n\nexport function AccountSettings({ fullPage=false }: { fullPage?: boolean }) {\n const user = useUser({ or: 'redirect' });\n\n const inner = <SidebarLayout\n items={[\n { title: 'My Profile', content: <ProfileSection/>, icon: Contact },\n { title: 'Security', content: <div className='flex flex-col gap-4'>\n <EmailVerificationSection />\n <PasswordSection />\n <MfaSection />\n </div>, icon: ShieldCheck },\n { title: 'Settings', content: <SignOutSection />, icon: Settings },\n ].filter(({ content }) => content as any)}\n title='Team Settings'\n />;\n\n if (fullPage) {\n return (\n <Container size={800} className='stack-scope'>\n {inner}\n </Container>\n );\n } else {\n return inner;\n }\n}\n\n\nfunction SettingSection(props: {\n title: string,\n desc: string,\n buttonText?: string,\n buttonDisabled?: boolean,\n onButtonClick?: React.ComponentProps<typeof Button>[\"onClick\"],\n buttonVariant?: 'default' | 'secondary',\n children?: React.ReactNode,\n}) {\n return (\n <Card>\n <CardHeader>\n <div>\n <Typography type='h4'>{props.title}</Typography>\n <Typography type='label' variant='secondary'>{props.desc}</Typography>\n </div>\n </CardHeader>\n {props.children && <CardContent>\n <div className='flex flex-col gap-4'>\n {props.children}\n </div>\n </CardContent>}\n {props.buttonText && <CardFooter>\n <div className='flex justify-end w-full'>\n <Button\n disabled={props.buttonDisabled}\n onClick={props.onButtonClick}\n variant={props.buttonVariant}\n >\n {props.buttonText}\n </Button>\n </div>\n </CardFooter>}\n </Card>\n );\n}\n\nfunction ProfileSection() {\n const user = useUser()!;\n const [userInfo, setUserInfo] = useState<{ displayName: string }>({ displayName: user.displayName || '' });\n const [changed, setChanged] = useState(false);\n\n return (\n <SettingSection\n title='Profile'\n desc='Your profile information'\n buttonDisabled={!changed}\n buttonText='Save'\n onButtonClick={async () => {\n await user.update(userInfo);\n setChanged(false);\n }}\n >\n <div className='flex gap-4 items-center'>\n <UserAvatar user={user} size={50}/>\n <div className='flex flex-col'>\n <Typography>{user.displayName}</Typography>\n <Typography variant='secondary' type='label'>{user.primaryEmail}</Typography>\n </div>\n </div>\n\n <div className='flex flex-col'>\n <Label htmlFor='display-name' className='mb-1'>Display Name</Label>\n <Input\n id='display-name'\n value={userInfo.displayName}\n onChange={(e) => {\n setUserInfo((i) => ({...i, displayName: e.target.value }));\n setChanged(true);\n }}\n />\n </div>\n </SettingSection>\n );\n}\n\nfunction EmailVerificationSection() {\n const user = useUser();\n const [emailSent, setEmailSent] = useState(false);\n\n return (\n <SettingSection\n title='Email Verification'\n desc='We want to make sure that you own the email address.'\n buttonDisabled={emailSent}\n buttonText={\n !user?.primaryEmailVerified ?\n emailSent ?\n 'Email sent!' :\n 'Send Email'\n : undefined\n }\n onButtonClick={async () => {\n await user?.sendVerificationEmail();\n setEmailSent(true);\n }}\n >\n {user?.primaryEmailVerified ?\n <Typography variant='success'>Your email has been verified.</Typography> :\n <Typography variant='destructive'>Your email has not been verified.</Typography>}\n </SettingSection>\n );\n}\n\nfunction PasswordSection() {\n const user = useUser({ or: \"throw\" });\n const [oldPassword, setOldPassword] = useState<string>('');\n const [oldPasswordError, setOldPasswordError] = useState<string>('');\n const [newPassword, setNewPassword] = useState<string>('');\n const [newPasswordError, setNewPasswordError] = useState<string>('');\n const [repeatNewPassword, setRepeatNewPassword] = useState<string>('');\n const [repeatNewPasswordError, setRepeatNewPasswordError] = useState<string>('');\n const [passwordChanged, setPasswordChanged] = useState(false);\n\n if (!user.hasPassword) {\n return null;\n }\n\n return (\n <SettingSection\n title='Password'\n desc='Change your password here.'\n buttonDisabled={passwordChanged || (!oldPassword && !newPassword && !repeatNewPassword)}\n buttonText={passwordChanged ? \"Password changed!\" : 'Update Password'}\n onButtonClick={async () => {\n setOldPasswordError('');\n setNewPasswordError('');\n setRepeatNewPasswordError('');\n if (!oldPassword) {\n setOldPasswordError('Please enter your old password');\n return;\n } else if (!newPassword) {\n setNewPasswordError('Please enter a new password');\n return;\n } else if (!repeatNewPassword) {\n setRepeatNewPasswordError('Please repeat your new password');\n return;\n } else {\n const errorMessage = getPasswordError(newPassword);\n if (errorMessage) {\n setNewPasswordError(errorMessage.message);\n } else {\n if (newPassword !== repeatNewPassword) {\n setRepeatNewPasswordError('Passwords do not match');\n return;\n }\n const errorCode = await user.updatePassword({ oldPassword, newPassword });\n if (errorCode) {\n setOldPasswordError('Incorrect password');\n } else {\n setOldPassword('');\n setNewPassword('');\n setRepeatNewPassword('');\n setPasswordChanged(true);\n }\n }\n }\n }}\n >\n <div className='flex flex-col'>\n <Label htmlFor='old-password' className='mb-1'>Old Password</Label>\n <PasswordInput\n id='old-password'\n value={oldPassword}\n onChange={(e) => {\n setOldPassword(e.target.value);\n setOldPasswordError('');\n setPasswordChanged(false);\n }}\n />\n <FormWarningText text={oldPasswordError} />\n </div>\n <div className='flex flex-col'>\n <Label htmlFor='new-password' className='mb-1'>New Password</Label>\n <PasswordInput\n id='new-password'\n value={newPassword}\n onChange={(e) => {\n setNewPassword(e.target.value);\n setNewPasswordError('');\n setPasswordChanged(false);\n }}\n />\n <FormWarningText text={newPasswordError} />\n </div>\n <div className='flex flex-col'>\n <Label htmlFor='repeat-new-password' className='mb-1'>Repeat New Password</Label>\n <PasswordInput\n id='repeat-new-password'\n value={repeatNewPassword}\n onChange={(e) => {\n setRepeatNewPassword(e.target.value);\n setRepeatNewPasswordError('');\n setPasswordChanged(false);\n }}\n />\n <FormWarningText text={repeatNewPasswordError} />\n </div>\n </SettingSection>\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 <SettingSection\n title='Multi-factor Authentication'\n desc='Secure your account with an additional layer of security.'\n buttonVariant='secondary'\n buttonText={isEnabled ? 'Disable' : (generatedSecret ? 'Cancel' : 'Enable')}\n onButtonClick={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 ? (\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 </SettingSection>\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 <SettingSection\n title='Sign out'\n desc='Sign out of your account on this device.'\n buttonVariant='secondary'\n buttonText='Sign Out'\n onButtonClick={() => user.signOut()}\n >\n </SettingSection>\n );\n}"],"mappings":";;;AAEA,SAAS,wBAAwB;AACjC,SAAS,wBAAwB;AACjC,SAAS,4BAA4B;AACrC,SAAS,gBAAgB;AACzB,SAAS,kCAAkC;AAC3C,SAAS,QAAQ,MAAM,aAAa,YAAY,YAAY,WAAW,OAAO,OAAO,eAAe,kBAAkB;AACtH,SAAS,SAAS,UAAkB,mBAAmB;AACvD,SAAS,gBAAgB,wBAAwB;AACjD,YAAY,YAAY;AACxB,SAAgB,WAAW,gBAAgB;AAC3C,SAA+B,aAAa,eAAe;AAC3D,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAOW,cACF,YADE;AAL/B,SAAS,gBAAgB,EAAE,WAAS,MAAM,GAA2B;AAC1E,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AAEvC,QAAM,QAAQ;AAAA,IAAC;AAAA;AAAA,MACb,OAAO;AAAA,QACL,EAAE,OAAO,cAAc,SAAS,oBAAC,kBAAc,GAAI,MAAM,QAAQ;AAAA,QACjE,EAAE,OAAO,YAAY,SAAS,qBAAC,SAAI,WAAU,uBAC3C;AAAA,8BAAC,4BAAyB;AAAA,UAC1B,oBAAC,mBAAgB;AAAA,UACjB,oBAAC,cAAW;AAAA,WACd,GAAQ,MAAM,YAAY;AAAA,QAC1B,EAAE,OAAO,YAAY,SAAS,oBAAC,kBAAe,GAAI,MAAM,SAAS;AAAA,MACnE,EAAE,OAAO,CAAC,EAAE,QAAQ,MAAM,OAAc;AAAA,MACxC,OAAM;AAAA;AAAA,EACR;AAEA,MAAI,UAAU;AACZ,WACE,oBAAC,aAAU,MAAM,KAAK,WAAU,eAC7B,iBACH;AAAA,EAEJ,OAAO;AACL,WAAO;AAAA,EACT;AACF;AAGA,SAAS,eAAe,OAQrB;AACD,SACE,qBAAC,QACC;AAAA,wBAAC,cACC,+BAAC,SACC;AAAA,0BAAC,cAAW,MAAK,MAAM,gBAAM,OAAM;AAAA,MACnC,oBAAC,cAAW,MAAK,SAAQ,SAAQ,aAAa,gBAAM,MAAK;AAAA,OAC3D,GACF;AAAA,IACC,MAAM,YAAY,oBAAC,eAClB,8BAAC,SAAI,WAAU,uBACZ,gBAAM,UACT,GACF;AAAA,IACC,MAAM,cAAc,oBAAC,cACpB,8BAAC,SAAI,WAAU,2BACb;AAAA,MAAC;AAAA;AAAA,QACC,UAAU,MAAM;AAAA,QAChB,SAAS,MAAM;AAAA,QACf,SAAS,MAAM;AAAA,QAEd,gBAAM;AAAA;AAAA,IACT,GACF,GACF;AAAA,KACF;AAEJ;AAEA,SAAS,iBAAiB;AACxB,QAAM,OAAO,QAAQ;AACrB,QAAM,CAAC,UAAU,WAAW,IAAI,SAAkC,EAAE,aAAa,KAAK,eAAe,GAAG,CAAC;AACzG,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,MAAK;AAAA,MACL,gBAAgB,CAAC;AAAA,MACjB,YAAW;AAAA,MACX,eAAe,YAAY;AACzB,cAAM,KAAK,OAAO,QAAQ;AAC1B,mBAAW,KAAK;AAAA,MAClB;AAAA,MAEA;AAAA,6BAAC,SAAI,WAAU,2BACb;AAAA,8BAAC,cAAW,MAAY,MAAM,IAAG;AAAA,UACjC,qBAAC,SAAI,WAAU,iBACb;AAAA,gCAAC,cAAY,eAAK,aAAY;AAAA,YAC9B,oBAAC,cAAW,SAAQ,aAAY,MAAK,SAAS,eAAK,cAAa;AAAA,aAClE;AAAA,WACF;AAAA,QAEA,qBAAC,SAAI,WAAU,iBACb;AAAA,8BAAC,SAAM,SAAQ,gBAAe,WAAU,QAAO,0BAAY;AAAA,UAC3D;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,OAAO,SAAS;AAAA,cAChB,UAAU,CAAC,MAAM;AACf,4BAAY,CAAC,OAAO,EAAC,GAAG,GAAG,aAAa,EAAE,OAAO,MAAM,EAAE;AACzD,2BAAW,IAAI;AAAA,cACjB;AAAA;AAAA,UACF;AAAA,WACF;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,2BAA2B;AAClC,QAAM,OAAO,QAAQ;AACrB,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAEhD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,MAAK;AAAA,MACL,gBAAgB;AAAA,MAChB,YACE,CAAC,MAAM,uBACL,YACE,gBACA,eACA;AAAA,MAEN,eAAe,YAAY;AACzB,cAAM,MAAM,sBAAsB;AAClC,qBAAa,IAAI;AAAA,MACnB;AAAA,MAEC,gBAAM,uBACL,oBAAC,cAAW,SAAQ,WAAU,2CAA6B,IAC3D,oBAAC,cAAW,SAAQ,eAAc,+CAAiC;AAAA;AAAA,EACvE;AAEJ;AAEA,SAAS,kBAAkB;AACzB,QAAM,OAAO,QAAQ,EAAE,IAAI,QAAQ,CAAC;AACpC,QAAM,CAAC,aAAa,cAAc,IAAI,SAAiB,EAAE;AACzD,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAiB,EAAE;AACnE,QAAM,CAAC,aAAa,cAAc,IAAI,SAAiB,EAAE;AACzD,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAiB,EAAE;AACnE,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAiB,EAAE;AACrE,QAAM,CAAC,wBAAwB,yBAAyB,IAAI,SAAiB,EAAE;AAC/E,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAS,KAAK;AAE5D,MAAI,CAAC,KAAK,aAAa;AACrB,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,MAAK;AAAA,MACL,gBAAgB,mBAAoB,CAAC,eAAe,CAAC,eAAe,CAAC;AAAA,MACrE,YAAY,kBAAkB,sBAAsB;AAAA,MACpD,eAAe,YAAY;AACzB,4BAAoB,EAAE;AACtB,4BAAoB,EAAE;AACtB,kCAA0B,EAAE;AAC5B,YAAI,CAAC,aAAa;AAChB,8BAAoB,gCAAgC;AACpD;AAAA,QACF,WAAW,CAAC,aAAa;AACvB,8BAAoB,6BAA6B;AACjD;AAAA,QACF,WAAW,CAAC,mBAAmB;AAC3B,oCAA0B,iCAAiC;AAC3D;AAAA,QACJ,OAAO;AACL,gBAAM,eAAe,iBAAiB,WAAW;AACjD,cAAI,cAAc;AAChB,gCAAoB,aAAa,OAAO;AAAA,UAC1C,OAAO;AACL,gBAAI,gBAAgB,mBAAmB;AACrC,wCAA0B,wBAAwB;AAClD;AAAA,YACF;AACA,kBAAM,YAAY,MAAM,KAAK,eAAe,EAAE,aAAa,YAAY,CAAC;AACxE,gBAAI,WAAW;AACb,kCAAoB,oBAAoB;AAAA,YAC1C,OAAO;AACL,6BAAe,EAAE;AACjB,6BAAe,EAAE;AACjB,mCAAqB,EAAE;AACvB,iCAAmB,IAAI;AAAA,YACzB;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,MAEA;AAAA,6BAAC,SAAI,WAAU,iBACb;AAAA,8BAAC,SAAM,SAAQ,gBAAe,WAAU,QAAO,0BAAY;AAAA,UAC3D;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,OAAO;AAAA,cACP,UAAU,CAAC,MAAM;AACf,+BAAe,EAAE,OAAO,KAAK;AAC7B,oCAAoB,EAAE;AACtB,mCAAmB,KAAK;AAAA,cAC1B;AAAA;AAAA,UACF;AAAA,UACA,oBAAC,mBAAgB,MAAM,kBAAkB;AAAA,WAC3C;AAAA,QACA,qBAAC,SAAI,WAAU,iBACb;AAAA,8BAAC,SAAM,SAAQ,gBAAe,WAAU,QAAO,0BAAY;AAAA,UAC3D;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,OAAO;AAAA,cACP,UAAU,CAAC,MAAM;AACf,+BAAe,EAAE,OAAO,KAAK;AAC7B,oCAAoB,EAAE;AACtB,mCAAmB,KAAK;AAAA,cAC1B;AAAA;AAAA,UACF;AAAA,UACA,oBAAC,mBAAgB,MAAM,kBAAkB;AAAA,WAC3C;AAAA,QACA,qBAAC,SAAI,WAAU,iBACb;AAAA,8BAAC,SAAM,SAAQ,uBAAsB,WAAU,QAAO,iCAAmB;AAAA,UACzE;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,OAAO;AAAA,cACP,UAAU,CAAC,MAAM;AACf,qCAAqB,EAAE,OAAO,KAAK;AACnC,0CAA0B,EAAE;AAC5B,mCAAmB,KAAK;AAAA,cAC1B;AAAA;AAAA,UACF;AAAA,UACA,oBAAC,mBAAgB,MAAM,wBAAwB;AAAA,WACjD;AAAA;AAAA;AAAA,EACF;AAEJ;AAEA,SAAS,aAAa;AACpB,QAAM,UAAU,YAAY,EAAE,WAAW;AACzC,QAAM,OAAO,QAAQ,EAAE,IAAI,QAAQ,CAAC;AACpC,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAA4B,IAAI;AAC9E,QAAM,CAAC,WAAW,YAAY,IAAI,SAAwB,IAAI;AAC9D,QAAM,CAAC,SAAS,UAAU,IAAI,SAAiB,EAAE;AACjD,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,YAAY,KAAK;AAEvB,QAAM,CAAC,cAAc,SAAS,IAAI,iBAAiB,YAAY;AAC7D,UAAM,KAAK,OAAO;AAAA,MAChB,uBAAuB;AAAA,IACzB,CAAC;AACD,uBAAmB,IAAI;AACvB,iBAAa,IAAI;AACjB,eAAW,EAAE;AAAA,EACf,GAAG,CAAC,iBAAiB,IAAI,CAAC;AAE1B,YAAU,MAAM;AACd,oBAAgB,KAAK;AACrB,+BAA2B,YAAY;AACrC,UAAI,mBAAmB,MAAM,IAAI,eAAe,EAAE,OAAO,SAAS,eAAe,GAAG;AAClF,cAAM,aAAa;AAAA,MACrB;AACA,sBAAgB,IAAI;AAAA,IACtB,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,iBAAiB,YAAY,CAAC;AAE3C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,MAAK;AAAA,MACL,eAAc;AAAA,MACd,YAAY,YAAY,YAAa,kBAAkB,WAAW;AAAA,MAClE,eAAe,YAAY;AACzB,YAAI,WAAW;AACb,gBAAM,KAAK,OAAO;AAAA,YAChB,uBAAuB;AAAA,UACzB,CAAC;AAAA,QACH,WAAW,CAAC,iBAAiB;AAC3B,gBAAM,SAAS,qBAAqB,IAAI,WAAW,EAAE,CAAC;AACtD,uBAAa,MAAM,mBAAmB,SAAS,MAAM,MAAM,CAAC;AAC5D,6BAAmB,MAAM;AAAA,QAC3B,OAAO;AACL,6BAAmB,IAAI;AACvB,uBAAa,IAAI;AACjB,qBAAW,EAAE;AAAA,QACf;AAAA,MACF;AAAA,MAEC,sBACC,oBAAC,cAAW,SAAQ,WAAU,+DAAiD,IAE/E,kBACE,qBAAC,SAAI,WAAU,oCACb;AAAA,4BAAC,cAAW,4DAA8C;AAAA,QAC1D,oBAAC,SAAI,OAAO,KAAK,QAAQ,KAAK,KAAK,aAAa,SAAS,iCAAiC,GAAG,KAAI,4CAA2C;AAAA,QAC5I,oBAAC,cAAW,kDAAoC;AAAA,QAChD;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,CAAC,MAAM;AACf,8BAAgB,KAAK;AACrB,yBAAW,EAAE,OAAO,KAAK;AAAA,YAC3B;AAAA,YACA,aAAY;AAAA,YACZ,WAAW;AAAA,YACX,UAAU;AAAA;AAAA,QACZ;AAAA,QACC,gBAAgB,QAAQ,WAAW,KAClC,oBAAC,cAAW,SAAQ,eAAc,+CAAiC;AAAA,SAEvE,IAEA,oBAAC,cAAW,SAAQ,eAAc,gEAAkD;AAAA;AAAA,EAG1F;AAEJ;AAEA,eAAe,mBAAmB,SAAkB,MAAmB,QAAoB;AACzF,QAAM,MAAM,iBAAiB,QAAQ,aAAa,KAAK,gBAAgB,KAAK,IAAI,MAAM;AACtF,SAAO,MAAa,iBAAU,GAAG;AACnC;AAEA,SAAS,iBAAiB;AACxB,QAAM,OAAO,QAAQ,EAAE,IAAI,QAAQ,CAAC;AACpC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,MAAK;AAAA,MACL,eAAc;AAAA,MACd,YAAW;AAAA,MACX,eAAe,MAAM,KAAK,QAAQ;AAAA;AAAA,EAEpC;AAEJ;","names":[]}
@@ -34,11 +34,14 @@ function AuthPage({
34
34
  if (user && !mockProject) {
35
35
  return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "signedIn", fullPage });
36
36
  }
37
+ if (type === "sign-up" && !project.config.signUpEnabled) {
38
+ return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "signUpDisabled", fullPage });
39
+ }
37
40
  const enableSeparator = (project.config.credentialEnabled || project.config.magicLinkEnabled) && project.config.oauthProviders.length > 0;
38
41
  return /* @__PURE__ */ jsx(MaybeFullPage, { fullPage, children: /* @__PURE__ */ jsxs("div", { className: "stack-scope flex flex-col items-stretch", children: [
39
42
  /* @__PURE__ */ jsxs("div", { className: "text-center mb-6", children: [
40
43
  /* @__PURE__ */ jsx(Typography, { type: "h2", children: type === "sign-in" ? "Sign in to your account" : "Create a new account" }),
41
- type === "sign-in" ? /* @__PURE__ */ jsxs(Typography, { children: [
44
+ type === "sign-in" ? project.config.signUpEnabled && /* @__PURE__ */ jsxs(Typography, { children: [
42
45
  "Don't have an account? ",
43
46
  /* @__PURE__ */ jsx(StyledLink, { href: stackApp.urls.signUp, onClick: (e) => {
44
47
  runAsynchronously(stackApp.redirectToSignUp());
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components-page/auth-page.tsx"],"sourcesContent":["'use client';\n\nimport { CredentialSignIn } from '../components/credential-sign-in';\nimport { SeparatorWithText } from '../components/elements/separator-with-text';\nimport { OAuthButtonGroup } from '../components/oauth-button-group';\nimport { MaybeFullPage } from '../components/elements/maybe-full-page';\nimport { useUser, useStackApp } from '..';\nimport { PredefinedMessageCard } from '../components/message-cards/predefined-message-card';\nimport { MagicLinkSignIn } from '../components/magic-link-sign-in';\nimport { CredentialSignUp } from '../components/credential-sign-up';\nimport { StyledLink, Tabs, TabsContent, TabsList, TabsTrigger, Typography } from '@stackframe/stack-ui';\nimport { Project } from '../lib/stack-app';\nimport { runAsynchronously } from '@stackframe/stack-shared/dist/utils/promises';\nimport { useEffect } from 'react';\n\nexport function AuthPage({\n fullPage=false,\n type,\n automaticRedirect,\n mockProject,\n}: {\n fullPage?: boolean,\n type: 'sign-in' | 'sign-up',\n automaticRedirect?: boolean,\n mockProject?: {\n config: {\n credentialEnabled: boolean,\n magicLinkEnabled: boolean,\n oauthProviders: {\n id: string,\n }[],\n },\n },\n}) {\n const stackApp = useStackApp();\n const user = useUser();\n const projectFromHook = stackApp.useProject();\n const project = mockProject || projectFromHook;\n\n useEffect(() => {\n if (automaticRedirect) {\n if (user && !mockProject) {\n runAsynchronously(type === 'sign-in' ? stackApp.redirectToAfterSignIn() : stackApp.redirectToAfterSignUp());\n }\n }\n }, [user, mockProject, stackApp, automaticRedirect]);\n\n if (user && !mockProject) {\n return <PredefinedMessageCard type='signedIn' fullPage={fullPage} />;\n }\n\n const enableSeparator = (project.config.credentialEnabled || project.config.magicLinkEnabled) && project.config.oauthProviders.length > 0;\n\n return (\n <MaybeFullPage fullPage={fullPage}>\n <div className='stack-scope flex flex-col items-stretch'>\n <div className=\"text-center mb-6\">\n <Typography type='h2'>\n {type === 'sign-in' ? 'Sign in to your account' : 'Create a new account'}\n </Typography>\n {type === 'sign-in' ? (\n <Typography>\n {\"Don't have an account? \"}\n <StyledLink href={stackApp.urls.signUp} onClick={(e) => {\n runAsynchronously(stackApp.redirectToSignUp());\n e.preventDefault();\n }}>\n Sign up\n </StyledLink>\n </Typography>\n ) : (\n <Typography>\n {\"Already have an account? \"}\n <StyledLink href={stackApp.urls.signIn} onClick={(e) => {\n runAsynchronously(stackApp.redirectToSignIn());\n e.preventDefault();\n }}>\n Sign in\n </StyledLink>\n </Typography>\n )}\n </div>\n <OAuthButtonGroup type={type} mockProject={mockProject} />\n {enableSeparator && <SeparatorWithText text={'Or continue with'} />}\n {project.config.credentialEnabled && project.config.magicLinkEnabled ? (\n <Tabs defaultValue='magic-link'>\n <TabsList className='w-full mb-2'>\n <TabsTrigger value='magic-link' className='flex-1'>Magic Link</TabsTrigger>\n <TabsTrigger value='password' className='flex-1'>Password</TabsTrigger>\n </TabsList>\n <TabsContent value='magic-link'>\n <MagicLinkSignIn/>\n </TabsContent>\n <TabsContent value='password'>\n {type === 'sign-up' ? <CredentialSignUp/> : <CredentialSignIn/>}\n </TabsContent>\n </Tabs>\n ) : project.config.credentialEnabled ? (\n type === 'sign-up' ? <CredentialSignUp/> : <CredentialSignIn/>\n ) : project.config.magicLinkEnabled ? (\n <MagicLinkSignIn/>\n ) : null}\n </div>\n </MaybeFullPage>\n );\n}\n"],"mappings":";;;AAEA,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAC9B,SAAS,SAAS,mBAAmB;AACrC,SAAS,6BAA6B;AACtC,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,YAAY,MAAM,aAAa,UAAU,aAAa,kBAAkB;AAEjF,SAAS,yBAAyB;AAClC,SAAS,iBAAiB;AAmCf,cAaC,YAbD;AAjCJ,SAAS,SAAS;AAAA,EACvB,WAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AACF,GAaG;AACD,QAAM,WAAW,YAAY;AAC7B,QAAM,OAAO,QAAQ;AACrB,QAAM,kBAAkB,SAAS,WAAW;AAC5C,QAAM,UAAU,eAAe;AAE/B,YAAU,MAAM;AACd,QAAI,mBAAmB;AACrB,UAAI,QAAQ,CAAC,aAAa;AACxB,0BAAkB,SAAS,YAAY,SAAS,sBAAsB,IAAI,SAAS,sBAAsB,CAAC;AAAA,MAC5G;AAAA,IACF;AAAA,EACF,GAAG,CAAC,MAAM,aAAa,UAAU,iBAAiB,CAAC;AAEnD,MAAI,QAAQ,CAAC,aAAa;AACxB,WAAO,oBAAC,yBAAsB,MAAK,YAAW,UAAoB;AAAA,EACpE;AAEA,QAAM,mBAAmB,QAAQ,OAAO,qBAAqB,QAAQ,OAAO,qBAAqB,QAAQ,OAAO,eAAe,SAAS;AAExI,SACE,oBAAC,iBAAc,UACb,+BAAC,SAAI,WAAU,2CACb;AAAA,yBAAC,SAAI,WAAU,oBACb;AAAA,0BAAC,cAAW,MAAK,MACd,mBAAS,YAAY,4BAA4B,wBACpD;AAAA,MACC,SAAS,YACR,qBAAC,cACE;AAAA;AAAA,QACD,oBAAC,cAAW,MAAM,SAAS,KAAK,QAAQ,SAAS,CAAC,MAAM;AACtD,4BAAkB,SAAS,iBAAiB,CAAC;AAC7C,YAAE,eAAe;AAAA,QACnB,GAAG,qBAEH;AAAA,SACF,IAEA,qBAAC,cACE;AAAA;AAAA,QACD,oBAAC,cAAW,MAAM,SAAS,KAAK,QAAQ,SAAS,CAAC,MAAM;AACtD,4BAAkB,SAAS,iBAAiB,CAAC;AAC7C,YAAE,eAAe;AAAA,QACnB,GAAG,qBAEH;AAAA,SACF;AAAA,OAEJ;AAAA,IACA,oBAAC,oBAAiB,MAAY,aAA0B;AAAA,IACvD,mBAAmB,oBAAC,qBAAkB,MAAM,oBAAoB;AAAA,IAChE,QAAQ,OAAO,qBAAqB,QAAQ,OAAO,mBAClD,qBAAC,QAAK,cAAa,cACjB;AAAA,2BAAC,YAAS,WAAU,eAClB;AAAA,4BAAC,eAAY,OAAM,cAAa,WAAU,UAAS,wBAAU;AAAA,QAC7D,oBAAC,eAAY,OAAM,YAAW,WAAU,UAAS,sBAAQ;AAAA,SAC3D;AAAA,MACA,oBAAC,eAAY,OAAM,cACjB,8BAAC,mBAAe,GAClB;AAAA,MACA,oBAAC,eAAY,OAAM,YAChB,mBAAS,YAAY,oBAAC,oBAAgB,IAAK,oBAAC,oBAAgB,GAC/D;AAAA,OACF,IACE,QAAQ,OAAO,oBACjB,SAAS,YAAY,oBAAC,oBAAgB,IAAK,oBAAC,oBAAgB,IAC1D,QAAQ,OAAO,mBACjB,oBAAC,mBAAe,IACd;AAAA,KACN,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components-page/auth-page.tsx"],"sourcesContent":["'use client';\n\nimport { CredentialSignIn } from '../components/credential-sign-in';\nimport { SeparatorWithText } from '../components/elements/separator-with-text';\nimport { OAuthButtonGroup } from '../components/oauth-button-group';\nimport { MaybeFullPage } from '../components/elements/maybe-full-page';\nimport { useUser, useStackApp } from '..';\nimport { PredefinedMessageCard } from '../components/message-cards/predefined-message-card';\nimport { MagicLinkSignIn } from '../components/magic-link-sign-in';\nimport { CredentialSignUp } from '../components/credential-sign-up';\nimport { StyledLink, Tabs, TabsContent, TabsList, TabsTrigger, Typography } from '@stackframe/stack-ui';\nimport { Project } from '../lib/stack-app';\nimport { runAsynchronously } from '@stackframe/stack-shared/dist/utils/promises';\nimport { useEffect } from 'react';\n\nexport function AuthPage({\n fullPage=false,\n type,\n automaticRedirect,\n mockProject,\n}: {\n fullPage?: boolean,\n type: 'sign-in' | 'sign-up',\n automaticRedirect?: boolean,\n mockProject?: {\n config: {\n signUpEnabled: boolean,\n credentialEnabled: boolean,\n magicLinkEnabled: boolean,\n oauthProviders: {\n id: string,\n }[],\n },\n },\n}) {\n const stackApp = useStackApp();\n const user = useUser();\n const projectFromHook = stackApp.useProject();\n const project = mockProject || projectFromHook;\n\n useEffect(() => {\n if (automaticRedirect) {\n if (user && !mockProject) {\n runAsynchronously(type === 'sign-in' ? stackApp.redirectToAfterSignIn() : stackApp.redirectToAfterSignUp());\n }\n }\n }, [user, mockProject, stackApp, automaticRedirect]);\n\n if (user && !mockProject) {\n return <PredefinedMessageCard type='signedIn' fullPage={fullPage} />;\n }\n\n if (type === 'sign-up' && !project.config.signUpEnabled) {\n return <PredefinedMessageCard type='signUpDisabled' fullPage={fullPage} />;\n }\n\n const enableSeparator = (project.config.credentialEnabled || project.config.magicLinkEnabled) && project.config.oauthProviders.length > 0;\n\n return (\n <MaybeFullPage fullPage={fullPage}>\n <div className='stack-scope flex flex-col items-stretch'>\n <div className=\"text-center mb-6\">\n <Typography type='h2'>\n {type === 'sign-in' ? 'Sign in to your account' : 'Create a new account'}\n </Typography>\n {type === 'sign-in' ? (\n project.config.signUpEnabled && (\n <Typography>\n {\"Don't have an account? \"}\n <StyledLink href={stackApp.urls.signUp} onClick={(e) => {\n runAsynchronously(stackApp.redirectToSignUp());\n e.preventDefault();\n }}>\n Sign up\n </StyledLink>\n </Typography>\n )\n ) : (\n <Typography>\n {\"Already have an account? \"}\n <StyledLink href={stackApp.urls.signIn} onClick={(e) => {\n runAsynchronously(stackApp.redirectToSignIn());\n e.preventDefault();\n }}>\n Sign in\n </StyledLink>\n </Typography>\n )}\n </div>\n <OAuthButtonGroup type={type} mockProject={mockProject} />\n {enableSeparator && <SeparatorWithText text={'Or continue with'} />}\n {project.config.credentialEnabled && project.config.magicLinkEnabled ? (\n <Tabs defaultValue='magic-link'>\n <TabsList className='w-full mb-2'>\n <TabsTrigger value='magic-link' className='flex-1'>Magic Link</TabsTrigger>\n <TabsTrigger value='password' className='flex-1'>Password</TabsTrigger>\n </TabsList>\n <TabsContent value='magic-link'>\n <MagicLinkSignIn/>\n </TabsContent>\n <TabsContent value='password'>\n {type === 'sign-up' ? <CredentialSignUp/> : <CredentialSignIn/>}\n </TabsContent>\n </Tabs>\n ) : project.config.credentialEnabled ? (\n type === 'sign-up' ? <CredentialSignUp/> : <CredentialSignIn/>\n ) : project.config.magicLinkEnabled ? (\n <MagicLinkSignIn/>\n ) : null}\n </div>\n </MaybeFullPage>\n );\n}\n"],"mappings":";;;AAEA,SAAS,wBAAwB;AACjC,SAAS,yBAAyB;AAClC,SAAS,wBAAwB;AACjC,SAAS,qBAAqB;AAC9B,SAAS,SAAS,mBAAmB;AACrC,SAAS,6BAA6B;AACtC,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,YAAY,MAAM,aAAa,UAAU,aAAa,kBAAkB;AAEjF,SAAS,yBAAyB;AAClC,SAAS,iBAAiB;AAoCf,cAkBG,YAlBH;AAlCJ,SAAS,SAAS;AAAA,EACvB,WAAS;AAAA,EACT;AAAA,EACA;AAAA,EACA;AACF,GAcG;AACD,QAAM,WAAW,YAAY;AAC7B,QAAM,OAAO,QAAQ;AACrB,QAAM,kBAAkB,SAAS,WAAW;AAC5C,QAAM,UAAU,eAAe;AAE/B,YAAU,MAAM;AACd,QAAI,mBAAmB;AACrB,UAAI,QAAQ,CAAC,aAAa;AACxB,0BAAkB,SAAS,YAAY,SAAS,sBAAsB,IAAI,SAAS,sBAAsB,CAAC;AAAA,MAC5G;AAAA,IACF;AAAA,EACF,GAAG,CAAC,MAAM,aAAa,UAAU,iBAAiB,CAAC;AAEnD,MAAI,QAAQ,CAAC,aAAa;AACxB,WAAO,oBAAC,yBAAsB,MAAK,YAAW,UAAoB;AAAA,EACpE;AAEA,MAAI,SAAS,aAAa,CAAC,QAAQ,OAAO,eAAe;AACvD,WAAO,oBAAC,yBAAsB,MAAK,kBAAiB,UAAoB;AAAA,EAC1E;AAEA,QAAM,mBAAmB,QAAQ,OAAO,qBAAqB,QAAQ,OAAO,qBAAqB,QAAQ,OAAO,eAAe,SAAS;AAExI,SACE,oBAAC,iBAAc,UACb,+BAAC,SAAI,WAAU,2CACb;AAAA,yBAAC,SAAI,WAAU,oBACb;AAAA,0BAAC,cAAW,MAAK,MACd,mBAAS,YAAY,4BAA4B,wBACpD;AAAA,MACC,SAAS,YACR,QAAQ,OAAO,iBACb,qBAAC,cACE;AAAA;AAAA,QACD,oBAAC,cAAW,MAAM,SAAS,KAAK,QAAQ,SAAS,CAAC,MAAM;AACtD,4BAAkB,SAAS,iBAAiB,CAAC;AAC7C,YAAE,eAAe;AAAA,QACnB,GAAG,qBAEH;AAAA,SACF,IAGF,qBAAC,cACE;AAAA;AAAA,QACD,oBAAC,cAAW,MAAM,SAAS,KAAK,QAAQ,SAAS,CAAC,MAAM;AACtD,4BAAkB,SAAS,iBAAiB,CAAC;AAC7C,YAAE,eAAe;AAAA,QACnB,GAAG,qBAEH;AAAA,SACF;AAAA,OAEJ;AAAA,IACA,oBAAC,oBAAiB,MAAY,aAA0B;AAAA,IACvD,mBAAmB,oBAAC,qBAAkB,MAAM,oBAAoB;AAAA,IAChE,QAAQ,OAAO,qBAAqB,QAAQ,OAAO,mBAClD,qBAAC,QAAK,cAAa,cACjB;AAAA,2BAAC,YAAS,WAAU,eAClB;AAAA,4BAAC,eAAY,OAAM,cAAa,WAAU,UAAS,wBAAU;AAAA,QAC7D,oBAAC,eAAY,OAAM,YAAW,WAAU,UAAS,sBAAQ;AAAA,SAC3D;AAAA,MACA,oBAAC,eAAY,OAAM,cACjB,8BAAC,mBAAe,GAClB;AAAA,MACA,oBAAC,eAAY,OAAM,YAChB,mBAAS,YAAY,oBAAC,oBAAgB,IAAK,oBAAC,oBAAgB,GAC/D;AAAA,OACF,IACE,QAAQ,OAAO,oBACjB,SAAS,YAAY,oBAAC,oBAAgB,IAAK,oBAAC,oBAAgB,IAC1D,QAAQ,OAAO,mBACjB,oBAAC,mBAAe,IACd;AAAA,KACN,GACF;AAEJ;","names":[]}
@@ -2,13 +2,59 @@
2
2
  "use client";
3
3
 
4
4
  // src/components-page/forgot-password.tsx
5
- import { ForgotPasswordForm } from "../components/forgot-password-form";
5
+ import { yupResolver } from "@hookform/resolvers/yup";
6
+ import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
7
+ import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
8
+ import { Button, Input, Label, StyledLink, Typography } from "@stackframe/stack-ui";
9
+ import { useState } from "react";
10
+ import { useForm } from "react-hook-form";
11
+ import { useStackApp, useUser } from "..";
12
+ import { FormWarningText } from "../components/elements/form-warning";
6
13
  import { MaybeFullPage } from "../components/elements/maybe-full-page";
7
- import { useUser, useStackApp } from "..";
8
14
  import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
9
- import { useState } from "react";
10
- import { StyledLink, Typography } from "@stackframe/stack-ui";
11
15
  import { jsx, jsxs } from "react/jsx-runtime";
16
+ var schema = yupObject({
17
+ email: yupString().email("Please enter a valid email").required("Please enter your email")
18
+ });
19
+ function ForgotPasswordForm({ onSent }) {
20
+ const { register, handleSubmit, formState: { errors }, clearErrors } = useForm({
21
+ resolver: yupResolver(schema)
22
+ });
23
+ const stackApp = useStackApp();
24
+ const [loading, setLoading] = useState(false);
25
+ const onSubmit = async (data) => {
26
+ setLoading(true);
27
+ try {
28
+ const { email } = data;
29
+ await stackApp.sendForgotPasswordEmail(email);
30
+ onSent?.();
31
+ } finally {
32
+ setLoading(false);
33
+ }
34
+ };
35
+ return /* @__PURE__ */ jsxs(
36
+ "form",
37
+ {
38
+ className: "flex flex-col items-stretch stack-scope",
39
+ onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
40
+ noValidate: true,
41
+ children: [
42
+ /* @__PURE__ */ jsx(Label, { htmlFor: "email", className: "mb-1", children: "Your Email" }),
43
+ /* @__PURE__ */ jsx(
44
+ Input,
45
+ {
46
+ id: "email",
47
+ type: "email",
48
+ ...register("email"),
49
+ onChange: () => clearErrors("email")
50
+ }
51
+ ),
52
+ /* @__PURE__ */ jsx(FormWarningText, { text: errors.email?.message?.toString() }),
53
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: "Send Email" })
54
+ ]
55
+ }
56
+ );
57
+ }
12
58
  function ForgotPassword({ fullPage = false }) {
13
59
  const stackApp = useStackApp();
14
60
  const user = useUser();
@@ -31,6 +77,7 @@ function ForgotPassword({ fullPage = false }) {
31
77
  ] });
32
78
  }
33
79
  export {
34
- ForgotPassword
80
+ ForgotPassword,
81
+ ForgotPasswordForm
35
82
  };
36
83
  //# sourceMappingURL=forgot-password.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components-page/forgot-password.tsx"],"sourcesContent":["'use client';\n\nimport { ForgotPasswordForm } from \"../components/forgot-password-form\";\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\nimport { useUser, useStackApp } from \"..\";\nimport { PredefinedMessageCard } from \"../components/message-cards/predefined-message-card\";\nimport { useState } from \"react\";\nimport { StyledLink, Typography } from \"@stackframe/stack-ui\";\n\n\nexport function ForgotPassword({ fullPage=false }: { fullPage?: boolean }) {\n const stackApp = useStackApp();\n const user = useUser();\n const [sent, setSent] = useState(false);\n\n if (user) {\n return <PredefinedMessageCard type='signedIn' fullPage={fullPage} />;\n }\n\n if (sent) {\n return <PredefinedMessageCard type='emailSent' fullPage={fullPage} />;\n }\n\n return (\n <MaybeFullPage fullPage={fullPage}>\n <div className=\"text-center mb-6 stack-scope\">\n <Typography type='h2'>Reset Your Password</Typography>\n <Typography>\n {\"Don't need to reset? \"}\n <StyledLink href={stackApp.urls['signUp']}>\n Sign in\n </StyledLink>\n </Typography>\n </div>\n <ForgotPasswordForm onSent={() => setSent(true)} />\n </MaybeFullPage>\n );\n};\n"],"mappings":";;;AAEA,SAAS,0BAA0B;AACnC,SAAS,qBAAqB;AAC9B,SAAS,SAAS,mBAAmB;AACrC,SAAS,6BAA6B;AACtC,SAAS,gBAAgB;AACzB,SAAS,YAAY,kBAAkB;AAS5B,cAWH,YAXG;AANJ,SAAS,eAAe,EAAE,WAAS,MAAM,GAA2B;AACzE,QAAM,WAAW,YAAY;AAC7B,QAAM,OAAO,QAAQ;AACrB,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AAEtC,MAAI,MAAM;AACR,WAAO,oBAAC,yBAAsB,MAAK,YAAW,UAAoB;AAAA,EACpE;AAEA,MAAI,MAAM;AACR,WAAO,oBAAC,yBAAsB,MAAK,aAAY,UAAoB;AAAA,EACrE;AAEA,SACE,qBAAC,iBAAc,UACb;AAAA,yBAAC,SAAI,WAAU,gCACb;AAAA,0BAAC,cAAW,MAAK,MAAK,iCAAmB;AAAA,MACzC,qBAAC,cACE;AAAA;AAAA,QACD,oBAAC,cAAW,MAAM,SAAS,KAAK,QAAQ,GAAG,qBAE3C;AAAA,SACF;AAAA,OACF;AAAA,IACA,oBAAC,sBAAmB,QAAQ,MAAM,QAAQ,IAAI,GAAG;AAAA,KACnD;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components-page/forgot-password.tsx"],"sourcesContent":["'use client';\n\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { yupObject, yupString } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Label, StyledLink, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { useStackApp, useUser } from \"..\";\nimport { FormWarningText } from \"../components/elements/form-warning\";\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\nimport { PredefinedMessageCard } from \"../components/message-cards/predefined-message-card\";\n\nconst schema = yupObject({\n email: yupString().email('Please enter a valid email').required('Please enter your email')\n});\n\nexport function ForgotPasswordForm({ onSent }: { onSent?: () => void }) {\n const { register, handleSubmit, formState: { errors }, clearErrors } = useForm({\n resolver: yupResolver(schema)\n });\n const stackApp = useStackApp();\n const [loading, setLoading] = useState(false);\n\n const onSubmit = async (data: yup.InferType<typeof schema>) => {\n setLoading(true);\n try {\n const { email } = data;\n await stackApp.sendForgotPasswordEmail(email);\n onSent?.();\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <form\n className=\"flex flex-col items-stretch stack-scope\"\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <Label htmlFor=\"email\" className=\"mb-1\">Your Email</Label>\n <Input\n id=\"email\"\n type=\"email\"\n {...register('email')}\n onChange={() => clearErrors('email')}\n />\n <FormWarningText text={errors.email?.message?.toString()} />\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>\n Send Email\n </Button>\n </form>\n );\n}\n\n\nexport function ForgotPassword({ fullPage=false }: { fullPage?: boolean }) {\n const stackApp = useStackApp();\n const user = useUser();\n const [sent, setSent] = useState(false);\n\n if (user) {\n return <PredefinedMessageCard type='signedIn' fullPage={fullPage} />;\n }\n\n if (sent) {\n return <PredefinedMessageCard type='emailSent' fullPage={fullPage} />;\n }\n\n return (\n <MaybeFullPage fullPage={fullPage}>\n <div className=\"text-center mb-6 stack-scope\">\n <Typography type='h2'>Reset Your Password</Typography>\n <Typography>\n {\"Don't need to reset? \"}\n <StyledLink href={stackApp.urls['signUp']}>\n Sign in\n </StyledLink>\n </Typography>\n </div>\n <ForgotPasswordForm onSent={() => setSent(true)} />\n </MaybeFullPage>\n );\n};\n"],"mappings":";;;AAEA,SAAS,mBAAmB;AAC5B,SAAS,WAAW,iBAAiB;AACrC,SAAS,kCAAkC;AAC3C,SAAS,QAAQ,OAAO,OAAO,YAAY,kBAAkB;AAC7D,SAAS,gBAAgB;AACzB,SAAS,eAAe;AAExB,SAAS,aAAa,eAAe;AACrC,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,6BAA6B;AAyBlC,SAKE,KALF;AAvBJ,IAAM,SAAS,UAAU;AAAA,EACvB,OAAO,UAAU,EAAE,MAAM,4BAA4B,EAAE,SAAS,yBAAyB;AAC3F,CAAC;AAEM,SAAS,mBAAmB,EAAE,OAAO,GAA4B;AACtE,QAAM,EAAE,UAAU,cAAc,WAAW,EAAE,OAAO,GAAG,YAAY,IAAI,QAAQ;AAAA,IAC7E,UAAU,YAAY,MAAM;AAAA,EAC9B,CAAC;AACD,QAAM,WAAW,YAAY;AAC7B,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,QAAM,WAAW,OAAO,SAAuC;AAC7D,eAAW,IAAI;AACf,QAAI;AACF,YAAM,EAAE,MAAM,IAAI;AAClB,YAAM,SAAS,wBAAwB,KAAK;AAC9C,eAAS;AAAA,IACT,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,UAAU,OAAK,2BAA2B,aAAa,QAAQ,EAAE,CAAC,CAAC;AAAA,MACnE,YAAU;AAAA,MAEV;AAAA,4BAAC,SAAM,SAAQ,SAAQ,WAAU,QAAO,wBAAU;AAAA,QAClD;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,MAAK;AAAA,YACJ,GAAG,SAAS,OAAO;AAAA,YACpB,UAAU,MAAM,YAAY,OAAO;AAAA;AAAA,QACrC;AAAA,QACA,oBAAC,mBAAgB,MAAM,OAAO,OAAO,SAAS,SAAS,GAAG;AAAA,QAE1D,oBAAC,UAAO,MAAK,UAAS,WAAU,QAAO,SAAkB,wBAEzD;AAAA;AAAA;AAAA,EACF;AAEJ;AAGO,SAAS,eAAe,EAAE,WAAS,MAAM,GAA2B;AACzE,QAAM,WAAW,YAAY;AAC7B,QAAM,OAAO,QAAQ;AACrB,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AAEtC,MAAI,MAAM;AACR,WAAO,oBAAC,yBAAsB,MAAK,YAAW,UAAoB;AAAA,EACpE;AAEA,MAAI,MAAM;AACR,WAAO,oBAAC,yBAAsB,MAAK,aAAY,UAAoB;AAAA,EACrE;AAEA,SACE,qBAAC,iBAAc,UACb;AAAA,yBAAC,SAAI,WAAU,gCACb;AAAA,0BAAC,cAAW,MAAK,MAAK,iCAAmB;AAAA,MACzC,qBAAC,cACE;AAAA;AAAA,QACD,oBAAC,cAAW,MAAM,SAAS,KAAK,QAAQ,GAAG,qBAE3C;AAAA,SACF;AAAA,OACF;AAAA,IACA,oBAAC,sBAAmB,QAAQ,MAAM,QAAQ,IAAI,GAAG;AAAA,KACnD;AAEJ;","names":[]}
@@ -2,14 +2,105 @@
2
2
  "use client";
3
3
 
4
4
  // src/components-page/password-reset.tsx
5
- import { MessageCard } from "../components/message-cards/message-card";
6
- import { useStackApp } from "..";
7
- import React from "react";
8
- import PasswordResetForm from "../components/password-reset-form";
9
- import { cacheFunction } from "@stackframe/stack-shared/dist/utils/caches";
5
+ import { yupResolver } from "@hookform/resolvers/yup";
10
6
  import { KnownErrors } from "@stackframe/stack-shared";
11
- import { Typography } from "@stackframe/stack-ui";
12
- import { jsx } from "react/jsx-runtime";
7
+ import { getPasswordError } from "@stackframe/stack-shared/dist/helpers/password";
8
+ import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
9
+ import { cacheFunction } from "@stackframe/stack-shared/dist/utils/caches";
10
+ import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
11
+ import { Button, Label, PasswordInput, Typography } from "@stackframe/stack-ui";
12
+ import React, { useState } from "react";
13
+ import { useForm } from "react-hook-form";
14
+ import * as yup from "yup";
15
+ import { useStackApp } from "..";
16
+ import { FormWarningText } from "../components/elements/form-warning";
17
+ import { MaybeFullPage } from "../components/elements/maybe-full-page";
18
+ import { MessageCard } from "../components/message-cards/message-card";
19
+ import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
20
+ import { jsx, jsxs } from "react/jsx-runtime";
21
+ var schema = yupObject({
22
+ password: yupString().required("Please enter your password").test({
23
+ name: "is-valid-password",
24
+ test: (value, ctx) => {
25
+ const error = getPasswordError(value);
26
+ if (error) {
27
+ return ctx.createError({ message: error.message });
28
+ } else {
29
+ return true;
30
+ }
31
+ }
32
+ }),
33
+ passwordRepeat: yupString().nullable().oneOf([yup.ref("password"), null], "Passwords do not match").required("Please repeat your password")
34
+ });
35
+ function PasswordResetForm({ code, fullPage = false }) {
36
+ const { register, handleSubmit, formState: { errors }, clearErrors } = useForm({
37
+ resolver: yupResolver(schema)
38
+ });
39
+ const stackApp = useStackApp();
40
+ const [finished, setFinished] = useState(false);
41
+ const [resetError, setResetError] = useState(false);
42
+ const [loading, setLoading] = useState(false);
43
+ const onSubmit = async (data) => {
44
+ setLoading(true);
45
+ try {
46
+ const { password } = data;
47
+ const errorCode = await stackApp.resetPassword({ password, code });
48
+ if (errorCode) {
49
+ setResetError(true);
50
+ return;
51
+ }
52
+ setFinished(true);
53
+ } finally {
54
+ setLoading(false);
55
+ }
56
+ };
57
+ if (finished) {
58
+ return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "passwordReset", fullPage });
59
+ }
60
+ if (resetError) {
61
+ return /* @__PURE__ */ jsx(MessageCard, { title: "Failed to reset password", fullPage, children: "Failed to reset password. Please request a new password reset link" });
62
+ }
63
+ return /* @__PURE__ */ jsxs(MaybeFullPage, { fullPage, children: [
64
+ /* @__PURE__ */ jsx("div", { className: "text-center mb-6", children: /* @__PURE__ */ jsx(Typography, { type: "h2", children: "Reset Your Password" }) }),
65
+ /* @__PURE__ */ jsxs(
66
+ "form",
67
+ {
68
+ style: { display: "flex", flexDirection: "column", alignItems: "stretch" },
69
+ onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
70
+ noValidate: true,
71
+ children: [
72
+ /* @__PURE__ */ jsx(Label, { htmlFor: "password", className: "mb-1", children: "New Password" }),
73
+ /* @__PURE__ */ jsx(
74
+ PasswordInput,
75
+ {
76
+ id: "password",
77
+ ...register("password"),
78
+ onChange: () => {
79
+ clearErrors("password");
80
+ clearErrors("passwordRepeat");
81
+ }
82
+ }
83
+ ),
84
+ /* @__PURE__ */ jsx(FormWarningText, { text: errors.password?.message?.toString() }),
85
+ /* @__PURE__ */ jsx(Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: "Repeat New Password" }),
86
+ /* @__PURE__ */ jsx(
87
+ PasswordInput,
88
+ {
89
+ id: "repeat-password",
90
+ ...register("passwordRepeat"),
91
+ onChange: () => {
92
+ clearErrors("password");
93
+ clearErrors("passwordRepeat");
94
+ }
95
+ }
96
+ ),
97
+ /* @__PURE__ */ jsx(FormWarningText, { text: errors.passwordRepeat?.message?.toString() }),
98
+ /* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: "Reset Password" })
99
+ ]
100
+ }
101
+ )
102
+ ] });
103
+ }
13
104
  var cachedVerifyPasswordResetCode = cacheFunction(async (stackApp, code) => {
14
105
  return await stackApp.verifyPasswordResetCode(code);
15
106
  });
@@ -21,7 +112,7 @@ function PasswordReset({
21
112
  const invalidJsx = /* @__PURE__ */ jsx(MessageCard, { title: "Invalid Password Reset Link", fullPage, children: /* @__PURE__ */ jsx(Typography, { children: "Please double check if you have the correct password reset link." }) });
22
113
  const expiredJsx = /* @__PURE__ */ jsx(MessageCard, { title: "Expired Password Reset Link", fullPage, children: /* @__PURE__ */ jsx(Typography, { children: "Your password reset link has expired. Please request a new password reset link from the login page." }) });
23
114
  const usedJsx = /* @__PURE__ */ jsx(MessageCard, { title: "Used Password Reset Link", fullPage, children: /* @__PURE__ */ jsx(Typography, { children: "This password reset link has already been used. If you need to reset your password again, please request a new password reset link from the login page." }) });
24
- const code = searchParams?.code;
115
+ const code = searchParams.code;
25
116
  if (!code) {
26
117
  return invalidJsx;
27
118
  }
@@ -38,6 +129,7 @@ function PasswordReset({
38
129
  return /* @__PURE__ */ jsx(PasswordResetForm, { code, fullPage });
39
130
  }
40
131
  export {
41
- PasswordReset
132
+ PasswordReset,
133
+ PasswordResetForm as default
42
134
  };
43
135
  //# sourceMappingURL=password-reset.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components-page/password-reset.tsx"],"sourcesContent":["'use client';\n\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { StackClientApp, useStackApp } from \"..\";\nimport React from \"react\";\nimport PasswordResetForm from \"../components/password-reset-form\";\nimport { cacheFunction } from \"@stackframe/stack-shared/dist/utils/caches\";\nimport { KnownErrors } from \"@stackframe/stack-shared\";\nimport { Typography } from \"@stackframe/stack-ui\";\n\nconst cachedVerifyPasswordResetCode = cacheFunction(async (stackApp: StackClientApp<true>, code: string) => {\n return await stackApp.verifyPasswordResetCode(code);\n});\n\nexport function PasswordReset({\n searchParams,\n fullPage = false,\n}: {\n searchParams?: Record<string, string>,\n fullPage?: boolean,\n}) {\n const stackApp = useStackApp();\n\n const invalidJsx = (\n <MessageCard title=\"Invalid Password Reset Link\" fullPage={fullPage}>\n <Typography>Please double check if you have the correct password reset link.</Typography>\n </MessageCard>\n );\n\n const expiredJsx = (\n <MessageCard title=\"Expired Password Reset Link\" fullPage={fullPage}>\n <Typography>Your password reset link has expired. Please request a new password reset link from the login page.</Typography>\n </MessageCard>\n );\n\n const usedJsx = (\n <MessageCard title=\"Used Password Reset Link\" fullPage={fullPage}>\n <Typography>This password reset link has already been used. If you need to reset your password again, please request a new password reset link from the login page.</Typography>\n </MessageCard>\n );\n\n const code = searchParams?.code;\n if (!code) {\n return invalidJsx;\n }\n\n const error = React.use(cachedVerifyPasswordResetCode(stackApp, code));\n\n if (error instanceof KnownErrors.VerificationCodeNotFound) {\n return invalidJsx;\n } else if (error instanceof KnownErrors.VerificationCodeExpired) {\n return expiredJsx;\n } else if (error instanceof KnownErrors.VerificationCodeAlreadyUsed) {\n return usedJsx;\n } else if (error) {\n throw error;\n }\n\n return <PasswordResetForm code={code} fullPage={fullPage} />;\n}\n"],"mappings":";;;AAEA,SAAS,mBAAmB;AAC5B,SAAyB,mBAAmB;AAC5C,OAAO,WAAW;AAClB,OAAO,uBAAuB;AAC9B,SAAS,qBAAqB;AAC9B,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB;AAiBrB;AAfN,IAAM,gCAAgC,cAAc,OAAO,UAAgC,SAAiB;AAC1G,SAAO,MAAM,SAAS,wBAAwB,IAAI;AACpD,CAAC;AAEM,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA,WAAW;AACb,GAGG;AACD,QAAM,WAAW,YAAY;AAE7B,QAAM,aACJ,oBAAC,eAAY,OAAM,+BAA8B,UAC/C,8BAAC,cAAW,8EAAgE,GAC9E;AAGF,QAAM,aACJ,oBAAC,eAAY,OAAM,+BAA8B,UAC/C,8BAAC,cAAW,iHAAmG,GACjH;AAGF,QAAM,UACJ,oBAAC,eAAY,OAAM,4BAA2B,UAC5C,8BAAC,cAAW,qKAAuJ,GACrK;AAGF,QAAM,OAAO,cAAc;AAC3B,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,MAAM,IAAI,8BAA8B,UAAU,IAAI,CAAC;AAErE,MAAI,iBAAiB,YAAY,0BAA0B;AACzD,WAAO;AAAA,EACT,WAAW,iBAAiB,YAAY,yBAAyB;AAC/D,WAAO;AAAA,EACT,WAAW,iBAAiB,YAAY,6BAA6B;AACnE,WAAO;AAAA,EACT,WAAW,OAAO;AAChB,UAAM;AAAA,EACR;AAEA,SAAO,oBAAC,qBAAkB,MAAY,UAAoB;AAC5D;","names":[]}
1
+ {"version":3,"sources":["../../../src/components-page/password-reset.tsx"],"sourcesContent":["'use client';\n\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { KnownErrors } from \"@stackframe/stack-shared\";\nimport { getPasswordError } from \"@stackframe/stack-shared/dist/helpers/password\";\nimport { yupObject, yupString } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { cacheFunction } from \"@stackframe/stack-shared/dist/utils/caches\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Label, PasswordInput, Typography } from \"@stackframe/stack-ui\";\nimport React, { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { StackClientApp, useStackApp } from \"..\";\nimport { FormWarningText } from \"../components/elements/form-warning\";\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { PredefinedMessageCard } from \"../components/message-cards/predefined-message-card\";\n\nconst schema = yupObject({\n password: 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 passwordRepeat: yupString().nullable().oneOf([yup.ref('password'), null], 'Passwords do not match').required('Please repeat your password')\n});\n\nexport default function PasswordResetForm(\n { code, fullPage = false }:\n { code: string, fullPage?: boolean }\n) {\n const { register, handleSubmit, formState: { errors }, clearErrors } = useForm({\n resolver: yupResolver(schema)\n });\n const stackApp = useStackApp();\n const [finished, setFinished] = useState(false);\n const [resetError, setResetError] = useState(false);\n const [loading, setLoading] = useState(false);\n\n const onSubmit = async (data: yup.InferType<typeof schema>) => {\n setLoading(true);\n try {\n const { password } = data;\n const errorCode = await stackApp.resetPassword({ password, code });\n if (errorCode) {\n setResetError(true);\n return;\n }\n\n setFinished(true);\n } finally {\n setLoading(false);\n }\n };\n\n if (finished) {\n return <PredefinedMessageCard type='passwordReset' fullPage={fullPage} />;\n }\n\n if (resetError) {\n return (\n <MessageCard title=\"Failed to reset password\" fullPage={fullPage}>\n Failed to reset password. Please request a new password reset link\n </MessageCard>\n );\n }\n\n return (\n <MaybeFullPage fullPage={fullPage}>\n <div className=\"text-center mb-6\">\n <Typography type='h2'>Reset Your Password</Typography>\n </div>\n\n <form\n style={{ display: 'flex', flexDirection: 'column', alignItems: 'stretch' }}\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <Label htmlFor=\"password\" className=\"mb-1\">New Password</Label>\n <PasswordInput\n id=\"password\"\n {...register('password')}\n onChange={() => {\n clearErrors('password');\n clearErrors('passwordRepeat');\n }}\n />\n <FormWarningText text={errors.password?.message?.toString()} />\n\n <Label htmlFor=\"repeat-password\" className=\"mt-4 mb-1\">Repeat New Password</Label>\n <PasswordInput\n id=\"repeat-password\"\n {...register('passwordRepeat')}\n onChange={() => {\n clearErrors('password');\n clearErrors('passwordRepeat');\n }}\n />\n <FormWarningText text={errors.passwordRepeat?.message?.toString()} />\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>\n Reset Password\n </Button>\n </form>\n </MaybeFullPage>\n );\n}\n\n\nconst cachedVerifyPasswordResetCode = cacheFunction(async (stackApp: StackClientApp<true>, code: string) => {\n return await stackApp.verifyPasswordResetCode(code);\n});\n\nexport function PasswordReset({\n searchParams,\n fullPage = false,\n}: {\n searchParams: Record<string, string>,\n fullPage?: boolean,\n}) {\n const stackApp = useStackApp();\n\n const invalidJsx = (\n <MessageCard title=\"Invalid Password Reset Link\" fullPage={fullPage}>\n <Typography>Please double check if you have the correct password reset link.</Typography>\n </MessageCard>\n );\n\n const expiredJsx = (\n <MessageCard title=\"Expired Password Reset Link\" fullPage={fullPage}>\n <Typography>Your password reset link has expired. Please request a new password reset link from the login page.</Typography>\n </MessageCard>\n );\n\n const usedJsx = (\n <MessageCard title=\"Used Password Reset Link\" fullPage={fullPage}>\n <Typography>This password reset link has already been used. If you need to reset your password again, please request a new password reset link from the login page.</Typography>\n </MessageCard>\n );\n\n const code = searchParams.code;\n if (!code) {\n return invalidJsx;\n }\n\n const error = React.use(cachedVerifyPasswordResetCode(stackApp, code));\n\n if (error instanceof KnownErrors.VerificationCodeNotFound) {\n return invalidJsx;\n } else if (error instanceof KnownErrors.VerificationCodeExpired) {\n return expiredJsx;\n } else if (error instanceof KnownErrors.VerificationCodeAlreadyUsed) {\n return usedJsx;\n } else if (error) {\n throw error;\n }\n\n return <PasswordResetForm code={code} fullPage={fullPage} />;\n}\n"],"mappings":"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","names":[]}