@stackframe/stack 2.5.18 → 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 (58) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/components/credential-sign-in.js +1 -3
  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 -12
  9. package/dist/components/elements/user-avatar.d.ts +5 -12
  10. package/dist/components/elements/user-avatar.js.map +1 -1
  11. package/dist/components/selected-team-switcher.js +35 -5
  12. package/dist/components/selected-team-switcher.js.map +1 -1
  13. package/dist/components-page/account-settings.js +47 -49
  14. package/dist/components-page/account-settings.js.map +1 -1
  15. package/dist/components-page/stack-handler.js +20 -0
  16. package/dist/components-page/stack-handler.js.map +1 -1
  17. package/dist/components-page/team-creation.d.mts +7 -0
  18. package/dist/components-page/team-creation.d.ts +7 -0
  19. package/dist/components-page/team-creation.js +92 -0
  20. package/dist/components-page/team-creation.js.map +1 -0
  21. package/dist/components-page/team-settings.d.mts +8 -0
  22. package/dist/components-page/team-settings.d.ts +8 -0
  23. package/dist/components-page/team-settings.js +139 -0
  24. package/dist/components-page/team-settings.js.map +1 -0
  25. package/dist/esm/components/credential-sign-in.js +1 -3
  26. package/dist/esm/components/credential-sign-in.js.map +1 -1
  27. package/dist/esm/components/elements/sidebar-layout.js +90 -0
  28. package/dist/esm/components/elements/sidebar-layout.js.map +1 -0
  29. package/dist/esm/components/elements/user-avatar.js.map +1 -1
  30. package/dist/esm/components/selected-team-switcher.js +39 -7
  31. package/dist/esm/components/selected-team-switcher.js.map +1 -1
  32. package/dist/esm/components-page/account-settings.js +33 -35
  33. package/dist/esm/components-page/account-settings.js.map +1 -1
  34. package/dist/esm/components-page/stack-handler.js +20 -0
  35. package/dist/esm/components-page/stack-handler.js.map +1 -1
  36. package/dist/esm/components-page/team-creation.js +68 -0
  37. package/dist/esm/components-page/team-creation.js.map +1 -0
  38. package/dist/esm/components-page/team-settings.js +115 -0
  39. package/dist/esm/components-page/team-settings.js.map +1 -0
  40. package/dist/esm/generated/global-css.js +1 -1
  41. package/dist/esm/generated/global-css.js.map +1 -1
  42. package/dist/esm/lib/auth.js +4 -0
  43. package/dist/esm/lib/auth.js.map +1 -1
  44. package/dist/esm/lib/stack-app.js +178 -34
  45. package/dist/esm/lib/stack-app.js.map +1 -1
  46. package/dist/generated/global-css.d.mts +1 -1
  47. package/dist/generated/global-css.d.ts +1 -1
  48. package/dist/generated/global-css.js +1 -1
  49. package/dist/generated/global-css.js.map +1 -1
  50. package/dist/index.d.mts +1 -1
  51. package/dist/index.d.ts +1 -1
  52. package/dist/lib/auth.js +4 -0
  53. package/dist/lib/auth.js.map +1 -1
  54. package/dist/lib/stack-app.d.mts +35 -5
  55. package/dist/lib/stack-app.d.ts +35 -5
  56. package/dist/lib/stack-app.js +178 -34
  57. package/dist/lib/stack-app.js.map +1 -1
  58. package/package.json +7 -6
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components-page/account-settings.tsx"],"sourcesContent":["'use client';\n\nimport React, { useEffect } from 'react';\nimport { CurrentUser, Project, useStackApp, 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';\nimport { generateRandomValues } from '@stackframe/stack-shared/dist/utils/crypto';\nimport { TOTPController, createTOTPKeyURI } from \"oslo/otp\";\nimport * as QRCode from 'qrcode';\nimport { throwErr } from '@stackframe/stack-shared/dist/utils/errors';\nimport { useAsyncCallback } from '@stackframe/stack-shared/dist/hooks/use-async-callback';\nimport { runAsynchronously, runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/promises';\nimport { set } from 'react-hook-form';\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 if (project.config.oauthProviders.length !== 0 || project.config.magicLinkEnabled) {\n // TODO next-release support MFA for OAuth and magic link\n return null;\n }\n\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}\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 <MfaSection />\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":["import_react"]}
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":"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","names":[]}
@@ -36,6 +36,8 @@ var import_oauth_callback = require("./oauth-callback");
36
36
  var import_password_reset = require("./password-reset");
37
37
  var import_sign_out = require("./sign-out");
38
38
  var import_team_invitation = require("./team-invitation");
39
+ var import_team_settings = require("./team-settings");
40
+ var import_team_creation = require("./team-creation");
39
41
  var import_jsx_runtime = require("react/jsx-runtime");
40
42
  async function StackHandler({
41
43
  app,
@@ -70,9 +72,19 @@ async function StackHandler({
70
72
  accountSettings: "account-settings",
71
73
  magicLinkCallback: "magic-link-callback",
72
74
  teamInvitation: "team-invitation",
75
+ teamCreation: "team-creation",
73
76
  error: "error"
74
77
  };
75
78
  const path = stack.join("/");
79
+ if (/team-settings\/[a-zA-Z0-9-]+/.test(path)) {
80
+ const teamId = path.split("/")[1];
81
+ const user = await app.getUser();
82
+ const team = await user?.getTeam(teamId);
83
+ if (!team) {
84
+ return (0, import_navigation.notFound)();
85
+ }
86
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_team_settings.TeamSettings, { fullPage, teamId });
87
+ }
76
88
  switch (path) {
77
89
  case availablePaths.signIn: {
78
90
  redirectIfNotHandler("signIn");
@@ -114,6 +126,14 @@ async function StackHandler({
114
126
  redirectIfNotHandler("teamInvitation");
115
127
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_team_invitation.TeamInvitation, { searchParams, fullPage });
116
128
  }
129
+ case availablePaths.teamCreation: {
130
+ const project = await app.getProject();
131
+ if (!project.config.clientTeamCreationEnabled) {
132
+ return (0, import_navigation.notFound)();
133
+ }
134
+ redirectIfNotHandler("teamCreation");
135
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_team_creation.TeamCreation, { fullPage });
136
+ }
117
137
  case availablePaths.error: {
118
138
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_error_page.ErrorPage, { searchParams, fullPage });
119
139
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components-page/stack-handler.tsx"],"sourcesContent":["import { getRelativePart } from \"@stackframe/stack-shared/dist/utils/urls\";\nimport { RedirectType, notFound, redirect } from 'next/navigation';\nimport { AuthPage, StackServerApp } from \"..\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { HandlerUrls } from \"../lib/stack-app\";\nimport { AccountSettings } from \"./account-settings\";\nimport { EmailVerification } from \"./email-verification\";\nimport { ErrorPage } from \"./error-page\";\nimport { ForgotPassword } from \"./forgot-password\";\nimport { MagicLinkCallback } from \"./magic-link-callback\";\nimport { OAuthCallback } from \"./oauth-callback\";\nimport { PasswordReset } from \"./password-reset\";\nimport { SignOut } from \"./sign-out\";\nimport { TeamInvitation } from \"./team-invitation\";\n\nexport default async function StackHandler<HasTokenStore extends boolean>({\n app,\n params: { stack } = {},\n searchParams = {},\n fullPage = false,\n}: {\n app: StackServerApp<HasTokenStore>,\n params?: { stack?: string[] },\n searchParams?: Record<string, string>,\n fullPage?: boolean,\n}) {\n if (!stack) {\n return (\n <MessageCard title=\"Invalid Stack Handler Setup\" fullPage={fullPage}>\n <p>Can't use Stack handler at this location. Make sure that the file is in a folder called [...stack].</p>\n </MessageCard>\n );\n }\n\n function redirectIfNotHandler(name: keyof HandlerUrls) {\n const url = app.urls[name];\n const handlerUrl = app.urls.handler;\n\n if (url !== handlerUrl && url.startsWith(handlerUrl + \"/\")) {\n // don't redirect if the url is a handler url\n return;\n }\n\n const urlObj = new URL(url, \"http://example.com\");\n for (const [key, value] of Object.entries(searchParams)) {\n urlObj.searchParams.set(key, value);\n }\n\n redirect(getRelativePart(urlObj), RedirectType.replace);\n };\n\n const availablePaths = {\n signIn: 'sign-in',\n signUp: 'sign-up',\n emailVerification: 'email-verification',\n passwordReset: 'password-reset',\n forgotPassword: 'forgot-password',\n signOut: 'sign-out',\n oauthCallback: 'oauth-callback',\n accountSettings: 'account-settings',\n magicLinkCallback: 'magic-link-callback',\n teamInvitation: 'team-invitation',\n error: 'error',\n };\n\n const path = stack.join('/');\n switch (path) {\n case availablePaths.signIn: {\n redirectIfNotHandler('signIn');\n return <AuthPage fullPage={fullPage} type='sign-in' automaticRedirect />;\n }\n case availablePaths.signUp: {\n redirectIfNotHandler('signUp');\n return <AuthPage fullPage={fullPage} type='sign-up' automaticRedirect />;\n }\n case availablePaths.emailVerification: {\n redirectIfNotHandler('emailVerification');\n return <EmailVerification searchParams={searchParams} fullPage={fullPage} />;\n }\n case availablePaths.passwordReset: {\n redirectIfNotHandler('passwordReset');\n return <PasswordReset searchParams={searchParams} fullPage={fullPage} />;\n }\n case availablePaths.forgotPassword: {\n redirectIfNotHandler('forgotPassword');\n return <ForgotPassword fullPage={fullPage} />;\n }\n case availablePaths.signOut: {\n redirectIfNotHandler('signOut');\n return <SignOut fullPage={fullPage} />;\n }\n case availablePaths.oauthCallback: {\n redirectIfNotHandler('oauthCallback');\n return <OAuthCallback fullPage={fullPage} />;\n }\n case availablePaths.accountSettings: {\n redirectIfNotHandler('accountSettings');\n return <AccountSettings fullPage={fullPage} />;\n }\n case availablePaths.magicLinkCallback: {\n redirectIfNotHandler('magicLinkCallback');\n return <MagicLinkCallback searchParams={searchParams} fullPage={fullPage} />;\n }\n case availablePaths.teamInvitation: {\n redirectIfNotHandler('teamInvitation');\n return <TeamInvitation searchParams={searchParams} fullPage={fullPage} />;\n }\n case availablePaths.error: {\n return <ErrorPage searchParams={searchParams} fullPage={fullPage} />;\n }\n default: {\n for (const [key, value] of Object.entries(availablePaths)) {\n if (path === value.replaceAll('-', '')) {\n redirect(`${app.urls.handler}/${value}`, RedirectType.replace);\n }\n }\n return notFound();\n }\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components-page/stack-handler.tsx"],"sourcesContent":["import { getRelativePart } from \"@stackframe/stack-shared/dist/utils/urls\";\nimport { RedirectType, notFound, redirect } from 'next/navigation';\nimport { AuthPage, StackServerApp } from \"..\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { HandlerUrls } from \"../lib/stack-app\";\nimport { AccountSettings } from \"./account-settings\";\nimport { EmailVerification } from \"./email-verification\";\nimport { ErrorPage } from \"./error-page\";\nimport { ForgotPassword } from \"./forgot-password\";\nimport { MagicLinkCallback } from \"./magic-link-callback\";\nimport { OAuthCallback } from \"./oauth-callback\";\nimport { PasswordReset } from \"./password-reset\";\nimport { SignOut } from \"./sign-out\";\nimport { TeamInvitation } from \"./team-invitation\";\nimport { TeamSettings } from \"./team-settings\";\nimport { TeamCreation } from \"./team-creation\";\n\nexport default async function StackHandler<HasTokenStore extends boolean>({\n app,\n params: { stack } = {},\n searchParams = {},\n fullPage = false,\n}: {\n app: StackServerApp<HasTokenStore>,\n params?: { stack?: string[] },\n searchParams?: Record<string, string>,\n fullPage?: boolean,\n}) {\n if (!stack) {\n return (\n <MessageCard title=\"Invalid Stack Handler Setup\" fullPage={fullPage}>\n <p>Can't use Stack handler at this location. Make sure that the file is in a folder called [...stack].</p>\n </MessageCard>\n );\n }\n\n function redirectIfNotHandler(name: keyof HandlerUrls) {\n const url = app.urls[name];\n const handlerUrl = app.urls.handler;\n\n if (url !== handlerUrl && url.startsWith(handlerUrl + \"/\")) {\n // don't redirect if the url is a handler url\n return;\n }\n\n const urlObj = new URL(url, \"http://example.com\");\n for (const [key, value] of Object.entries(searchParams)) {\n urlObj.searchParams.set(key, value);\n }\n\n redirect(getRelativePart(urlObj), RedirectType.replace);\n };\n\n const availablePaths = {\n signIn: 'sign-in',\n signUp: 'sign-up',\n emailVerification: 'email-verification',\n passwordReset: 'password-reset',\n forgotPassword: 'forgot-password',\n signOut: 'sign-out',\n oauthCallback: 'oauth-callback',\n accountSettings: 'account-settings',\n magicLinkCallback: 'magic-link-callback',\n teamInvitation: 'team-invitation',\n teamCreation: 'team-creation',\n error: 'error',\n };\n\n const path = stack.join('/');\n\n if (/team-settings\\/[a-zA-Z0-9-]+/.test(path)) {\n const teamId = path.split('/')[1];\n const user = await app.getUser();\n const team = await user?.getTeam(teamId);\n\n if (!team) {\n return notFound();\n }\n\n return <TeamSettings fullPage={fullPage} teamId={teamId} />;\n }\n\n\n switch (path) {\n case availablePaths.signIn: {\n redirectIfNotHandler('signIn');\n return <AuthPage fullPage={fullPage} type='sign-in' automaticRedirect />;\n }\n case availablePaths.signUp: {\n redirectIfNotHandler('signUp');\n return <AuthPage fullPage={fullPage} type='sign-up' automaticRedirect />;\n }\n case availablePaths.emailVerification: {\n redirectIfNotHandler('emailVerification');\n return <EmailVerification searchParams={searchParams} fullPage={fullPage} />;\n }\n case availablePaths.passwordReset: {\n redirectIfNotHandler('passwordReset');\n return <PasswordReset searchParams={searchParams} fullPage={fullPage} />;\n }\n case availablePaths.forgotPassword: {\n redirectIfNotHandler('forgotPassword');\n return <ForgotPassword fullPage={fullPage} />;\n }\n case availablePaths.signOut: {\n redirectIfNotHandler('signOut');\n return <SignOut fullPage={fullPage} />;\n }\n case availablePaths.oauthCallback: {\n redirectIfNotHandler('oauthCallback');\n return <OAuthCallback fullPage={fullPage} />;\n }\n case availablePaths.accountSettings: {\n redirectIfNotHandler('accountSettings');\n return <AccountSettings fullPage={fullPage} />;\n }\n case availablePaths.magicLinkCallback: {\n redirectIfNotHandler('magicLinkCallback');\n return <MagicLinkCallback searchParams={searchParams} fullPage={fullPage} />;\n }\n case availablePaths.teamInvitation: {\n redirectIfNotHandler('teamInvitation');\n return <TeamInvitation searchParams={searchParams} fullPage={fullPage} />;\n }\n case availablePaths.teamCreation: {\n const project = await app.getProject();\n if (!project.config.clientTeamCreationEnabled) {\n return notFound();\n }\n\n redirectIfNotHandler('teamCreation');\n return <TeamCreation fullPage={fullPage} />;\n }\n case availablePaths.error: {\n return <ErrorPage searchParams={searchParams} fullPage={fullPage} />;\n }\n default: {\n for (const [key, value] of Object.entries(availablePaths)) {\n if (path === value.replaceAll('-', '')) {\n redirect(`${app.urls.handler}/${value}`, RedirectType.replace);\n }\n }\n return notFound();\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kBAAgC;AAChC,wBAAiD;AACjD,eAAyC;AACzC,0BAA4B;AAE5B,8BAAgC;AAChC,gCAAkC;AAClC,wBAA0B;AAC1B,6BAA+B;AAC/B,iCAAkC;AAClC,4BAA8B;AAC9B,4BAA8B;AAC9B,sBAAwB;AACxB,6BAA+B;AAC/B,2BAA6B;AAC7B,2BAA6B;AAgBrB;AAdR,eAAO,aAAmE;AAAA,EACxE;AAAA,EACA,QAAQ,EAAE,MAAM,IAAI,CAAC;AAAA,EACrB,eAAe,CAAC;AAAA,EAChB,WAAW;AACb,GAKG;AACD,MAAI,CAAC,OAAO;AACV,WACE,4CAAC,mCAAY,OAAM,+BAA8B,UAC/C,sDAAC,OAAE,iHAAmG,GACxG;AAAA,EAEJ;AAEA,WAAS,qBAAqB,MAAyB;AACrD,UAAM,MAAM,IAAI,KAAK,IAAI;AACzB,UAAM,aAAa,IAAI,KAAK;AAE5B,QAAI,QAAQ,cAAc,IAAI,WAAW,aAAa,GAAG,GAAG;AAE1D;AAAA,IACF;AAEA,UAAM,SAAS,IAAI,IAAI,KAAK,oBAAoB;AAChD,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,YAAY,GAAG;AACvD,aAAO,aAAa,IAAI,KAAK,KAAK;AAAA,IACpC;AAEA,wCAAS,6BAAgB,MAAM,GAAG,+BAAa,OAAO;AAAA,EACxD;AAAC;AAED,QAAM,iBAAiB;AAAA,IACrB,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,eAAe;AAAA,IACf,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,OAAO;AAAA,EACT;AAEA,QAAM,OAAO,MAAM,KAAK,GAAG;AAE3B,MAAI,+BAA+B,KAAK,IAAI,GAAG;AAC7C,UAAM,SAAS,KAAK,MAAM,GAAG,EAAE,CAAC;AAChC,UAAM,OAAO,MAAM,IAAI,QAAQ;AAC/B,UAAM,OAAO,MAAM,MAAM,QAAQ,MAAM;AAEvC,QAAI,CAAC,MAAM;AACT,iBAAO,4BAAS;AAAA,IAClB;AAEA,WAAO,4CAAC,qCAAa,UAAoB,QAAgB;AAAA,EAC3D;AAGA,UAAQ,MAAM;AAAA,IACZ,KAAK,eAAe,QAAQ;AAC1B,2BAAqB,QAAQ;AAC7B,aAAO,4CAAC,qBAAS,UAAoB,MAAK,WAAU,mBAAiB,MAAC;AAAA,IACxE;AAAA,IACA,KAAK,eAAe,QAAQ;AAC1B,2BAAqB,QAAQ;AAC7B,aAAO,4CAAC,qBAAS,UAAoB,MAAK,WAAU,mBAAiB,MAAC;AAAA,IACxE;AAAA,IACA,KAAK,eAAe,mBAAmB;AACrC,2BAAqB,mBAAmB;AACxC,aAAO,4CAAC,+CAAkB,cAA4B,UAAoB;AAAA,IAC5E;AAAA,IACA,KAAK,eAAe,eAAe;AACjC,2BAAqB,eAAe;AACpC,aAAO,4CAAC,uCAAc,cAA4B,UAAoB;AAAA,IACxE;AAAA,IACA,KAAK,eAAe,gBAAgB;AAClC,2BAAqB,gBAAgB;AACrC,aAAO,4CAAC,yCAAe,UAAoB;AAAA,IAC7C;AAAA,IACA,KAAK,eAAe,SAAS;AAC3B,2BAAqB,SAAS;AAC9B,aAAO,4CAAC,2BAAQ,UAAoB;AAAA,IACtC;AAAA,IACA,KAAK,eAAe,eAAe;AACjC,2BAAqB,eAAe;AACpC,aAAO,4CAAC,uCAAc,UAAoB;AAAA,IAC5C;AAAA,IACA,KAAK,eAAe,iBAAiB;AACnC,2BAAqB,iBAAiB;AACtC,aAAO,4CAAC,2CAAgB,UAAoB;AAAA,IAC9C;AAAA,IACA,KAAK,eAAe,mBAAmB;AACrC,2BAAqB,mBAAmB;AACxC,aAAO,4CAAC,gDAAkB,cAA4B,UAAoB;AAAA,IAC5E;AAAA,IACA,KAAK,eAAe,gBAAgB;AAClC,2BAAqB,gBAAgB;AACrC,aAAO,4CAAC,yCAAe,cAA4B,UAAoB;AAAA,IACzE;AAAA,IACA,KAAK,eAAe,cAAc;AAChC,YAAM,UAAU,MAAM,IAAI,WAAW;AACrC,UAAI,CAAC,QAAQ,OAAO,2BAA2B;AAC7C,mBAAO,4BAAS;AAAA,MAClB;AAEA,2BAAqB,cAAc;AACnC,aAAO,4CAAC,qCAAa,UAAoB;AAAA,IAC3C;AAAA,IACA,KAAK,eAAe,OAAO;AACzB,aAAO,4CAAC,+BAAU,cAA4B,UAAoB;AAAA,IACpE;AAAA,IACA,SAAS;AACP,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,cAAc,GAAG;AACzD,YAAI,SAAS,MAAM,WAAW,KAAK,EAAE,GAAG;AACtC,0CAAS,GAAG,IAAI,KAAK,OAAO,IAAI,KAAK,IAAI,+BAAa,OAAO;AAAA,QAC/D;AAAA,MACF;AACA,iBAAO,4BAAS;AAAA,IAClB;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,7 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ declare function TeamCreation(props: {
4
+ fullPage?: boolean;
5
+ }): react_jsx_runtime.JSX.Element;
6
+
7
+ export { TeamCreation };
@@ -0,0 +1,7 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ declare function TeamCreation(props: {
4
+ fullPage?: boolean;
5
+ }): react_jsx_runtime.JSX.Element;
6
+
7
+ export { TeamCreation };
@@ -0,0 +1,92 @@
1
+ "use client";
2
+ "use strict";
3
+ "use client";
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/components-page/team-creation.tsx
23
+ var team_creation_exports = {};
24
+ __export(team_creation_exports, {
25
+ TeamCreation: () => TeamCreation
26
+ });
27
+ module.exports = __toCommonJS(team_creation_exports);
28
+ var import_yup = require("@hookform/resolvers/yup");
29
+ var import_schema_fields = require("@stackframe/stack-shared/dist/schema-fields");
30
+ var import_promises = require("@stackframe/stack-shared/dist/utils/promises");
31
+ var import_stack_ui = require("@stackframe/stack-ui");
32
+ var import_react = require("react");
33
+ var import_react_hook_form = require("react-hook-form");
34
+ var import__ = require("..");
35
+ var import_form_warning = require("../components/elements/form-warning");
36
+ var import_maybe_full_page = require("../components/elements/maybe-full-page");
37
+ var import_navigation = require("next/navigation");
38
+ var import_jsx_runtime = require("react/jsx-runtime");
39
+ var schema = (0, import_schema_fields.yupObject)({
40
+ displayName: (0, import_schema_fields.yupString)().required("Please enter a team name")
41
+ });
42
+ function TeamCreation(props) {
43
+ const { register, handleSubmit, formState: { errors } } = (0, import_react_hook_form.useForm)({
44
+ resolver: (0, import_yup.yupResolver)(schema)
45
+ });
46
+ const app = (0, import__.useStackApp)();
47
+ const project = app.useProject();
48
+ const user = (0, import__.useUser)({ or: "redirect" });
49
+ const [loading, setLoading] = (0, import_react.useState)(false);
50
+ const router = (0, import_navigation.useRouter)();
51
+ if (!project.config.clientTeamCreationEnabled) {
52
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import__.MessageCard, { title: "Team creation is not enabled" });
53
+ }
54
+ const onSubmit = async (data) => {
55
+ setLoading(true);
56
+ try {
57
+ const team = await user.createTeam({ displayName: data.displayName });
58
+ router.push(`${app.urls.handler}/team-settings/${team.id}`);
59
+ } finally {
60
+ setLoading(false);
61
+ }
62
+ };
63
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_maybe_full_page.MaybeFullPage, { fullPage: props.fullPage, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "stack-scope flex flex-col items-stretch", children: [
64
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "text-center mb-6", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { type: "h2", children: "Create a Team" }) }),
65
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
66
+ "form",
67
+ {
68
+ className: "flex flex-col items-stretch stack-scope",
69
+ onSubmit: (e) => (0, import_promises.runAsynchronously)(handleSubmit(onSubmit)(e)),
70
+ noValidate: true,
71
+ children: [
72
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "email", className: "mb-1", children: "Display name" }),
73
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
74
+ import_stack_ui.Input,
75
+ {
76
+ id: "email",
77
+ type: "email",
78
+ ...register("displayName")
79
+ }
80
+ ),
81
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.displayName?.message?.toString() }),
82
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { type: "submit", className: "mt-6", loading, children: "Create" })
83
+ ]
84
+ }
85
+ )
86
+ ] }) });
87
+ }
88
+ // Annotate the CommonJS export names for ESM import in node:
89
+ 0 && (module.exports = {
90
+ TeamCreation
91
+ });
92
+ //# sourceMappingURL=team-creation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components-page/team-creation.tsx"],"sourcesContent":["'use client';\n\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { yupObject, yupString } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Label, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { MessageCard, useStackApp, useUser } from \"..\";\nimport { FormWarningText } from \"../components/elements/form-warning\";\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\nimport { useRouter } from \"next/navigation\";\n\nconst schema = yupObject({\n displayName: yupString().required('Please enter a team name'),\n});\n\nexport function TeamCreation(props: { fullPage?: boolean }) {\n const { register, handleSubmit, formState: { errors } } = useForm({\n resolver: yupResolver(schema)\n });\n const app = useStackApp();\n const project = app.useProject();\n const user = useUser({ or: 'redirect' });\n const [loading, setLoading] = useState(false);\n const router = useRouter();\n\n if (!project.config.clientTeamCreationEnabled) {\n return <MessageCard title='Team creation is not enabled' />;\n }\n\n const onSubmit = async (data: yup.InferType<typeof schema>) => {\n setLoading(true);\n\n try {\n const team = await user.createTeam({ displayName: data.displayName });\n router.push(`${app.urls.handler}/team-settings/${team.id}`);\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <MaybeFullPage fullPage={props.fullPage}>\n <div className='stack-scope flex flex-col items-stretch'>\n <div className=\"text-center mb-6\">\n <Typography type='h2'>\n Create a Team\n </Typography>\n </div>\n <form\n className=\"flex flex-col items-stretch stack-scope\"\n onSubmit={e => runAsynchronously(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <Label htmlFor=\"email\" className=\"mb-1\">Display name</Label>\n <Input\n id=\"email\"\n type=\"email\"\n {...register('displayName')}\n />\n <FormWarningText text={errors.displayName?.message?.toString()} />\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>\n Create\n </Button>\n </form>\n </div>\n </MaybeFullPage>\n );\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,iBAA4B;AAC5B,2BAAqC;AACrC,sBAAkC;AAClC,sBAAiD;AACjD,mBAAyB;AACzB,6BAAwB;AAExB,eAAkD;AAClD,0BAAgC;AAChC,6BAA8B;AAC9B,wBAA0B;AAiBf;AAfX,IAAM,aAAS,gCAAU;AAAA,EACvB,iBAAa,gCAAU,EAAE,SAAS,0BAA0B;AAC9D,CAAC;AAEM,SAAS,aAAa,OAA+B;AAC1D,QAAM,EAAE,UAAU,cAAc,WAAW,EAAE,OAAO,EAAE,QAAI,gCAAQ;AAAA,IAChE,cAAU,wBAAY,MAAM;AAAA,EAC9B,CAAC;AACD,QAAM,UAAM,sBAAY;AACxB,QAAM,UAAU,IAAI,WAAW;AAC/B,QAAM,WAAO,kBAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,QAAM,aAAS,6BAAU;AAEzB,MAAI,CAAC,QAAQ,OAAO,2BAA2B;AAC7C,WAAO,4CAAC,wBAAY,OAAM,gCAA+B;AAAA,EAC3D;AAEA,QAAM,WAAW,OAAO,SAAuC;AAC7D,eAAW,IAAI;AAEf,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,WAAW,EAAE,aAAa,KAAK,YAAY,CAAC;AACpE,aAAO,KAAK,GAAG,IAAI,KAAK,OAAO,kBAAkB,KAAK,EAAE,EAAE;AAAA,IAC5D,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,SACE,4CAAC,wCAAc,UAAU,MAAM,UAC7B,uDAAC,SAAI,WAAU,2CACb;AAAA,gDAAC,SAAI,WAAU,oBACb,sDAAC,8BAAW,MAAK,MAAK,2BAEtB,GACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,UAAU,WAAK,mCAAkB,aAAa,QAAQ,EAAE,CAAC,CAAC;AAAA,QAC1D,YAAU;AAAA,QAEV;AAAA,sDAAC,yBAAM,SAAQ,SAAQ,WAAU,QAAO,0BAAY;AAAA,UACpD;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,MAAK;AAAA,cACJ,GAAG,SAAS,aAAa;AAAA;AAAA,UAC5B;AAAA,UACA,4CAAC,uCAAgB,MAAM,OAAO,aAAa,SAAS,SAAS,GAAG;AAAA,UAEhE,4CAAC,0BAAO,MAAK,UAAS,WAAU,QAAO,SAAkB,oBAEzD;AAAA;AAAA;AAAA,IACF;AAAA,KACF,GACF;AAEJ;","names":[]}
@@ -0,0 +1,8 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ declare function TeamSettings(props: {
4
+ fullPage?: boolean;
5
+ teamId: string;
6
+ }): react_jsx_runtime.JSX.Element;
7
+
8
+ export { TeamSettings };
@@ -0,0 +1,8 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+
3
+ declare function TeamSettings(props: {
4
+ fullPage?: boolean;
5
+ teamId: string;
6
+ }): react_jsx_runtime.JSX.Element;
7
+
8
+ export { TeamSettings };
@@ -0,0 +1,139 @@
1
+ "use client";
2
+ "use strict";
3
+ "use client";
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/components-page/team-settings.tsx
23
+ var team_settings_exports = {};
24
+ __export(team_settings_exports, {
25
+ TeamSettings: () => TeamSettings
26
+ });
27
+ module.exports = __toCommonJS(team_settings_exports);
28
+ var import_stack_ui = require("@stackframe/stack-ui");
29
+ var import_lucide_react = require("lucide-react");
30
+ var import_react = require("react");
31
+ var import__ = require("..");
32
+ var import_sidebar_layout = require("../components/elements/sidebar-layout");
33
+ var import_user_avatar = require("../components/elements/user-avatar");
34
+ var import_jsx_runtime = require("react/jsx-runtime");
35
+ function TeamSettings(props) {
36
+ const user = (0, import__.useUser)({ or: "redirect" });
37
+ const team = user.useTeam(props.teamId);
38
+ if (!team) {
39
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import__.MessageCard, { title: "Team not found" });
40
+ }
41
+ const inner = /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
42
+ import_sidebar_layout.SidebarLayout,
43
+ {
44
+ items: [
45
+ { title: "My Profile", content: profileSettings({ team }), icon: import_lucide_react.Contact, description: `Your profile in the team "${team.displayName}"` },
46
+ { title: "Members", content: membersSettings({ team }), icon: import_lucide_react.Users },
47
+ { title: "Team Info", content: managementSettings({ team }), icon: import_lucide_react.Info }
48
+ // { title: 'Settings', content: userSettings({ team }), icon: Settings },
49
+ ].filter(({ content }) => content),
50
+ title: "Team Settings"
51
+ }
52
+ );
53
+ if (props.fullPage) {
54
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Container, { size: 800, className: "stack-scope", children: inner });
55
+ } else {
56
+ return inner;
57
+ }
58
+ }
59
+ function managementSettings(props) {
60
+ const user = (0, import__.useUser)({ or: "redirect" });
61
+ const updateTeamPermission = user.usePermission(props.team, "$update_team");
62
+ if (!updateTeamPermission) {
63
+ return null;
64
+ }
65
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
66
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { children: "Team display name" }),
67
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.EditableText, { value: props.team.displayName, onSave: () => {
68
+ } })
69
+ ] }) });
70
+ }
71
+ function profileSettings(props) {
72
+ const user = (0, import__.useUser)({ or: "redirect" });
73
+ const profile = user.useTeamProfile(props.team);
74
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col", children: [
75
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.Label, { className: "flex gap-2", children: [
76
+ "Display name ",
77
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.SimpleTooltip, { tooltip: "This overwrites your user display name in the account setting", type: "info" })
78
+ ] }),
79
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
80
+ import_stack_ui.EditableText,
81
+ {
82
+ value: profile.displayName || "",
83
+ onSave: async (newDisplayName) => {
84
+ await profile.update({ displayName: newDisplayName });
85
+ }
86
+ }
87
+ )
88
+ ] }) });
89
+ }
90
+ function membersSettings(props) {
91
+ const [removeModalOpen, setRemoveModalOpen] = (0, import_react.useState)(false);
92
+ const user = (0, import__.useUser)({ or: "redirect" });
93
+ const removeMemberPermission = user.usePermission(props.team, "$remove_members");
94
+ const readMemberPermission = user.usePermission(props.team, "$read_members");
95
+ const inviteMemberPermission = user.usePermission(props.team, "$invite_members");
96
+ const [email, setEmail] = (0, import_react.useState)("");
97
+ const [invited, setInvited] = (0, import_react.useState)(false);
98
+ if (!readMemberPermission && !inviteMemberPermission) {
99
+ return null;
100
+ }
101
+ const users = props.team.useUsers();
102
+ (0, import_react.useEffect)(() => {
103
+ if (invited && email) {
104
+ setInvited(false);
105
+ }
106
+ }, [email]);
107
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-8", children: [
108
+ inviteMemberPermission && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
109
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { children: "Invite a user to team" }),
110
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-2 md:flex-row", children: [
111
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Input, { placeholder: "Email", value: email, onChange: (e) => setEmail(e.target.value) }),
112
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { onClick: async () => {
113
+ await props.team.inviteUser({ email });
114
+ setEmail("");
115
+ setInvited(true);
116
+ }, children: "Invite" })
117
+ ] }),
118
+ invited && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { type: "label", variant: "secondary", children: "User invited." })
119
+ ] }),
120
+ readMemberPermission && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
121
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { children: "Members" }),
122
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.Table, { children: [
123
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
124
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[100px]", children: "User" }),
125
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[200px]", children: "Name" })
126
+ ] }) }),
127
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableBody, { children: users.map(({ id, teamProfile }, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
128
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_user_avatar.UserAvatar, { user: teamProfile }) }),
129
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: teamProfile.displayName }) })
130
+ ] }, id)) })
131
+ ] })
132
+ ] })
133
+ ] }) });
134
+ }
135
+ // Annotate the CommonJS export names for ESM import in node:
136
+ 0 && (module.exports = {
137
+ TeamSettings
138
+ });
139
+ //# sourceMappingURL=team-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components-page/team-settings.tsx"],"sourcesContent":["'use client';\n\nimport { ActionCell, ActionDialog, Button, Container, EditableText, Input, Label, SimpleTooltip, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from \"@stackframe/stack-ui\";\nimport { Contact, Info, Settings, Users } from \"lucide-react\";\nimport { useEffect, useState } from \"react\";\nimport { MessageCard, Team, useStackApp, useUser } from \"..\";\nimport { SidebarLayout } from \"../components/elements/sidebar-layout\";\nimport { UserAvatar } from \"../components/elements/user-avatar\";\n\nexport function TeamSettings(props: { fullPage?: boolean, teamId: string }) {\n const user = useUser({ or: 'redirect' });\n const team = user.useTeam(props.teamId);\n\n if (!team) {\n return <MessageCard title='Team not found' />;\n }\n\n\n const inner = <SidebarLayout\n items={[\n { title: 'My Profile', content: profileSettings({ team }), icon: Contact, description: `Your profile in the team \"${team.displayName}\"` },\n { title: 'Members', content: membersSettings({ team }), icon: Users },\n { title: 'Team Info', content: managementSettings({ team }), icon: Info },\n // { title: 'Settings', content: userSettings({ team }), icon: Settings },\n ].filter(({ content }) => content as any)}\n title='Team Settings'\n />;\n\n if (props.fullPage) {\n return (\n <Container size={800} className='stack-scope'>\n {inner}\n </Container>\n );\n } else {\n return inner;\n }\n}\n\nfunction managementSettings(props: { team: Team }) {\n const user = useUser({ or: 'redirect' });\n const updateTeamPermission = user.usePermission(props.team, '$update_team');\n\n if (!updateTeamPermission) {\n return null;\n }\n\n return (\n <>\n <div>\n <Label>Team display name</Label>\n <EditableText value={props.team.displayName} onSave={() => {}}/>\n </div>\n </>\n );\n}\n\nfunction profileSettings(props: { team: Team }) {\n const user = useUser({ or: 'redirect' });\n const profile = user.useTeamProfile(props.team);\n\n return (\n <div className=\"flex flex-col\">\n <div className=\"flex flex-col\">\n <Label className=\"flex gap-2\">Display name <SimpleTooltip tooltip=\"This overwrites your user display name in the account setting\" type='info'/></Label>\n <EditableText\n value={profile.displayName || ''}\n onSave={async (newDisplayName) => {\n await profile.update({ displayName: newDisplayName });\n }}/>\n </div>\n </div>\n );\n}\n\nfunction userSettings(props: { team: Team }) {\n const app = useStackApp();\n const user = useUser({ or: 'redirect' });\n\n return (\n <div>\n <div>\n <Button\n variant='secondary'\n onClick={async () => {\n await user.leaveTeam(props.team);\n await app.redirectToHome();\n }}\n >\n Leave team\n </Button>\n </div>\n </div>\n );\n}\n\nfunction RemoveMemberDialog(props: {\n open?: boolean,\n onOpenChange?: (open: boolean) => void,\n}) {\n return (\n <ActionDialog\n open={props.open}\n onOpenChange={props.onOpenChange}\n title=\"Delete domain\"\n danger\n okButton={{\n label: \"Delete\",\n onClick: async () => {\n }\n }}\n cancelButton\n >\n <Typography>\n Do you really want to remove from the team?\n </Typography>\n </ActionDialog>\n );\n}\n\nfunction membersSettings(props: { team: Team }) {\n const [removeModalOpen, setRemoveModalOpen] = useState(false);\n const user = useUser({ or: 'redirect' });\n const removeMemberPermission = user.usePermission(props.team, '$remove_members');\n const readMemberPermission = user.usePermission(props.team, '$read_members');\n const inviteMemberPermission = user.usePermission(props.team, '$invite_members');\n const [email, setEmail] = useState('');\n const [invited, setInvited] = useState(false);\n\n if (!readMemberPermission && !inviteMemberPermission) {\n return null;\n }\n\n const users = props.team.useUsers();\n\n useEffect(() => {\n if (invited && email) {\n setInvited(false);\n }\n }, [email]);\n\n return (\n <>\n <div className=\"flex flex-col gap-8\">\n {inviteMemberPermission &&\n <div>\n <Label>Invite a user to team</Label>\n <div className=\"flex flex-col gap-2 md:flex-row\">\n <Input placeholder=\"Email\" value={email} onChange={e => setEmail(e.target.value)}/>\n <Button onClick={async () => {\n await props.team.inviteUser({ email });\n setEmail('');\n setInvited(true);\n }}>Invite</Button>\n </div>\n {invited && <Typography type='label' variant='secondary'>User invited.</Typography>}\n </div>}\n {readMemberPermission &&\n <div>\n <Label>Members</Label>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[100px]\">User</TableHead>\n <TableHead className=\"w-[200px]\">Name</TableHead>\n {/* {removeMemberPermission && <TableHead className=\"w-[100px]\">Actions</TableHead>} */}\n </TableRow>\n </TableHeader>\n <TableBody>\n {users.map(({ id, teamProfile }, i) => (\n <TableRow key={id}>\n <TableCell>\n <UserAvatar user={teamProfile}/>\n </TableCell>\n <TableCell>\n <Typography>{teamProfile.displayName}</Typography>\n </TableCell>\n {/* {removeMemberPermission && <TableCell>\n <ActionCell items={[\n { item: 'Remove', onClick: () => setRemoveModalOpen(true), danger: true },\n ]}/>\n <RemoveMemberDialog open={removeModalOpen} onOpenChange={setRemoveModalOpen} />\n </TableCell>} */}\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>}\n </div>\n </>\n );\n}"],"mappings":"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","names":[]}
@@ -27,9 +27,7 @@ function CredentialSignIn() {
27
27
  const { email, password } = data;
28
28
  const error = await app.signInWithCredential({
29
29
  email,
30
- password,
31
- // TODO next-release remove
32
- ...{ __experimental_mfa: true }
30
+ password
33
31
  });
34
32
  setError("email", { type: "manual", message: error?.message });
35
33
  } finally {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/credential-sign-in.tsx"],"sourcesContent":["'use client';\n\nimport { useForm } from \"react-hook-form\";\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport * as yup from \"yup\";\nimport { FormWarningText } from \"./elements/form-warning\";\nimport { useStackApp } from \"..\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Label, PasswordInput, StyledLink } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { yupObject, yupString, yupNumber, yupBoolean, yupArray, yupMixed } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { KnownErrors } from \"@stackframe/stack-shared\";\n\nconst schema = yupObject({\n email: yupString().email('Please enter a valid email').required('Please enter your email'),\n password: yupString().required('Please enter your password')\n});\n\nexport function CredentialSignIn() {\n const { register, handleSubmit, setError, formState: { errors } } = useForm({\n resolver: yupResolver(schema)\n });\n const app = useStackApp();\n const [loading, setLoading] = useState(false);\n\n const onSubmit = async (data: yup.InferType<typeof schema>) => {\n setLoading(true);\n\n try {\n const { email, password } = data;\n const error = await app.signInWithCredential({\n email,\n password,\n\n // TODO next-release remove\n ...{ __experimental_mfa: true },\n });\n setError('email', { type: 'manual', message: error?.message });\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\">Email</Label>\n <Input\n id=\"email\"\n type=\"email\"\n {...register('email')}\n />\n <FormWarningText text={errors.email?.message?.toString()} />\n\n <Label htmlFor=\"password\" className=\"mt-4 mb-1\">Password</Label>\n <PasswordInput\n id=\"password\"\n {...register('password')}\n />\n <FormWarningText text={errors.password?.message?.toString()} />\n\n <StyledLink href={app.urls.forgotPassword} className=\"mt-1 text-sm\">\n Forgot password?\n </StyledLink>\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>\n Sign In\n </Button>\n </form>\n );\n}\n"],"mappings":";;;AAEA,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAE5B,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,kCAAkC;AAC3C,SAAS,QAAQ,OAAO,OAAO,eAAe,kBAAkB;AAChE,SAAS,gBAAgB;AACzB,SAAS,WAAW,iBAA4D;AAkC5E,SAKE,KALF;AA/BJ,IAAM,SAAS,UAAU;AAAA,EACvB,OAAO,UAAU,EAAE,MAAM,4BAA4B,EAAE,SAAS,yBAAyB;AAAA,EACzF,UAAU,UAAU,EAAE,SAAS,4BAA4B;AAC7D,CAAC;AAEM,SAAS,mBAAmB;AACjC,QAAM,EAAE,UAAU,cAAc,UAAU,WAAW,EAAE,OAAO,EAAE,IAAI,QAAQ;AAAA,IAC1E,UAAU,YAAY,MAAM;AAAA,EAC9B,CAAC;AACD,QAAM,MAAM,YAAY;AACxB,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,QAAM,WAAW,OAAO,SAAuC;AAC7D,eAAW,IAAI;AAEf,QAAI;AACF,YAAM,EAAE,OAAO,SAAS,IAAI;AAC5B,YAAM,QAAQ,MAAM,IAAI,qBAAqB;AAAA,QAC3C;AAAA,QACA;AAAA;AAAA,QAGA,GAAG,EAAE,oBAAoB,KAAK;AAAA,MAChC,CAAC;AACD,eAAS,SAAS,EAAE,MAAM,UAAU,SAAS,OAAO,QAAQ,CAAC;AAAA,IAC/D,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,mBAAK;AAAA,QAC7C;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,MAAK;AAAA,YACJ,GAAG,SAAS,OAAO;AAAA;AAAA,QACtB;AAAA,QACA,oBAAC,mBAAgB,MAAM,OAAO,OAAO,SAAS,SAAS,GAAG;AAAA,QAE1D,oBAAC,SAAM,SAAQ,YAAW,WAAU,aAAY,sBAAQ;AAAA,QACxD;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACF,GAAG,SAAS,UAAU;AAAA;AAAA,QACzB;AAAA,QACA,oBAAC,mBAAgB,MAAM,OAAO,UAAU,SAAS,SAAS,GAAG;AAAA,QAE7D,oBAAC,cAAW,MAAM,IAAI,KAAK,gBAAgB,WAAU,gBAAe,8BAEpE;AAAA,QAEA,oBAAC,UAAO,MAAK,UAAS,WAAU,QAAO,SAAkB,qBAEzD;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/credential-sign-in.tsx"],"sourcesContent":["'use client';\n\nimport { useForm } from \"react-hook-form\";\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport * as yup from \"yup\";\nimport { FormWarningText } from \"./elements/form-warning\";\nimport { useStackApp } from \"..\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Label, PasswordInput, StyledLink } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { yupObject, yupString, yupNumber, yupBoolean, yupArray, yupMixed } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { KnownErrors } from \"@stackframe/stack-shared\";\n\nconst schema = yupObject({\n email: yupString().email('Please enter a valid email').required('Please enter your email'),\n password: yupString().required('Please enter your password')\n});\n\nexport function CredentialSignIn() {\n const { register, handleSubmit, setError, formState: { errors } } = useForm({\n resolver: yupResolver(schema)\n });\n const app = useStackApp();\n const [loading, setLoading] = useState(false);\n\n const onSubmit = async (data: yup.InferType<typeof schema>) => {\n setLoading(true);\n\n try {\n const { email, password } = data;\n const error = await app.signInWithCredential({\n email,\n password,\n });\n setError('email', { type: 'manual', message: error?.message });\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\">Email</Label>\n <Input\n id=\"email\"\n type=\"email\"\n {...register('email')}\n />\n <FormWarningText text={errors.email?.message?.toString()} />\n\n <Label htmlFor=\"password\" className=\"mt-4 mb-1\">Password</Label>\n <PasswordInput\n id=\"password\"\n {...register('password')}\n />\n <FormWarningText text={errors.password?.message?.toString()} />\n\n <StyledLink href={app.urls.forgotPassword} className=\"mt-1 text-sm\">\n Forgot password?\n </StyledLink>\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>\n Sign In\n </Button>\n </form>\n );\n}\n"],"mappings":";;;AAEA,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAE5B,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,kCAAkC;AAC3C,SAAS,QAAQ,OAAO,OAAO,eAAe,kBAAkB;AAChE,SAAS,gBAAgB;AACzB,SAAS,WAAW,iBAA4D;AA+B5E,SAKE,KALF;AA5BJ,IAAM,SAAS,UAAU;AAAA,EACvB,OAAO,UAAU,EAAE,MAAM,4BAA4B,EAAE,SAAS,yBAAyB;AAAA,EACzF,UAAU,UAAU,EAAE,SAAS,4BAA4B;AAC7D,CAAC;AAEM,SAAS,mBAAmB;AACjC,QAAM,EAAE,UAAU,cAAc,UAAU,WAAW,EAAE,OAAO,EAAE,IAAI,QAAQ;AAAA,IAC1E,UAAU,YAAY,MAAM;AAAA,EAC9B,CAAC;AACD,QAAM,MAAM,YAAY;AACxB,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,QAAM,WAAW,OAAO,SAAuC;AAC7D,eAAW,IAAI;AAEf,QAAI;AACF,YAAM,EAAE,OAAO,SAAS,IAAI;AAC5B,YAAM,QAAQ,MAAM,IAAI,qBAAqB;AAAA,QAC3C;AAAA,QACA;AAAA,MACF,CAAC;AACD,eAAS,SAAS,EAAE,MAAM,UAAU,SAAS,OAAO,QAAQ,CAAC;AAAA,IAC/D,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,mBAAK;AAAA,QAC7C;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,MAAK;AAAA,YACJ,GAAG,SAAS,OAAO;AAAA;AAAA,QACtB;AAAA,QACA,oBAAC,mBAAgB,MAAM,OAAO,OAAO,SAAS,SAAS,GAAG;AAAA,QAE1D,oBAAC,SAAM,SAAQ,YAAW,WAAU,aAAY,sBAAQ;AAAA,QACxD;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACF,GAAG,SAAS,UAAU;AAAA;AAAA,QACzB;AAAA,QACA,oBAAC,mBAAgB,MAAM,OAAO,UAAU,SAAS,SAAS,GAAG;AAAA,QAE7D,oBAAC,cAAW,MAAM,IAAI,KAAK,gBAAgB,WAAU,gBAAe,8BAEpE;AAAA,QAEA,oBAAC,UAAO,MAAK,UAAS,WAAU,QAAO,SAAkB,qBAEzD;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -0,0 +1,90 @@
