fix(auth): retain input on failure + copy fixes

This commit is contained in:
Gabriel Brown
2026-07-11 17:23:25 -04:00
parent a3ec04ad99
commit d873c8eaf7
3 changed files with 68 additions and 11 deletions
@@ -100,12 +100,14 @@ const ForgotPassword = () => {
await signIn('password', formData).then(() => { await signIn('password', formData).then(() => {
setEmail(values.email); setEmail(values.email);
setFlow('reset-verification'); setFlow('reset-verification');
// Only clear the form once the request succeeded; a failed request
// keeps the typed email so the user can retry without re-entering it.
forgotPasswordForm.reset();
}); });
} catch (error) { } catch (error) {
console.error('Error resetting password: ', error); console.error('Error resetting password: ', error);
toast.error('Error resetting password.'); toast.error('Error resetting password.');
} finally { } finally {
forgotPasswordForm.reset();
setLoading(false); setLoading(false);
} }
}; };
@@ -121,12 +123,14 @@ const ForgotPassword = () => {
setLoading(true); setLoading(true);
try { try {
await signIn('password', formData); await signIn('password', formData);
// Only clear on success (navigation follows); a failed submit keeps the
// entered password so the user can correct and retry.
resetVerificationForm.reset();
router.push('/'); router.push('/');
} catch (error) { } catch (error) {
console.error('Error resetting password: ', error); console.error('Error resetting password: ', error);
toast.error('Error resetting password.'); toast.error('Error resetting password.');
} finally { } finally {
resetVerificationForm.reset();
setLoading(false); setLoading(false);
} }
}; };
@@ -230,7 +234,7 @@ const ForgotPassword = () => {
</FormControl> </FormControl>
<FormDescription> <FormDescription>
Please enter the one-time password sent to your Please enter the one-time password sent to your
phone. email.
</FormDescription> </FormDescription>
<div className='flex w-full flex-col items-center'> <div className='flex w-full flex-col items-center'>
<FormMessage className='w-5/6 text-center' /> <FormMessage className='w-5/6 text-center' />
@@ -265,7 +269,7 @@ const ForgotPassword = () => {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className='text-xl'> <FormLabel className='text-xl'>
Confirm Passsword Confirm Password
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
+11 -7
View File
@@ -132,12 +132,14 @@ const SignIn = () => {
formData.append('flow', flow); formData.append('flow', flow);
setLoading(true); setLoading(true);
try { try {
await signIn('password', formData).then(() => router.push('/dashboard')); await signIn('password', formData).then(() => {
signInForm.reset();
router.push('/dashboard');
});
} catch (error) { } catch (error) {
console.error('Error signing in:', error); console.error('Error signing in:', error);
toast.error('Error signing in.'); toast.error('Error signing in.');
} finally { } finally {
signInForm.reset();
setLoading(false); setLoading(false);
} }
}; };
@@ -153,6 +155,7 @@ const SignIn = () => {
if (values.confirmPassword !== values.password) if (values.confirmPassword !== values.password)
throw new ConvexError('Passwords do not match.'); throw new ConvexError('Passwords do not match.');
await signIn('password', formData).then(() => { await signIn('password', formData).then(() => {
signUpForm.reset();
setEmail(values.email); setEmail(values.email);
setFlow('email-verification'); setFlow('email-verification');
}); });
@@ -160,7 +163,6 @@ const SignIn = () => {
console.error('Error signing up:', error); console.error('Error signing up:', error);
toast.error('Error signing up.'); toast.error('Error signing up.');
} finally { } finally {
signUpForm.reset();
setLoading(false); setLoading(false);
} }
}; };
@@ -174,12 +176,14 @@ const SignIn = () => {
formData.append('email', email); formData.append('email', email);
setLoading(true); setLoading(true);
try { try {
await signIn('password', formData).then(() => router.push('/dashboard')); await signIn('password', formData).then(() => {
verifyEmailForm.reset();
router.push('/dashboard');
});
} catch (error) { } catch (error) {
console.error('Error verifying email:', error); console.error('Error verifying email:', error);
toast.error('Error verifying email.'); toast.error('Error verifying email.');
} finally { } finally {
verifyEmailForm.reset();
setLoading(false); setLoading(false);
} }
}; };
@@ -232,7 +236,7 @@ const SignIn = () => {
/> />
<SubmitButton <SubmitButton
disabled={loading} disabled={loading}
pendingText='Signing Up...' pendingText='Verifying...'
className='mx-auto w-2/3 text-xl font-semibold' className='mx-auto w-2/3 text-xl font-semibold'
> >
Verify Email Verify Email
@@ -423,7 +427,7 @@ const SignIn = () => {
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel className='text-xl'> <FormLabel className='text-xl'>
Confirm Passsword Confirm Password
</FormLabel> </FormLabel>
<FormControl> <FormControl>
<Input <Input
@@ -0,0 +1,49 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import SignIn from '../../src/app/(auth)/sign-in/page';
const { mockSignIn, mockToastError } = vi.hoisted(() => ({
mockSignIn: vi.fn(),
mockToastError: vi.fn(),
}));
vi.mock('@convex-dev/auth/react', () => ({
useAuthActions: () => ({ signIn: mockSignIn }),
}));
vi.mock('next/navigation', () => ({
useRouter: () => ({ push: vi.fn(), replace: vi.fn() }),
}));
vi.mock('sonner', () => ({
toast: {
error: mockToastError,
success: vi.fn(),
},
}));
describe('sign-in input retention', () => {
it('keeps the typed email and password after a failed sign-in', async () => {
mockSignIn.mockRejectedValue(new Error('Invalid credentials'));
render(<SignIn />);
const emailInput = screen.getByPlaceholderText('you@example.com');
const passwordInput = screen.getByPlaceholderText('Your password');
fireEvent.change(emailInput, {
target: { value: 'person@example.com' },
});
fireEvent.change(passwordInput, { target: { value: 'Sup3rSecret!' } });
fireEvent.click(screen.getByRole('button', { name: 'Sign In' }));
await waitFor(() => expect(mockToastError).toHaveBeenCalled());
await waitFor(() => {
expect(emailInput).toHaveValue('person@example.com');
expect(passwordInput).toHaveValue('Sup3rSecret!');
});
});
});