From e309dcfb42de6d2a1f23c2d64485f60db54a8b5a Mon Sep 17 00:00:00 2001 From: JeanIrad Date: Fri, 21 Jun 2024 22:28:10 +0200 Subject: [PATCH] finishing password reset finishing a rebase --- src/App.tsx | 15 +- .../authentication/LoginComponent.tsx | 6 +- src/components/common/Button.tsx | 7 +- src/components/footer/Footer.tsx | 149 +++--- src/components/navbar/Navbar.tsx | 437 ++++++++++-------- src/pages/NewPassword.tsx | 85 ++++ src/pages/ResetPassword.tsx | 60 +++ src/services/index.ts | 4 +- src/services/resetPassword.ts | 22 + src/utils/schemas.ts | 18 + 10 files changed, 520 insertions(+), 283 deletions(-) create mode 100644 src/pages/NewPassword.tsx create mode 100644 src/pages/ResetPassword.tsx create mode 100644 src/services/resetPassword.ts diff --git a/src/App.tsx b/src/App.tsx index 812cc15..ac86201 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -6,7 +6,10 @@ import Login from './pages/Login'; import LandingPage from './pages/LandingPage'; import GoogleAuthSuccess from './components/authentication/GoogleAuthSucces'; import { ToastContainer } from 'react-toastify'; +import { cn } from './utils'; +import ResetPassword from './pages/ResetPassword'; +import NewPassword from './pages/NewPassword'; const App = () => { const router = createBrowserRouter([ { @@ -24,6 +27,16 @@ const App = () => { path: 'auth/success/:token', element: , }, + { + path: '/reset-password', + children: [ + { path: '', element: }, + { + path: ':token', + element: , + }, + ], + }, ], }, { @@ -41,7 +54,7 @@ const App = () => { return ( <> - + ); }; diff --git a/src/components/authentication/LoginComponent.tsx b/src/components/authentication/LoginComponent.tsx index adb05e0..d93fe51 100644 --- a/src/components/authentication/LoginComponent.tsx +++ b/src/components/authentication/LoginComponent.tsx @@ -6,7 +6,7 @@ import GoogleIcon from '../../assets/googleIcon.svg'; import Button from '../common/Button'; import Input from '../common/Input'; import { loginSchema, LoginData } from '../../utils/schemas'; -import { useNavigate, useLocation, useSearchParams } from 'react-router-dom'; +import { useNavigate, useLocation, useSearchParams, Link } from 'react-router-dom'; import { useSelector, useDispatch } from 'react-redux'; import { setToken, setUser } from '../../redux/slices/userSlice'; import { useLoginUserMutation } from '../../services/authAPI'; @@ -106,7 +106,9 @@ const LoginComponent = () => { {...register('password')} error={errors.password && errors.password.message} /> -

Forget password

+ +

Forget password

+ - - Email is not valid - - - -

© 2024 Mavericks Shop. All rights reserved.

+ return ( + <> +
+
+
+ +
+

K309 St , Makuza plaza, Nyarugenge , Kigali, Rwanda

+

andela.mavericks@gmail.com

+

+250 788888888

- - ) + +
+
+ +
+ + +
+
+
+ +
+ + + + + +
+
+
+ +
+ + + + +
+
+
+ +

+ Be the first to get latest news about trends,Promotions and many more. +

+
+
+ + + +
+ Email is not valid +
+
+
+

+ © 2024 Mavericks Shop. All rights reserved. +

+
+ + ); } -export default Footer +export default Footer; diff --git a/src/components/navbar/Navbar.tsx b/src/components/navbar/Navbar.tsx index 075b0c9..77c1314 100644 --- a/src/components/navbar/Navbar.tsx +++ b/src/components/navbar/Navbar.tsx @@ -5,213 +5,246 @@ import { useEffect } from 'react'; import WishNav from './wishNav/WishNav'; import CartNav from './cartNav/CartNav'; const Navbar: React.FC = () => { - const [cartOpen, SetCartOpen] = useState(false) - const [wish, setWish] = useState(false) - const navigate = useNavigate(); - useEffect(() => { - const humbergurBtn = document.getElementById('humbergurBtn'); - const closeBtn = document.getElementById('close'); - const overlay = document.getElementById('overlay'); - const container = document.getElementById('humbergerContainer'); - const hideScrollbar = () => { - document.body.style.overflow = 'hidden'; - }; - const showScrollbar = () => { - document.body.style.overflow = 'auto'; - }; - closeBtn?.addEventListener('click', () => { - overlay?.classList.add('-translate-x-full'); - container?.classList.add('-translate-x-full'); - showScrollbar(); - }); - humbergurBtn?.addEventListener('click', () => { - overlay?.classList.remove('-translate-x-full'); - container?.classList.remove('-translate-x-full'); - hideScrollbar(); - }); - overlay?.addEventListener('click', e => { - if (e.target !== e.currentTarget) { - return; - } - overlay?.classList.add('-translate-x-full'); - container?.classList.add('-translate-x-full'); - showScrollbar(); - }); - }, []); + const [cartOpen, SetCartOpen] = useState(false); + const [wish, setWish] = useState(false); + const navigate = useNavigate(); + useEffect(() => { + const humbergurBtn = document.getElementById('humbergurBtn'); + const closeBtn = document.getElementById('close'); + const overlay = document.getElementById('overlay'); + const container = document.getElementById('humbergerContainer'); + const hideScrollbar = () => { + document.body.style.overflow = 'hidden'; + }; + const showScrollbar = () => { + document.body.style.overflow = 'auto'; + }; + closeBtn?.addEventListener('click', () => { + overlay?.classList.add('-translate-x-full'); + container?.classList.add('-translate-x-full'); + showScrollbar(); + }); + humbergurBtn?.addEventListener('click', () => { + overlay?.classList.remove('-translate-x-full'); + container?.classList.remove('-translate-x-full'); + hideScrollbar(); + }); + overlay?.addEventListener('click', e => { + if (e.target !== e.currentTarget) { + return; + } + overlay?.classList.add('-translate-x-full'); + container?.classList.add('-translate-x-full'); + showScrollbar(); + }); + }, []); - return ( - <> - { - (wish || cartOpen) &&
{ - if (e.target !== e.currentTarget) { - return - } - setWish(false) - SetCartOpen(false) - }} - className='absolute bg-[#00000000] top-0 w-full border h-full' style={{ zIndex: 10 }}>
+ return ( + <> + {(wish || cartOpen) && ( +
{ + if (e.target !== e.currentTarget) { + return; } -
-
-
-
- - - -
-
navigate('/')} - className='leading-none font-bold text-2xl hover:cursor-pointer md:text-3xl' - > -

MAVERICKSđź›’

-
-
-
- {/* Profile */} -
setWish(state => !state)} - className='rounded-full transition-all ease-in-out delay-100 hover:bg-grayColor active:bg-greenColor p-1 active:text-blackColor hover:text-blackColor'> - - - - {wish &&
{ - if (e.target !== e.currentTarget) { - setWish(false) - return - } - }} - className='absolute top-0 md:h-[115px] w-screen right-0 h-[100px] bg-[#0000000] z-40'> - - -
} -
- {/* Favorite */} - - - - - - - -
SetCartOpen(state => !state)} - className='rounded-full transition-all ease-in-out delay-100 hover:bg-grayColor active:bg-greenColor active:text-blackColor hover:text-blackColor p-1 select-none'> -
- - - - - 3 - -
- {/* Cart */} - {cartOpen &&
{ - if (e.target !== e.currentTarget) { - SetCartOpen(false) - return - } - }} - className='absolute top-0 md:h-[115px] w-screen right-0 h-[100px] bg-[#00000000] z-40'> - - -
} -
-
- {/* Searching box */} -
-
- - -
-
+ setWish(false); + SetCartOpen(false); + }} + className='absolute bg-[#00000000] top-0 w-full border h-full' + style={{ zIndex: 10 }} + >
+ )} +
+
+
+
+ + + +
+
navigate('/')} + className='leading-none font-bold text-2xl hover:cursor-pointer md:text-3xl' + > +

