Skip to content

Commit 672d249

Browse files
TheodoreSpeaksemir-karabeg
authored andcommitted
fix(signup): show multiple signup errors at once (#3987)
* fix(signup): show multiple signup errors at once * Fix reset password error formatting * Remove dead code * Fix unit tests --------- Co-authored-by: Theodore Li <theo@sim.ai>
1 parent 008af87 commit 672d249

4 files changed

Lines changed: 38 additions & 39 deletions

File tree

apps/sim/app/(auth)/reset-password/reset-password-form.tsx

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -97,49 +97,49 @@ export function SetNewPasswordForm({
9797
}: SetNewPasswordFormProps) {
9898
const [password, setPassword] = useState('')
9999
const [confirmPassword, setConfirmPassword] = useState('')
100-
const [validationMessage, setValidationMessage] = useState('')
100+
const [validationMessages, setValidationMessages] = useState<string[]>([])
101101
const [showPassword, setShowPassword] = useState(false)
102102
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
103103

104104
const handleSubmit = async (e: React.FormEvent) => {
105105
e.preventDefault()
106106

107+
const errors: string[] = []
108+
107109
if (password.length < 8) {
108-
setValidationMessage('Password must be at least 8 characters long')
109-
return
110+
errors.push('Password must be at least 8 characters long')
110111
}
111112

112113
if (password.length > 100) {
113-
setValidationMessage('Password must not exceed 100 characters')
114-
return
114+
errors.push('Password must not exceed 100 characters')
115115
}
116116

117117
if (!/[A-Z]/.test(password)) {
118-
setValidationMessage('Password must contain at least one uppercase letter')
119-
return
118+
errors.push('Password must contain at least one uppercase letter')
120119
}
121120

122121
if (!/[a-z]/.test(password)) {
123-
setValidationMessage('Password must contain at least one lowercase letter')
124-
return
122+
errors.push('Password must contain at least one lowercase letter')
125123
}
126124

127125
if (!/[0-9]/.test(password)) {
128-
setValidationMessage('Password must contain at least one number')
129-
return
126+
errors.push('Password must contain at least one number')
130127
}
131128

132129
if (!/[^A-Za-z0-9]/.test(password)) {
133-
setValidationMessage('Password must contain at least one special character')
134-
return
130+
errors.push('Password must contain at least one special character')
135131
}
136132

137133
if (password !== confirmPassword) {
138-
setValidationMessage('Passwords do not match')
134+
errors.push('Passwords do not match')
135+
}
136+
137+
if (errors.length > 0) {
138+
setValidationMessages(errors)
139139
return
140140
}
141141

142-
setValidationMessage('')
142+
setValidationMessages([])
143143
onSubmit(password)
144144
}
145145

@@ -162,7 +162,10 @@ export function SetNewPasswordForm({
162162
onChange={(e) => setPassword(e.target.value)}
163163
required
164164
placeholder='Enter new password'
165-
className={cn('pr-10', validationMessage && 'border-red-500 focus:border-red-500')}
165+
className={cn(
166+
'pr-10',
167+
validationMessages.length > 0 && 'border-red-500 focus:border-red-500'
168+
)}
166169
/>
167170
<button
168171
type='button'
@@ -190,7 +193,10 @@ export function SetNewPasswordForm({
190193
onChange={(e) => setConfirmPassword(e.target.value)}
191194
required
192195
placeholder='Confirm new password'
193-
className={cn('pr-10', validationMessage && 'border-red-500 focus:border-red-500')}
196+
className={cn(
197+
'pr-10',
198+
validationMessages.length > 0 && 'border-red-500 focus:border-red-500'
199+
)}
194200
/>
195201
<button
196202
type='button'
@@ -203,9 +209,11 @@ export function SetNewPasswordForm({
203209
</div>
204210
</div>
205211

206-
{validationMessage && (
212+
{validationMessages.length > 0 && (
207213
<div className='mt-1 space-y-1 text-red-400 text-xs'>
208-
<p>{validationMessage}</p>
214+
{validationMessages.map((error, index) => (
215+
<p key={index}>{error}</p>
216+
))}
209217
</div>
210218
)}
211219

apps/sim/app/(auth)/signup/signup-form.tsx

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -228,18 +228,6 @@ function SignupFormContent({
228228
emailValidationErrors.length > 0 ||
229229
errors.length > 0
230230
) {
231-
if (nameValidationErrors.length > 0) {
232-
setNameErrors([nameValidationErrors[0]])
233-
setShowNameValidationError(true)
234-
}
235-
if (emailValidationErrors.length > 0) {
236-
setEmailErrors([emailValidationErrors[0]])
237-
setShowEmailValidationError(true)
238-
}
239-
if (errors.length > 0) {
240-
setPasswordErrors([errors[0]])
241-
setShowValidationError(true)
242-
}
243231
setIsLoading(false)
244232
return
245233
}
@@ -400,7 +388,7 @@ function SignupFormContent({
400388
/>
401389
<div
402390
className={cn(
403-
'absolute right-0 left-0 z-10 grid transition-[grid-template-rows] duration-200 ease-out',
391+
'grid transition-[grid-template-rows] duration-200 ease-out',
404392
showNameValidationError && nameErrors.length > 0
405393
? 'grid-rows-[1fr]'
406394
: 'grid-rows-[0fr]'
@@ -438,7 +426,7 @@ function SignupFormContent({
438426
/>
439427
<div
440428
className={cn(
441-
'absolute right-0 left-0 z-10 grid transition-[grid-template-rows] duration-200 ease-out',
429+
'grid transition-[grid-template-rows] duration-200 ease-out',
442430
(showEmailValidationError && emailErrors.length > 0) ||
443431
(emailError && !showEmailValidationError)
444432
? 'grid-rows-[1fr]'
@@ -497,7 +485,7 @@ function SignupFormContent({
497485
</div>
498486
<div
499487
className={cn(
500-
'absolute right-0 left-0 z-10 grid transition-[grid-template-rows] duration-200 ease-out',
488+
'grid transition-[grid-template-rows] duration-200 ease-out',
501489
showValidationError && passwordErrors.length > 0
502490
? 'grid-rows-[1fr]'
503491
: 'grid-rows-[0fr]'

apps/sim/app/api/auth/reset-password/route.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ describe('Reset Password API Route', () => {
6868

6969
it('should handle missing token', async () => {
7070
const req = createMockRequest('POST', {
71-
newPassword: 'newSecurePassword123',
71+
newPassword: 'newSecurePassword123!',
7272
})
7373

7474
const response = await POST(req)
@@ -97,7 +97,7 @@ describe('Reset Password API Route', () => {
9797
it('should handle empty token', async () => {
9898
const req = createMockRequest('POST', {
9999
token: '',
100-
newPassword: 'newSecurePassword123',
100+
newPassword: 'newSecurePassword123!',
101101
})
102102

103103
const response = await POST(req)
@@ -119,7 +119,11 @@ describe('Reset Password API Route', () => {
119119
const data = await response.json()
120120

121121
expect(response.status).toBe(400)
122-
expect(data.message).toBe('Password must be at least 8 characters long')
122+
expect(data.message).toContain('Password must be at least 8 characters long')
123+
expect(data.message).toContain('Password must contain at least one uppercase letter')
124+
expect(data.message).toContain('Password must contain at least one lowercase letter')
125+
expect(data.message).toContain('Password must contain at least one number')
126+
expect(data.message).toContain('Password must contain at least one special character')
123127

124128
expect(mockResetPassword).not.toHaveBeenCalled()
125129
})

apps/sim/app/api/auth/reset-password/route.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ export async function POST(request: NextRequest) {
2626
const validationResult = resetPasswordSchema.safeParse(body)
2727

2828
if (!validationResult.success) {
29-
const firstError = validationResult.error.errors[0]
30-
const errorMessage = firstError?.message || 'Invalid request data'
29+
const errorMessage = validationResult.error.errors.map((e) => e.message).join(' ')
3130

3231
logger.warn('Invalid password reset request data', {
3332
errors: validationResult.error.format(),

0 commit comments

Comments
 (0)