Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion frontend/src/Pages/Authentication.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React, { useState } from 'react';
import React, { useState, useContext } from 'react';
import { Button } from '@/components/ui/button';
import { LoginForm, SignUpForm, OTPVerificationForm, ForgotPasswordForm, ResetPasswordForm } from './Authentication/forms.tsx';
import { Link, useLocation } from 'react-router-dom';
import DebateCover from '../assets/DebateCover4.svg';
import { ThemeToggle } from '@/components/ThemeToggle';
import { AuthContext } from '../context/authContext';

const LeftSection = () => (
<div className="hidden md:flex w-full h-full flex-col justify-between bg-muted p-10 text-black dark:text-white">
Expand Down Expand Up @@ -105,6 +106,7 @@ const RightSection: React.FC<RightSectionProps> = ({

const Authentication = () => {
const location = useLocation();
const authContext = useContext(AuthContext);
// Extend authMode to include 'resetPassword'
const [authMode, setAuthMode] = useState<
'login' | 'signup' | 'otpVerification' | 'forgotPassword' | 'resetPassword'
Expand All @@ -115,33 +117,41 @@ const Authentication = () => {
const [infoMessage, setInfoMessage] = useState('');

const toggleAuthMode = () => {
authContext?.clearError();
setInfoMessage('');
setAuthMode((prevMode) => (prevMode === 'login' ? 'signup' : 'login'));
};

// Start OTP verification process
const startOtpVerification = (email: string) => {
authContext?.clearError();
setEmailForOTP(email);
setAuthMode('otpVerification');
};

// Handle successful OTP verification
const handleOtpVerified = () => {
authContext?.clearError();
setAuthMode('login');
};

// Start forgot password process
const startForgotPassword = () => {
authContext?.clearError();
setInfoMessage('');
setAuthMode('forgotPassword');
};

// Start reset password process
const startResetPassword = (email: string) => {
authContext?.clearError();
setEmailForPasswordReset(email);
setAuthMode('resetPassword');
};

// Handle successful password reset
const handlePasswordReset = () => {
authContext?.clearError();
setInfoMessage('Your password was successfully reset. You can now log in.');
setAuthMode('login');
};
Expand Down
173 changes: 122 additions & 51 deletions frontend/src/Pages/Authentication/forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,32 +20,41 @@ export const LoginForm: React.FC<LoginFormProps> = ({ startForgotPassword, infoM
throw new Error('LoginForm must be used within an AuthProvider');
}

const { login, googleLogin, error, loading } = authContext;
const { login, googleLogin, error, loading, clearError } = authContext;

const [localError, setLocalError] = useState<string | null>(null);

useEffect(() => {
clearError();
}, []);

const MIN_PASSWORD_LENGTH = 8;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (password.length < MIN_PASSWORD_LENGTH) {
setLocalError(`Password must be at least ${MIN_PASSWORD_LENGTH} characters`);
return;
}
setLocalError(null);
await login(email, password);
};



const MIN_PASSWORD_LENGTH = 8;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
clearError();
if (password.length < MIN_PASSWORD_LENGTH) {
setLocalError(`Password must be at least ${MIN_PASSWORD_LENGTH} characters`);
return;
}
setLocalError(null);
try {
await login(email, password);
} catch {
// Handled by authContext error state
}
};

const handleGoogleLogin = useCallback(
(response: { credential: string; select_by: string }) => {
const idToken = response.credential;
googleLogin(idToken);
},
[googleLogin]
);
const handleGoogleLogin = useCallback(
async (response: { credential: string; select_by: string }) => {
try {
const idToken = response.credential;
await googleLogin(idToken);
} catch {
// Handled by authContext error state
}
},
[googleLogin]
);
useEffect(() => {
const google = window.google;
if (!google?.accounts) {
Expand Down Expand Up @@ -79,21 +88,28 @@ const handleGoogleLogin = useCallback(
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"
/>
<Input
type={passwordVisible ? "text" : "password"}
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
onChange={(e) => {
setPassword(e.target.value);
if (error) clearError();
if (localError) setLocalError(null);
}}
className="mb-1 dark:border-white"
/>
{localError && (
<p className="text-red-500 text-sm mt-2">
{localError}
</p>
)}
)}
<div className='w-full flex justify-start items-center pl-1'>
<div className='w-4'>
<Input
Expand Down Expand Up @@ -135,7 +151,11 @@ export const SignUpForm: React.FC<SignUpFormProps> = ({ startOtpVerification })
throw new Error('SignUpForm must be used within an AuthProvider');
}

const { signup, googleLogin, error, loading } = authContext;
const { signup, googleLogin, error, loading, clearError } = authContext;

useEffect(() => {
clearError();
}, []);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
Expand All @@ -144,18 +164,26 @@ export const SignUpForm: React.FC<SignUpFormProps> = ({ startOtpVerification })
authContext.handleError('Passwords do not match');
return;
}

await signup(email, password);
startOtpVerification(email);
clearError();
try {
await signup(email, password);
startOtpVerification(email);
} catch {
// Handled by authContext error state
}
};

const handleGoogleLogin = useCallback(
(response: { credential: string; select_by: string }) => {
const idToken = response.credential;
googleLogin(idToken);
},
[googleLogin]
);
const handleGoogleLogin = useCallback(
async (response: { credential: string; select_by: string }) => {
try {
const idToken = response.credential;
await googleLogin(idToken);
} catch {
// Handled by authContext error state
}
},
[googleLogin]
);


useEffect(() => {
Expand Down Expand Up @@ -190,21 +218,30 @@ export const SignUpForm: React.FC<SignUpFormProps> = ({ 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"
/>
<Input
type={passwordVisible ? "text" : "password"}
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
onChange={(e) => {
setPassword(e.target.value);
if (error) clearError();
}}
className="mb-2 dark:border-white"
/>
<Input
type={passwordVisible ? "text" : "password"}
placeholder="confirm password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
onChange={(e) => {
setConfirmPassword(e.target.value);
if (error) clearError();
}}
className="mb-4 dark:border-white"
/>
<div className='w-full flex justify-start items-center pl-1'>
Expand Down Expand Up @@ -240,12 +277,21 @@ export const OTPVerificationForm: React.FC<OTPVerificationFormProps> = ({ 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 (
Expand All @@ -256,7 +302,10 @@ export const OTPVerificationForm: React.FC<OTPVerificationFormProps> = ({ email,
<Input
type="text"
value={otp}
onChange={(e) => setOtp(e.target.value)}
onChange={(e) => {
setOtp(e.target.value);
if (error) clearError();
}}
placeholder="Enter OTP"
className="w-full mb-4 dark:border-white"
/>
Expand Down Expand Up @@ -293,7 +342,8 @@ export const ForgotPasswordForm: React.FC<ForgotPasswordFormProps> = ({
});

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;
}

Expand All @@ -311,7 +361,10 @@ export const ForgotPasswordForm: React.FC<ForgotPasswordFormProps> = ({
<Input
type="email"
value={email}
onChange={(e) => 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"
/>
Expand Down Expand Up @@ -340,7 +393,11 @@ export const ResetPasswordForm: React.FC<ResetPasswordFormProps> = ({ 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();
Expand All @@ -350,9 +407,14 @@ export const ResetPasswordForm: React.FC<ResetPasswordFormProps> = ({ 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 (
Expand All @@ -362,21 +424,30 @@ export const ResetPasswordForm: React.FC<ResetPasswordFormProps> = ({ email, han
<Input
type="text"
value={code}
onChange={(e) => 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"
/>
<Input
type={passwordVisible ? "text" : "password"}
value={newPassword}
onChange={(e) => setNewPassword(e.target.value)}
onChange={(e) => {
setNewPassword(e.target.value);
if (error) clearError();
}}
placeholder="New Password"
className="w-full mb-2 dark:border-white"
/>
<Input
type={passwordVisible ? "text" : "password"}
value={confirmNewPassword}
onChange={(e) => 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"
/>
Expand Down
Loading