diff --git a/apps/sim/app/(auth)/login/login-form.tsx b/apps/sim/app/(auth)/login/login-form.tsx index 19f6fe5ff3..5d5f445532 100644 --- a/apps/sim/app/(auth)/login/login-form.tsx +++ b/apps/sim/app/(auth)/login/login-form.tsx @@ -10,7 +10,6 @@ import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { client } from '@/lib/auth-client' import { createLogger } from '@/lib/logs/console-logger' -import { useNotificationStore } from '@/stores/notifications/store' import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons' const logger = createLogger('LoginForm') @@ -49,9 +48,10 @@ export default function LoginPage({ const searchParams = useSearchParams() const [isLoading, setIsLoading] = useState(false) const [mounted, setMounted] = useState(false) - const { addNotification } = useNotificationStore() const [showPassword, setShowPassword] = useState(false) const [password, setPassword] = useState('') + const [passwordErrors, setPasswordErrors] = useState([]) + const [showValidationError, setShowValidationError] = useState(false) // Initialize state for URL parameters const [callbackUrl, setCallbackUrl] = useState('/w') @@ -121,45 +121,47 @@ export default function LoginPage({ { onError: (ctx) => { console.error('Login error:', ctx.error) - let errorMessage = 'Invalid email or password' + let errorMessage: string[] = ['Invalid email or password'] - // Handle all possible error cases from Better Auth - if (ctx.error.message?.includes('EMAIL_NOT_VERIFIED')) { + if (ctx.error.code?.includes('EMAIL_NOT_VERIFIED')) { return } else if ( - ctx.error.message?.includes('BAD_REQUEST') || + ctx.error.code?.includes('BAD_REQUEST') || ctx.error.message?.includes('Email and password sign in is not enabled') ) { - errorMessage = 'Email sign in is currently disabled.' + errorMessage.push('Email sign in is currently disabled.') } else if ( - ctx.error.message?.includes('INVALID_CREDENTIALS') || + ctx.error.code?.includes('INVALID_CREDENTIALS') || ctx.error.message?.includes('invalid password') ) { - errorMessage = 'Invalid email or password. Please try again.' + errorMessage.push('Invalid email or password. Please try again.') } else if ( - ctx.error.message?.includes('USER_NOT_FOUND') || + ctx.error.code?.includes('USER_NOT_FOUND') || ctx.error.message?.includes('not found') ) { - errorMessage = 'No account found with this email. Please sign up first.' - } else if (ctx.error.message?.includes('MISSING_CREDENTIALS')) { - errorMessage = 'Please enter both email and password.' - } else if (ctx.error.message?.includes('EMAIL_PASSWORD_DISABLED')) { - errorMessage = 'Email and password login is disabled.' - } else if (ctx.error.message?.includes('FAILED_TO_CREATE_SESSION')) { - errorMessage = 'Failed to create session. Please try again later.' - } else if (ctx.error.message?.includes('too many attempts')) { - errorMessage = + errorMessage.push('No account found with this email. Please sign up first.') + } else if (ctx.error.code?.includes('MISSING_CREDENTIALS')) { + errorMessage.push('Please enter both email and password.') + } else if (ctx.error.code?.includes('EMAIL_PASSWORD_DISABLED')) { + errorMessage.push('Email and password login is disabled.') + } else if (ctx.error.code?.includes('FAILED_TO_CREATE_SESSION')) { + errorMessage.push('Failed to create session. Please try again later.') + } else if (ctx.error.code?.includes('too many attempts')) { + errorMessage.push( 'Too many login attempts. Please try again later or reset your password.' - } else if (ctx.error.message?.includes('account locked')) { - errorMessage = + ) + } else if (ctx.error.code?.includes('account locked')) { + errorMessage.push( 'Your account has been locked for security. Please reset your password.' - } else if (ctx.error.message?.includes('network')) { - errorMessage = 'Network error. Please check your connection and try again.' + ) + } else if (ctx.error.code?.includes('network')) { + errorMessage.push('Network error. Please check your connection and try again.') } else if (ctx.error.message?.includes('rate limit')) { - errorMessage = 'Too many requests. Please wait a moment before trying again.' + errorMessage.push('Too many requests. Please wait a moment before trying again.') } - addNotification('error', errorMessage, null) + setPasswordErrors(errorMessage) + setShowValidationError(true) }, } ) @@ -176,7 +178,7 @@ export default function LoginPage({ } } catch (err: any) { // Handle only the special verification case that requires a redirect - if (err.message?.includes('not verified') || err.message?.includes('EMAIL_NOT_VERIFIED')) { + if (err.message?.includes('not verified') || err.code?.includes('EMAIL_NOT_VERIFIED')) { try { await client.emailOtp.sendVerificationOtp({ email, @@ -190,11 +192,8 @@ export default function LoginPage({ router.push(`/verify`) return } catch (verifyErr) { - addNotification( - 'error', - 'Failed to send verification code. Please try again later.', - null - ) + setPasswordErrors(['Failed to send verification code. Please try again later.']) + setShowValidationError(true) setIsLoading(false) return } @@ -308,7 +307,13 @@ export default function LoginPage({ autoCorrect="off" placeholder="Enter your password" value={password} - onChange={(e) => setPassword(e.target.value)} + onChange={(e) => { + setPassword(e.target.value) + if (showValidationError) { + setShowValidationError(false) + setPasswordErrors([]) + } + }} className="bg-neutral-900 border-neutral-700 text-white pr-10" /> + {showValidationError && passwordErrors.length > 0 && ( +
+ {passwordErrors.map((error, index) => ( +

{error}

+ ))} +
+ )} diff --git a/apps/sim/app/(auth)/signup/signup-form.tsx b/apps/sim/app/(auth)/signup/signup-form.tsx index 57d958f2b7..ef1f091589 100644 --- a/apps/sim/app/(auth)/signup/signup-form.tsx +++ b/apps/sim/app/(auth)/signup/signup-form.tsx @@ -3,12 +3,12 @@ import { Suspense, useEffect, useState } from 'react' import Link from 'next/link' import { useRouter, useSearchParams } from 'next/navigation' -import { Command, CornerDownLeft, Eye, EyeOff } from 'lucide-react' +import { Eye, EyeOff } from 'lucide-react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { client } from '@/lib/auth-client' -import { useNotificationStore } from '@/stores/notifications/store' +import { cn } from '@/lib/utils' import { SocialLoginButtons } from '@/app/(auth)/components/social-login-buttons' const PASSWORD_VALIDATIONS = { @@ -41,12 +41,12 @@ function SignupFormContent({ const searchParams = useSearchParams() const [isLoading, setIsLoading] = useState(false) const [, setMounted] = useState(false) - const { addNotification } = useNotificationStore() const [showPassword, setShowPassword] = useState(false) const [password, setPassword] = useState('') const [passwordErrors, setPasswordErrors] = useState([]) const [showValidationError, setShowValidationError] = useState(false) const [email, setEmail] = useState('') + const [emailError, setEmailError] = useState('') const [waitlistToken, setWaitlistToken] = useState('') const [redirectUrl, setRedirectUrl] = useState('') const [isInviteFlow, setIsInviteFlow] = useState(false) @@ -144,6 +144,14 @@ function SignupFormContent({ setShowValidationError(false) } + const handleEmailChange = (e: React.ChangeEvent) => { + setEmail(e.target.value) + // Clear any previous email errors when the user starts typing + if (emailError) { + setEmailError('') + } + } + async function onSubmit(e: React.FormEvent) { e.preventDefault() setIsLoading(true) @@ -163,7 +171,8 @@ function SignupFormContent({ try { if (errors.length > 0) { // Show first error as notification - addNotification('error', errors[0], null) + setPasswordErrors([errors[0]]) + setShowValidationError(true) setIsLoading(false) return } @@ -177,33 +186,42 @@ function SignupFormContent({ { onError: (ctx) => { console.error('Signup error:', ctx.error) - let errorMessage = 'Failed to create account' + let errorMessage: string[] = ['Failed to create account'] - // Handle all possible error cases from Better Auth - if (ctx.error.status === 422 || ctx.error.message?.includes('already exists')) { - errorMessage = 'An account with this email already exists. Please sign in instead.' + if (ctx.error.code?.includes('USER_ALREADY_EXISTS')) { + errorMessage.push( + 'An account with this email already exists. Please sign in instead.' + ) + setEmailError(errorMessage[0]) } else if ( - ctx.error.message?.includes('BAD_REQUEST') || + ctx.error.code?.includes('BAD_REQUEST') || ctx.error.message?.includes('Email and password sign up is not enabled') ) { - errorMessage = 'Email signup is currently disabled.' - } else if (ctx.error.message?.includes('INVALID_EMAIL')) { - errorMessage = 'Please enter a valid email address.' - } else if (ctx.error.message?.includes('PASSWORD_TOO_SHORT')) { - errorMessage = 'Password must be at least 8 characters long.' - } else if (ctx.error.message?.includes('MISSING_CREDENTIALS')) { - errorMessage = 'Please enter all required fields.' - } else if (ctx.error.message?.includes('EMAIL_PASSWORD_DISABLED')) { - errorMessage = 'Email and password signup is disabled.' - } else if (ctx.error.message?.includes('FAILED_TO_CREATE_USER')) { - errorMessage = 'Failed to create account. Please try again later.' - } else if (ctx.error.message?.includes('network')) { - errorMessage = 'Network error. Please check your connection and try again.' - } else if (ctx.error.message?.includes('rate limit')) { - errorMessage = 'Too many requests. Please wait a moment before trying again.' + errorMessage.push('Email signup is currently disabled.') + setEmailError(errorMessage[0]) + } else if (ctx.error.code?.includes('INVALID_EMAIL')) { + errorMessage.push('Please enter a valid email address.') + setEmailError(errorMessage[0]) + } else if (ctx.error.code?.includes('PASSWORD_TOO_SHORT')) { + errorMessage.push('Password must be at least 8 characters long.') + setPasswordErrors(errorMessage) + setShowValidationError(true) + } else if (ctx.error.code?.includes('PASSWORD_TOO_LONG')) { + errorMessage.push('Password must be less than 128 characters long.') + setPasswordErrors(errorMessage) + setShowValidationError(true) + } else if (ctx.error.code?.includes('network')) { + errorMessage.push('Network error. Please check your connection and try again.') + setPasswordErrors(errorMessage) + setShowValidationError(true) + } else if (ctx.error.code?.includes('rate limit')) { + errorMessage.push('Too many requests. Please wait a moment before trying again.') + setPasswordErrors(errorMessage) + setShowValidationError(true) + } else { + setPasswordErrors(errorMessage) + setShowValidationError(true) } - - addNotification('error', errorMessage, null) }, } ) @@ -255,7 +273,8 @@ function SignupFormContent({ router.push('/verify') } catch (error) { console.error('Failed to send verification code:', error) - addNotification('error', 'Account created but failed to send verification code.', null) + setPasswordErrors(['Account created but failed to send verification code.']) + setShowValidationError(true) router.push('/login') } } catch (error) { @@ -304,9 +323,17 @@ function SignupFormContent({ autoComplete="email" autoCorrect="off" value={email} - onChange={(e) => setEmail(e.target.value)} - className="bg-neutral-900 border-neutral-700 text-white" + onChange={handleEmailChange} + className={cn( + 'bg-neutral-900 border-neutral-700 text-white', + emailError && 'border-red-500 focus-visible:ring-red-500' + )} /> + {emailError && ( +
+

{emailError}

+
+ )}