From 45c2c22129ce240f642083b8177d24959021654c Mon Sep 17 00:00:00 2001 From: pradeepkumar Date: Mon, 22 Dec 2025 13:14:00 +0530 Subject: [PATCH] feat: Add forgot password and reset password pages. --- src/app/forgot-password/page.tsx | 122 ++++++++++++++++ src/app/reset-password/page.tsx | 241 +++++++++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 src/app/forgot-password/page.tsx create mode 100644 src/app/reset-password/page.tsx diff --git a/src/app/forgot-password/page.tsx b/src/app/forgot-password/page.tsx new file mode 100644 index 0000000..98e10ab --- /dev/null +++ b/src/app/forgot-password/page.tsx @@ -0,0 +1,122 @@ +'use client'; + +import { useState } from 'react'; +import Link from 'next/link'; +import { authService, AuthService } from '@/services'; + +export default function ForgotPasswordPage() { + const [email, setEmail] = useState(''); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + const [submitted, setSubmitted] = useState(false); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setError(''); + + try { + await authService.forgotPassword({ email }); + setSubmitted(true); + } catch (err) { + setError(AuthService.getErrorMessage(err)); + } finally { + setLoading(false); + } + }; + + return ( +
+
+ {/* Logo */} +
+ RE-QuestN +
+ + {submitted ? ( + /* Success Message */ +
+
+
+ + + +
+

Check Your Email

+

+ We've sent a password reset link to {email}. Please check your inbox and spam folder. +

+ + Back to Login + + +
+
+ ) : ( + <> + {/* Forgot Password Heading */} +
+

Forgot Password

+

+ Enter your email address and we'll send you a link to reset your password +

+
+ + {/* Forgot Password Form */} +
+ {error && ( +
+ {error} +
+ )} + + {/* Email Input */} +
+ setEmail(e.target.value)} + required + className="w-full px-6 py-6 bg-[#f0f5fc] rounded-[20px] text-[#00293d] text-sm font-light placeholder:text-[#00293d] focus:outline-none focus:ring-2 focus:ring-[#00293d]/20 transition-all" + /> +
+ + {/* Submit Button */} + +
+ + {/* Back to Login */} +
+ + Back to Login + +
+ + )} +
+
+ ); +} diff --git a/src/app/reset-password/page.tsx b/src/app/reset-password/page.tsx new file mode 100644 index 0000000..72a3474 --- /dev/null +++ b/src/app/reset-password/page.tsx @@ -0,0 +1,241 @@ +'use client'; + +import { Suspense, useState } from 'react'; +import { useSearchParams } from 'next/navigation'; +import Link from 'next/link'; +import { authService, AuthService } from '@/services'; + +function ResetPasswordContent() { + const searchParams = useSearchParams(); + const token = searchParams.get('token'); + + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [showConfirmPassword, setShowConfirmPassword] = useState(false); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + const [success, setSuccess] = useState(false); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(''); + + // Validate passwords match + if (password !== confirmPassword) { + setError('Passwords do not match'); + return; + } + + // Validate password strength + const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/; + if (!passwordRegex.test(password)) { + setError('Password must be at least 8 characters with 1 uppercase, 1 lowercase, 1 number, and 1 special character'); + return; + } + + if (!token) { + setError('Invalid reset link. No token provided.'); + return; + } + + setLoading(true); + + try { + await authService.resetPassword({ token, password }); + setSuccess(true); + } catch (err) { + setError(AuthService.getErrorMessage(err)); + } finally { + setLoading(false); + } + }; + + // No token provided + if (!token) { + return ( +
+
+
+ + + +
+

Invalid Link

+

+ This password reset link is invalid or has expired. Please request a new one. +

+ + Request New Link + +
+
+ ); + } + + // Success state + if (success) { + return ( +
+
+
+ + + +
+

Password Reset Successful

+

+ Your password has been reset successfully. You can now login with your new password. +

+ + Go to Login + +
+
+ ); + } + + // Reset password form + return ( + <> + {/* Reset Password Heading */} +
+

Reset Password

+

+ Enter your new password below +

+
+ + {/* Reset Password Form */} +
+ {error && ( +
+ {error} +
+ )} + + {/* New Password Input */} +
+
+ setPassword(e.target.value)} + required + className="w-full px-6 py-6 bg-[#f0f5fc] rounded-[20px] text-[#00293d] text-sm font-light placeholder:text-[#00293d] focus:outline-none focus:ring-2 focus:ring-[#00293d]/20 transition-all pr-14" + /> + +
+

+ Min 8 chars, 1 uppercase, 1 lowercase, 1 number, 1 special character +

+
+ + {/* Confirm Password Input */} +
+ setConfirmPassword(e.target.value)} + required + className="w-full px-6 py-6 bg-[#f0f5fc] rounded-[20px] text-[#00293d] text-sm font-light placeholder:text-[#00293d] focus:outline-none focus:ring-2 focus:ring-[#00293d]/20 transition-all pr-14" + /> + +
+ + {/* Submit Button */} + +
+ + {/* Back to Login */} +
+ + Back to Login + +
+ + ); +} + +function LoadingFallback() { + return ( +
+
+
+ + + + +
+

Loading...

+

Please wait...

+
+
+ ); +} + +export default function ResetPasswordPage() { + return ( +
+
+ {/* Logo */} +
+ RE-QuestN +
+ + {/* Content */} + }> + + +
+
+ ); +}