diff --git a/src/pages/auth/ResetPasswordPage.jsx b/src/pages/auth/ResetPasswordPage.jsx new file mode 100644 index 0000000..7bd07e3 --- /dev/null +++ b/src/pages/auth/ResetPasswordPage.jsx @@ -0,0 +1,332 @@ +import { useState } from "react"; +import { useParams, useNavigate, Link } from "react-router-dom"; +import { useDispatch } from "react-redux"; +import { useForm, Controller } from "react-hook-form"; +import { yupResolver } from "@hookform/resolvers/yup"; +import { HiArrowLeft } from "react-icons/hi"; +import { Button } from "../../components/common/button"; +import PasswordInput from "../../components/common/passwordInput"; +import { resetPassword } from "../../features/auth/authThunks"; +import { resetPasswordSchema } from "../../features/auth/authValidation"; + +/** + * ResetPasswordPage + * Allows users to reset their password using a token from email link. + * Handles token validation, password reset, and appropriate error states. + */ +const ResetPasswordPage = () => { + const { token } = useParams(); + const navigate = useNavigate(); + const dispatch = useDispatch(); + const [isSuccess, setIsSuccess] = useState(false); + const [isExpired, setIsExpired] = useState(!token); + + const { + control, + handleSubmit, + formState: { errors, isValid, isSubmitting }, + } = useForm({ + resolver: yupResolver(resetPasswordSchema), + mode: "onChange", + defaultValues: { + password: "", + confirmPassword: "", + token: token || "", + }, + }); + + const handleResetPassword = async (data) => { + try { + await dispatch(resetPassword({ token: data.token, password: data.password })).unwrap(); + setIsSuccess(true); + + // Redirect to login after 2 seconds + setTimeout(() => { + navigate("/login"); + }, 2000); + } catch (error) { + // Check if error indicates expired/invalid token + const errorMessage = error?.message?.toLowerCase() || ""; + if ( + errorMessage.includes("expired") || + errorMessage.includes("invalid") || + errorMessage.includes("token") + ) { + setIsExpired(true); + } + } + }; + + return ( +
+
+ {/* Logo Section */} +
+
+ S +
+ + StellarAid + +
+ + {/* Success State */} + {isSuccess && ( +
+
+

+ Password Reset! +

+

+ Your password has been successfully reset. +

+
+ +
+
+

Redirecting to login...

+
+
+
+ )} + + {/* Expired Token State */} + {isExpired && !isSuccess && ( +
+
+

+ Link Expired +

+

+ This password reset link has expired or is invalid. +

+
+ +
+
+

+ This link has expired +

+

+ Password reset links are valid for a limited time for security reasons. +

+
+ +
+ + Request a new reset link + +
+
+ +
+ (e.target.style.color = "#1d4ed8")} + onMouseLeave={(e) => (e.target.style.color = "#64748b")} + > + + Back to Login + +
+
+ )} + + {/* Reset Password Form */} + {!isSuccess && !isExpired && ( + <> +
+

+ Reset Password +

+

+ Enter your new password below. +

+
+ +
+ {/* New Password */} + ( + + )} + /> + {errors.password && ( +

+ {errors.password.message} +

+ )} + + {/* Confirm New Password */} + ( + + )} + /> + {errors.confirmPassword && ( +

+ {errors.confirmPassword.message} +

+ )} + + + +
+ (e.target.style.color = "#1d4ed8")} + onMouseLeave={(e) => (e.target.style.color = "#64748b")} + > + + Back to Login + +
+ + + )} +
+
+ ); +}; + +export default ResetPasswordPage;