diff --git a/src/AppRoutes.jsx b/src/AppRoutes.jsx index 6f6eb5f..752ad9e 100644 --- a/src/AppRoutes.jsx +++ b/src/AppRoutes.jsx @@ -17,7 +17,6 @@ import ChallengesHome from './[features]/challenges/pages/ChallengesHome'; import Login from './[features]/auth/pages/Login'; import Register from './[features]/auth/pages/CreateAccount'; import ForgotPassword from './[features]/auth/pages/ForgotPassword'; -import SetNewPassword from './[features]/auth/pages/SetNewPassword'; import ProfilePage from './[features]/auth/pages/ProfilePage'; import LeaderboardHome from './[features]/leaderboard/pages/LeaderboardHome'; import CartSidebar from './[features]/shop/components/CartSidebar'; @@ -96,15 +95,6 @@ function AppRoutes() { } /> - - - - } - /> - {/* Protected Routes */} { const [email, setEmail] = useState(''); + const [otp, setOtp] = useState(''); + const [step, setStep] = useState('email'); // 'email', 'otp', or 'reset' + const [newPassword, setNewPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); - const handleSubmit = (e) => { - e.preventDefault(); - console.log('Reset link sent to:', email); + const BASE_URL = 'http://localhost:8080/api/auth'; + + const handleSendOTP = async (e) => { + if (e) e.preventDefault(); + setError(''); + setIsLoading(true); + + try { + const response = await fetch(`${BASE_URL}/send-otp`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email }), + }); + + if (response.ok) { + setStep('otp'); + } else { + const msg = await response.text(); + setError(msg || 'Failed to send OTP'); + } + } catch { + setError('Failed to connect to the server'); + } finally { + setIsLoading(false); + } + }; + + const handleVerifyOTP = async (e) => { + if (e) e.preventDefault(); + setError(''); + setIsLoading(true); + + try { + const response = await fetch(`${BASE_URL}/verify-otp`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, otp }), + }); + + const isValid = await response.json(); + + if (response.ok && isValid === true) { + setStep('reset'); + } else { + setError('Invalid OTP. Please try again.'); + } + } catch { + setError('Failed to verify OTP'); + } finally { + setIsLoading(false); + } + }; + + const handleResetPassword = async (e) => { + if (e) e.preventDefault(); + setError(''); + + if (newPassword !== confirmPassword) { + setError('Passwords do not match'); + return; + } + + setIsLoading(true); + + try { + const response = await fetch(`${BASE_URL}/update-password-with-otp`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, otp, newPassword, confirmPassword }), + }); + + if (response.ok) { + alert( + 'Password reset successful! You can now login with your new password.', + ); + // Optional: Redirect to login + window.location.href = '/login'; + } else { + const msg = await response.text(); + setError(msg || 'Password reset failed'); + } + } catch { + setError('Failed to reset password'); + } finally { + setIsLoading(false); + } + }; + + const renderStep = () => { + switch (step) { + case 'email': + return ( + <> +

+ Forgot Password +

+

+ Enter your email address to receive a verification code +

+
+ setEmail(e.target.value)} + className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500" + required + /> + +
+ + ); + + case 'otp': + return ( + <> +

+ Verify OTP +

+

+ Enter the 6-digit code sent to {email} +

+
+ + setOtp(e.target.value.replace(/[^0-9]/g, '').substring(0, 6)) + } + className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 text-center text-xl tracking-widest" + required + maxLength={6} + /> + +
+ + | + +
+
+ + ); + + case 'reset': + return ( + <> +

+ Reset Password +

+

+ Create a new password for your account +

+
+ setNewPassword(e.target.value)} + className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500" + required + /> + setConfirmPassword(e.target.value)} + className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500" + required + /> + +
+ + ); + + default: + return null; + } }; return ( -
-
-

- Reset Your Password -

-
-
- setEmail(e.target.value)} - className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500" - required - /> +
+
+ {error && ( +
+ {error}
- - + )} + {renderStep()}
); diff --git a/src/[features]/auth/pages/SetNewPassword.jsx b/src/[features]/auth/pages/SetNewPassword.jsx deleted file mode 100644 index a856fa2..0000000 --- a/src/[features]/auth/pages/SetNewPassword.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import { useState } from 'react'; - -const SetNewPassword = () => { - const [password, setPassword] = useState(''); - const [confirmPassword, setConfirmPassword] = useState(''); - - const handleSubmit = (e) => { - e.preventDefault(); - if (password === confirmPassword) { - alert('Password changed successfully!'); - } else { - alert('Passwords do not match!'); - } - }; - - return ( -
-
-

- Set New Password -

-
- setPassword(e.target.value)} - className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500" - /> - setConfirmPassword(e.target.value)} - className="w-full p-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500" - /> - -
-
-
- ); -}; - -export default SetNewPassword;