MAVERICKSđź›’

+
+
+
+ {/* Profile */} +
setWish(state => !state)} + className='rounded-full transition-all ease-in-out delay-100 hover:bg-grayColor active:bg-greenColor p-1 active:text-blackColor hover:text-blackColor' + > + + + + {wish && ( +
{ + if (e.target !== e.currentTarget) { + setWish(false); + return; + } + }} + className='absolute top-0 md:h-[115px] w-screen right-0 h-[100px] bg-[#0000000] z-40' + > +
- {/* Desktop Navs */} - - {/* Mobile sidebar Navs */} + )} +
+ {/* Favorite */} + + + + + + +
SetCartOpen(state => !state)} + className='rounded-full transition-all ease-in-out delay-100 hover:bg-grayColor active:bg-greenColor active:text-blackColor hover:text-blackColor p-1 select-none' + > +
+ + + + + 3 + +
+ {/* Cart */} + {cartOpen && (
{ + if (e.target !== e.currentTarget) { + SetCartOpen(false); + return; + } + }} + className='absolute top-0 md:h-[115px] w-screen right-0 h-[100px] bg-[#00000000] z-40' > -
- {/* close */} -
-
- - - -
-
-
- - - -
-
+
+ )} +
+
+ {/* Searching box */} +
+
+ + +
+
+
+ {/* Desktop Navs */} + + {/* Mobile sidebar Navs */} +
+
+ {/* close */} +
+
+ + + +
+
+
+ + +
- - ) -} +
+
+
+ + ); +}; -export default Navbar \ No newline at end of file +export default Navbar; diff --git a/src/pages/NewPassword.tsx b/src/pages/NewPassword.tsx new file mode 100644 index 0000000..6cb22b2 --- /dev/null +++ b/src/pages/NewPassword.tsx @@ -0,0 +1,85 @@ +import 'react-toastify/dist/ReactToastify.css'; +import { toast } from 'react-toastify'; + +import { cn } from '../utils'; +import Button from '../components/common/Button'; +import Input from '../components/common/Input'; +import Footer from '../components/footer/Footer'; +import Navbar from '../components/navbar/Navbar'; +import { useForm } from 'react-hook-form'; +import { zodResolver } from '@hookform/resolvers/zod'; +import { resetPasswordSchema } from '../utils/schemas'; +import { useParams, useNavigate } from 'react-router-dom'; + +import { useNewPasswordMutation } from '../services/resetPassword'; + +interface PasswordData { + newPassword: string; + passwordConfirm: string; +} +const NewPassword = () => { + const navigate = useNavigate(); + const { token } = useParams<{ token: string }>(); + const [mutate, { isLoading }] = useNewPasswordMutation(); + const { + register, + handleSubmit, + reset, + formState: { errors }, + } = useForm({ resolver: zodResolver(resetPasswordSchema) }); + + const onSubmit = async (data: PasswordData) => { + try { + const { newPassword } = data; + const res = await mutate({ newPassword, token }); + if (res.error) { + const { data } = res.error as any; + return toast.error(data.error || 'Failed, please try again later!'); + } else { + reset(); + const { + data: { message }, + } = res; + toast.success(message || 'Password reset successfully!'); + setTimeout(() => navigate('/login'), 3000); + } + } catch (err) { + toast.error('An unexpected error occurred'); + } + }; + return ( + <> + +
+ + {errors.newPassword &&

{errors.newPassword.message}

} + + {errors.passwordConfirm && ( +

{errors.passwordConfirm.message}

+ )} +