= ({ startOtpVerification })
type="email"
placeholder="name@example.com"
value={email}
- onChange={(e) => setEmail(e.target.value)}
+ onChange={(e) => {
+ setEmail(e.target.value);
+ if (error) clearError();
+ }}
className="mb-2 dark:border-white"
/>
setPassword(e.target.value)}
+ onChange={(e) => {
+ setPassword(e.target.value);
+ if (error) clearError();
+ }}
className="mb-2 dark:border-white"
/>
setConfirmPassword(e.target.value)}
+ onChange={(e) => {
+ setConfirmPassword(e.target.value);
+ if (error) clearError();
+ }}
className="mb-4 dark:border-white"
/>
@@ -240,12 +277,21 @@ export const OTPVerificationForm: React.FC = ({ email,
throw new Error('OTPVerificationForm must be used within an AuthProvider');
}
- const { verifyEmail, error, loading } = authContext;
+ const { verifyEmail, error, loading, clearError } = authContext;
+
+ useEffect(() => {
+ clearError();
+ }, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
- await verifyEmail(email, otp);
- handleOtpVerified();
+ clearError();
+ try {
+ await verifyEmail(email, otp);
+ handleOtpVerified();
+ } catch {
+ // Handled by authContext error state
+ }
};
return (
@@ -256,7 +302,10 @@ export const OTPVerificationForm: React.FC = ({ email,
setOtp(e.target.value)}
+ onChange={(e) => {
+ setOtp(e.target.value);
+ if (error) clearError();
+ }}
placeholder="Enter OTP"
className="w-full mb-4 dark:border-white"
/>
@@ -293,7 +342,8 @@ export const ForgotPasswordForm: React.FC = ({
});
if (!response.ok) {
- setError('Failed to send reset password code. Please try again.');
+ const data = await response.json().catch(() => ({}));
+ setError(data.error || data.message || 'Failed to send reset password code. Please try again.');
return;
}
@@ -311,7 +361,10 @@ export const ForgotPasswordForm: React.FC = ({
setEmail(e.target.value)}
+ onChange={(e) => {
+ setEmail(e.target.value);
+ if (error) setError('');
+ }}
placeholder="name@example.com"
className="w-full mb-4 dark:border-white"
/>
@@ -340,7 +393,11 @@ export const ResetPasswordForm: React.FC = ({ email, han
throw new Error('ResetPasswordForm must be used within an AuthProvider');
}
- const { confirmForgotPassword, login, error, loading } = authContext;
+ const { confirmForgotPassword, login, error, loading, clearError } = authContext;
+
+ useEffect(() => {
+ clearError();
+ }, []);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
@@ -350,9 +407,14 @@ export const ResetPasswordForm: React.FC = ({ email, han
return;
}
- await confirmForgotPassword(email, code, newPassword);
- await login(email, newPassword);
- handlePasswordReset();
+ try {
+ await confirmForgotPassword(email, code, newPassword);
+ clearError();
+ await login(email, newPassword);
+ handlePasswordReset();
+ } catch {
+ // Handled by authContext error state
+ }
};
return (
@@ -362,21 +424,30 @@ export const ResetPasswordForm: React.FC = ({ email, han
setCode(e.target.value)}
+ onChange={(e) => {
+ setCode(e.target.value);
+ if (error) clearError();
+ }}
placeholder="Enter Code"
className="w-full mb-2 border dark:border-white"
/>
setNewPassword(e.target.value)}
+ onChange={(e) => {
+ setNewPassword(e.target.value);
+ if (error) clearError();
+ }}
placeholder="New Password"
className="w-full mb-2 dark:border-white"
/>
setConfirmNewPassword(e.target.value)}
+ onChange={(e) => {
+ setConfirmNewPassword(e.target.value);
+ if (error) clearError();
+ }}
placeholder="Confirm New Password"
className="w-full mb-4 dark:border-white"
/>
diff --git a/frontend/src/context/authContext.tsx b/frontend/src/context/authContext.tsx
index 37ad105b..cf38c778 100644
--- a/frontend/src/context/authContext.tsx
+++ b/frontend/src/context/authContext.tsx
@@ -19,7 +19,8 @@ interface AuthContextType {
isAuthenticated: boolean;
loading: boolean;
error: string | null;
- handleError: (error: string) => void;
+ handleError: (error: unknown) => void;
+ clearError: () => void;
login: (email: string, password: string) => Promise;
logout: () => void;
signup: (email: string, password: string) => Promise;
@@ -46,12 +47,19 @@ export const AuthProvider = ({ children }: { children: ReactNode }) => {
const navigate = useNavigate();
const setUser = useSetAtom(userAtom);
- const handleError = (error: unknown) => {
+ const clearError = useCallback(() => {
+ setError(null);
+ }, []);
+
+ const handleError = useCallback((error: unknown) => {
const message =
- error instanceof Error ? error.message : 'An unexpected error occurred';
+ typeof error === 'string'
+ ? error
+ : error instanceof Error
+ ? error.message
+ : 'An unexpected error occurred';
setError(message);
- throw error;
- };
+ }, []);
let currentRequest = 0;
const verifyToken = useCallback(async () => {
@@ -136,6 +144,7 @@ const verifyToken = useCallback(async () => {
const login = async (email: string, password: string) => {
setLoading(true);
+ setError(null);
try {
const response = await fetch(`${baseURL}/login`, {
method: 'POST',
@@ -143,8 +152,8 @@ const verifyToken = useCallback(async () => {
body: JSON.stringify({ email, password }),
});
- const data = await response.json();
- if (!response.ok) throw new Error(data.message || 'Login failed');
+ const data = await response.json().catch(() => ({}));
+ if (!response.ok) throw new Error(data.error || data.message || 'Login failed');
setToken(data.accessToken);
localStorage.setItem('token', data.accessToken);
@@ -177,6 +186,7 @@ const verifyToken = useCallback(async () => {
navigate('/');
} catch (error) {
handleError(error);
+ throw error;
} finally {
setLoading(false);
}
@@ -184,6 +194,7 @@ const verifyToken = useCallback(async () => {
const signup = async (email: string, password: string) => {
setLoading(true);
+ setError(null);
try {
const response = await fetch(`${baseURL}/signup`, {
method: 'POST',
@@ -192,11 +203,12 @@ const verifyToken = useCallback(async () => {
});
if (!response.ok) {
- const data = await response.json();
- throw new Error(data.message || 'Signup failed');
+ const data = await response.json().catch(() => ({}));
+ throw new Error(data.error || data.message || 'Signup failed');
}
} catch (error) {
handleError(error);
+ throw error;
} finally {
setLoading(false);
}
@@ -204,6 +216,7 @@ const verifyToken = useCallback(async () => {
const verifyEmail = async (email: string, code: string) => {
setLoading(true);
+ setError(null);
try {
const response = await fetch(`${baseURL}/verifyEmail`, {
method: 'POST',
@@ -211,13 +224,11 @@ const verifyToken = useCallback(async () => {
body: JSON.stringify({ email, confirmationCode: code }),
});
+ const data = await response.json().catch(() => ({}));
if (!response.ok) {
- const data = await response.json();
- throw new Error(data.error || 'Verification failed');
+ throw new Error(data.error || data.message || 'Verification failed');
}
- const data = await response.json();
-
// User is now verified and logged in
if (data.accessToken) {
setToken(data.accessToken);
@@ -251,6 +262,7 @@ const verifyToken = useCallback(async () => {
}
} catch (error) {
handleError(error);
+ throw error;
} finally {
setLoading(false);
}
@@ -258,6 +270,7 @@ const verifyToken = useCallback(async () => {
const forgotPassword = async (email: string) => {
setLoading(true);
+ setError(null);
try {
const response = await fetch(`${baseURL}/forgotPassword`, {
method: 'POST',
@@ -266,11 +279,12 @@ const verifyToken = useCallback(async () => {
});
if (!response.ok) {
- const data = await response.json();
- throw new Error(data.message || 'Password reset failed');
+ const data = await response.json().catch(() => ({}));
+ throw new Error(data.error || data.message || 'Password reset failed');
}
} catch (error) {
handleError(error);
+ throw error;
} finally {
setLoading(false);
}
@@ -282,6 +296,7 @@ const verifyToken = useCallback(async () => {
newPassword: string
) => {
setLoading(true);
+ setError(null);
try {
const response = await fetch(`${baseURL}/confirmForgotPassword`, {
method: 'POST',
@@ -290,11 +305,12 @@ const verifyToken = useCallback(async () => {
});
if (!response.ok) {
- const data = await response.json();
- throw new Error(data.message || 'Password update failed');
+ const data = await response.json().catch(() => ({}));
+ throw new Error(data.error || data.message || 'Password update failed');
}
} catch (error) {
handleError(error);
+ throw error;
} finally {
setLoading(false);
}
@@ -302,6 +318,7 @@ const verifyToken = useCallback(async () => {
const googleLogin = async (idToken: string) => {
setLoading(true);
+ setError(null);
try {
const response = await fetch(`${baseURL}/googleLogin`, {
method: 'POST',
@@ -309,8 +326,8 @@ const verifyToken = useCallback(async () => {
body: JSON.stringify({ idToken }),
});
- const data = await response.json();
- if (!response.ok) throw new Error(data.message || 'Google login failed');
+ const data = await response.json().catch(() => ({}));
+ if (!response.ok) throw new Error(data.error || data.message || 'Google login failed');
setToken(data.accessToken);
localStorage.setItem('token', data.accessToken);
@@ -344,12 +361,14 @@ const verifyToken = useCallback(async () => {
navigate('/');
} catch (error) {
handleError(error);
+ throw error;
} finally {
setLoading(false);
}
};
const logout = () => {
+ setError(null);
setToken(null);
localStorage.removeItem('token');
localStorage.removeItem(USER_CACHE_KEY);
@@ -365,6 +384,7 @@ const verifyToken = useCallback(async () => {
loading,
error,
handleError,
+ clearError,
login,
logout,
signup,