1
+ "use client";
2
+ "use client";
3
+
4
+ // src/components/elements/sidebar-layout.tsx
5
+ import { Button, Typography, cn } from "@stackframe/stack-ui";
6
+ import { XIcon } from "lucide-react";
7
+ import React from "react";
8
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
+ function SidebarNavItem(props) {
10
+ return /* @__PURE__ */ jsxs(
11
+ Button,
12
+ {
13
+ variant: "ghost",
14
+ size: "sm",
15
+ className: cn(
16
+ props.selected && "bg-muted",
17
+ "justify-start text-md text-zinc-800 dark:text-zinc-300 px-2"
18
+ ),
19
+ onClick: props.onClick,
20
+ children: [
21
+ props.item.icon && /* @__PURE__ */ jsx(props.item.icon, { className: "mr-2 h-4 w-4" }),
22
+ props.item.title
23
+ ]
24
+ }
25
+ );
26
+ }
27
+ function SidebarLayout(props) {
28
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
29
+ /* @__PURE__ */ jsx("div", { className: "hidden sm:flex", children: /* @__PURE__ */ jsx(DesktopLayout, { items: props.items, title: props.title }) }),
30
+ /* @__PURE__ */ jsx("div", { className: "sm:hidden", children: /* @__PURE__ */ jsx(MobileLayout, { items: props.items, title: props.title }) })
31
+ ] });
32
+ }
33
+ function DesktopLayout(props) {
34
+ const [selectedIndex, setSelectedIndex] = React.useState(0);
35
+ const currentItem = props.items[selectedIndex];
36
+ return /* @__PURE__ */ jsxs("div", { className: "stack-scope flex p-2 w-full", children: [
37
+ /* @__PURE__ */ jsxs("div", { className: "flex w-[200px] border-r flex-col items-stretch gap-2 p-2", children: [
38
+ props.title && /* @__PURE__ */ jsx("div", { className: "mb-2 ml-2", children: /* @__PURE__ */ jsx(Typography, { type: "h2", className: "text-lg font-semibold text-zinc-800 dark:text-zinc-300", children: props.title }) }),
39
+ props.items.map((item, index) => /* @__PURE__ */ jsx(
40
+ SidebarNavItem,
41
+ {
42
+ item,
43
+ onClick: () => setSelectedIndex(index),
44
+ selected: index === selectedIndex
45
+ },
46
+ index
47
+ ))
48
+ ] }),
49
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-4 py-2 px-4", children: [
50
+ /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
51
+ /* @__PURE__ */ jsx(Typography, { type: "h4", children: currentItem.title }),
52
+ currentItem.description && /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: currentItem.description })
53
+ ] }),
54
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: currentItem.content })
55
+ ] })
56
+ ] });
57
+ }
58
+ function MobileLayout(props) {
59
+ const [selectedIndex, setSelectedIndex] = React.useState(null);
60
+ if (selectedIndex === null) {
61
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 p-2", children: [
62
+ props.title && /* @__PURE__ */ jsx("div", { className: "mb-2 ml-2", children: /* @__PURE__ */ jsx(Typography, { type: "h2", className: "text-lg font-semibold text-zinc-800 dark:text-zinc-300", children: props.title }) }),
63
+ props.items.map((item, index) => /* @__PURE__ */ jsx(
64
+ SidebarNavItem,
65
+ {
66
+ item,
67
+ onClick: () => setSelectedIndex(index),
68
+ selected: false
69
+ },
70
+ index
71
+ ))
72
+ ] });
73
+ } else {
74
+ return /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-4 py-2 px-4", children: [
75
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
76
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
77
+ /* @__PURE__ */ jsx(Typography, { type: "h4", children: props.items[selectedIndex].title }),
78
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: () => setSelectedIndex(null), children: /* @__PURE__ */ jsx(XIcon, { className: "h-5 w-5" }) })
79
+ ] }),
80
+ props.items[selectedIndex].description && /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: props.items[selectedIndex].description })
81
+ ] }),
82
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: props.items[selectedIndex].content })
83
+ ] });
84
+ }
85
+ }
86
+ export {
87
+ SidebarLayout,
88
+ SidebarNavItem
89
+ };
90
+ //# sourceMappingURL=sidebar-layout.js.map