From da47fe80857dbfae94c2e684f5089e2a5d26c8db Mon Sep 17 00:00:00 2001 From: bitstarkbridge Date: Thu, 5 Mar 2026 01:53:09 -0800 Subject: [PATCH 1/2] feature:Reset Password UI --- src/pages/auth/ResetPasswordPage.jsx | 339 +++++++++++++++++++++++++++ src/routes/AppRouter.jsx | 2 + 2 files changed, 341 insertions(+) create mode 100644 src/pages/auth/ResetPasswordPage.jsx diff --git a/src/pages/auth/ResetPasswordPage.jsx b/src/pages/auth/ResetPasswordPage.jsx new file mode 100644 index 0000000..a754b16 --- /dev/null +++ b/src/pages/auth/ResetPasswordPage.jsx @@ -0,0 +1,339 @@ +import { useState, useEffect } 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(false); + + const { + control, + handleSubmit, + formState: { errors, isValid, isSubmitting }, + } = useForm({ + resolver: yupResolver(resetPasswordSchema), + mode: "onChange", + defaultValues: { + password: "", + confirmPassword: "", + token: token || "", + }, + }); + + // Validate token exists + useEffect(() => { + if (!token) { + setIsExpired(true); + } + }, [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; diff --git a/src/routes/AppRouter.jsx b/src/routes/AppRouter.jsx index a41a5e1..e5c41c5 100644 --- a/src/routes/AppRouter.jsx +++ b/src/routes/AppRouter.jsx @@ -9,6 +9,7 @@ import Explore from '../pages/Explore'; import Login from '../pages/Login'; import Register from '../pages/Register'; import ForgotPasswordPage from '../pages/auth/ForgotPasswordPage'; +import ResetPasswordPage from '../pages/auth/ResetPasswordPage'; import Dashboard from '../pages/Dashboard'; import CampaignDetails from '../pages/CampaignDetails'; import CreateCampaign from '../pages/CreateCampaign'; @@ -25,6 +26,7 @@ const AppRouter = () => { } /> } /> } /> + } /> } /> } /> } /> From e32ae179a685713d990401e3c1587224b654115b Mon Sep 17 00:00:00 2001 From: bitstarkbridge Date: Thu, 5 Mar 2026 01:56:33 -0800 Subject: [PATCH 2/2] feature:Reset Password UI --- src/pages/auth/ResetPasswordPage.jsx | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/pages/auth/ResetPasswordPage.jsx b/src/pages/auth/ResetPasswordPage.jsx index a754b16..7bd07e3 100644 --- a/src/pages/auth/ResetPasswordPage.jsx +++ b/src/pages/auth/ResetPasswordPage.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect } from "react"; +import { useState } from "react"; import { useParams, useNavigate, Link } from "react-router-dom"; import { useDispatch } from "react-redux"; import { useForm, Controller } from "react-hook-form"; @@ -19,7 +19,7 @@ const ResetPasswordPage = () => { const navigate = useNavigate(); const dispatch = useDispatch(); const [isSuccess, setIsSuccess] = useState(false); - const [isExpired, setIsExpired] = useState(false); + const [isExpired, setIsExpired] = useState(!token); const { control, @@ -35,13 +35,6 @@ const ResetPasswordPage = () => { }, }); - // Validate token exists - useEffect(() => { - if (!token) { - setIsExpired(true); - } - }, [token]); - const handleResetPassword = async (data) => { try { await dispatch(resetPassword({ token: data.token, password: data.password })).unwrap();