Skip to content

Commit

Permalink
ft<password-reset> enabling password reset feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Ndevu12 committed Jun 24, 2024
1 parent f8c3b3b commit 62bf410
Show file tree
Hide file tree
Showing 45 changed files with 1,922 additions and 43 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ dist
.env
dist-ssr
*.local
yarn.lock

# Editor directories and files
.vscode/*
Expand Down
1 change: 1 addition & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<!-- poppin font -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="preconnect" href='https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap' rel="stylesheet">;
<link
href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"
rel="stylesheet"
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,34 @@
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"lint:fix": "eslint . --fix",
"format": "prettier --write ./**/*.{js,jsx,ts,tsx,css,md,json} --config ./.prettierrc.json",
"format": "prettier --write ./**/*.{js,ts,tsx,css,md,json} --config ./.prettierrc.json",
"preview": "vite --host 0.0.0.0",
"prepare": "husky",
"test": "vitest --coverage"
},
"dependencies": {
"@reduxjs/toolkit": "^2.2.5",
"@testing-library/dom": "^10.1.0",
"@testing-library/user-event": "^14.5.2",
"axios": "^1.7.2",
"lucide-react": "^0.395.0",
"prettier": "^3.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.52.0",
"react-hot-toast": "^2.4.1",
"react-jwt": "^1.2.1",
"react-redux": "^9.1.2",
"react-router-dom": "^6.23.1",
"react-spinners": "^0.13.8",
"redux": "^5.0.1",
"vitest": "^1.6.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.4.5",
"@testing-library/react": "^16.0.0",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^20.14.2",
"@types/node": "^20.14.7",
"@types/react": "^18.2.66",
"@types/react-dom": "^18.2.22",
"@typescript-eslint/eslint-plugin": "^7.2.0",
Expand Down
4 changes: 2 additions & 2 deletions public/knights logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 66 additions & 1 deletion src/Routes/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import { Navigate, Route, Routes } from 'react-router-dom';
import PageTitle from '../components/PageTitle';
import WelcomePage from '../pages/welcomePage';
import Register from '../pages/Authentication/Register';
import RegisterVendor from '../pages/Authentication/RegisterVendor';
import VerifyEmail from '../pages/Authentication/VerifyEmail';
import Login, { DecodedToken } from '../pages/Authentication/Login';
import { useSelector } from 'react-redux';
import { RootState } from '../redux/store';
import GoogleLoginSuccess from '../pages/Authentication/GoogleLoginSuccess';
import { useJwt } from 'react-jwt';
import OtpPage from '../pages/Authentication/OtpPage';
import SuspendedAccount from '../components/SuspendedAccount/SuspendedAccount';
import { ForgotPassword } from '../pages/Authentication/ForgotPassword';
import { ResetPassword } from '../pages/Authentication/ResetPassword';

const Router = () => {
const { userToken } = useSelector((state: RootState) => state.auth);
const { decodedToken } = useJwt<DecodedToken>(userToken);

const isAdmin = decodedToken?.role.toLowerCase() === 'admin';
const isVendor = decodedToken?.role.toLowerCase() === 'vendor';
const isBuyer = decodedToken?.role.toLowerCase() === 'buyer';

return (
<Routes>
<Route path="/" element={<WelcomePage />} />
Expand All @@ -31,6 +47,9 @@ const Router = () => {
}
/>

<Route path="/forgot-password" element={<ForgotPassword />} />
<Route path="/reset-password" element={<ResetPassword />} />

<Route
path="/verify-email/:token"
element={
Expand All @@ -40,6 +59,52 @@ const Router = () => {
</>
}
/>
<Route
path="/login"
element={
<>
<PageTitle title="Knights Store | Login" />
{userToken && isAdmin && <Navigate to="/admin/dashboard" />}
{userToken && isVendor && <Navigate to="/vendor/dashboard" />}
{userToken && isBuyer && <Navigate to="/" />}
{!userToken && <Login />}
</>
}
/>
<Route
path="/login/google-auth"
element={
<>
<PageTitle title="Knights Store | Login" />
{userToken && isAdmin && <Navigate to="/admin/dashboard" />}
{userToken && isVendor && <Navigate to="/vendor/dashboard" />}
{userToken && isBuyer && <Navigate to="/" />}
{!userToken && <GoogleLoginSuccess />}
</>
}
/>
<Route
path="/suspended-account"
element={
<>
<PageTitle title="Knights Store | Suspended Account" />
{userToken && <Navigate to="/" />}
<SuspendedAccount />
</>
}
/>
<Route
path="/otp-verficaton"
element={
<>
<PageTitle title="Knights Store | Verify OTP" />
<OtpPage />
{userToken && isAdmin && <Navigate to="/admin/dashboard" />}
{userToken && isVendor && <Navigate to="/vendor/dashboard" />}
{userToken && isBuyer && <Navigate to="/" />}
</>
}
/>
</Routes>
);
};
Expand Down
3 changes: 3 additions & 0 deletions src/assets/iconoir_xmark-circle (2).svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 8 additions & 15 deletions src/components/Footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ function Footer() {
<div className="w-full flex flex-col items-center justify-start gap-y-8 py-8">
<div className="w-full md:w-[80%] flex items-start justify-between p-4 md:p-0">
<div className="flex flex-col items-start justify-start gap-y-6 pb-10">
<h1 className="flex items-end justify-start gap-x-2 text-primary capitalize font-medium text-2xl">
<h1 className="flex items-end justify-start gap-x-2 text-primary capitalize font-medium text-xl">
<img src={logo} alt="" />
KNIGHTS STORE
</h1>
Expand Down Expand Up @@ -42,24 +42,17 @@ function Footer() {
<h2 className="flex items-center justify-start gap-x-2 text-primary font-medium text-sm md:text-lg">
Address / Office
</h2>

<p className="mt-8 flex items-center justify-start gap-x-2 text-black2 font-normal text-sm md:text-lg">
Kigali-Rwanda
</p>
<p className="flex items-center justify-start gap-x-2 text-black2 font-normal text-sm md:text-lg">
Gasabo, Kimironko
</p>
<p className="flex items-center justify-start gap-x-2 text-black2 font-normal text-sm md:text-lg">
Tel No: +250780288777
</p>
<p className="flex items-center justify-start gap-x-2 text-black2 font-normal text-sm md:text-lg">
Email: knights@andela.com
</p>
<div className="flex flex-col gap-y-1 text-sm text-grey2">
<p>Kigali-Rwanda</p>
<p>Gasabo, Kimironko</p>
<p>Tel No: +250780288777</p>
<p>Email: knights@andela.com</p>
</div>
</div>
</div>
<hr className="w-[80%] h-[2px] bg-grey3" />
<div className="w-full flex items-center justify-center">
<p className="text-primary text-sm md:text-lg">
<p className="text-primary text-sm md:text-base">
© {new Date().getFullYear()} Knights Store. All Rights Reserved.
</p>
</div>
Expand Down
31 changes: 31 additions & 0 deletions src/components/Menu/DesktopMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { AppDispatch } from '../../redux/store';
import { clearCredentials } from '../../redux/reducers/authReducer';
import { useDispatch } from 'react-redux';
import { clearUser } from '../../redux/reducers/userReducer';
import { useNavigate } from 'react-router-dom';

function DesktopMenu() {
const navigate = useNavigate();
const dispatch = useDispatch<AppDispatch>();

const logoutHandler = () => {
dispatch(clearCredentials());
dispatch(clearUser());
navigate('/');
};

return (
<div className="bg-baseWhite border border-neutral-300 rounded-2 w-48 p-1 text-neutral-600">
<ul className="flex flex-col ">
<li className="hover:bg-neutral-300 pl-5 py-2 cursor-pointer">WishList</li>
<li className="hover:bg-neutral-300 pl-5 py-2 cursor-pointer">Profile</li>
<li onClick={logoutHandler} className="hover:bg-neutral-300 pl-5 py-2 cursor-pointer">
Logout
</li>
</ul>
</div>
);
}

export default DesktopMenu;
65 changes: 51 additions & 14 deletions src/components/Navbar/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,76 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Search } from 'lucide-react';
import knightsLogo from '../../images/logo.png';
import cart from '../../images/cart.png';
import user from '../../images/user.png';
import notificationBell from '../../images/bell.png';
import { useSelector } from 'react-redux';
import { RootState } from '../../redux/store';
import { Link } from 'react-router-dom';
import DesktopMenu from '../Menu/DesktopMenu';

function Navbar() {
const { userToken } = useSelector((state: RootState) => state.auth);
const [showDesktopMenu, setShowDesktopMenu] = useState(false);

useEffect(() => {
setShowDesktopMenu(false);
}, [userToken]);

const desktopMenuHandler = () => {
setShowDesktopMenu((prevData) => !prevData);
};

return (
<nav className="w-full min-h-[10vh] h-auto flex items-center justify-between bg-white px-12 py-5 sticky top-0 shadow-navbar">
<h1 className="flex items-center justify-start gap-x-2 text-primary capitalize font-medium text-2xl">
<nav className="w-full min-h-[10vh] h-auto flex items-center justify-between bg-white px-6 lg:px-12 py-5 sticky top-0 shadow-navbar text-baseBlack">
<h1 className="flex items-center justify-start gap-x-2 text-primary capitalize font-medium text-xl">
<img src={knightsLogo} alt="" />
KNIGHTS STORE
</h1>

<div className="hidden w-[500px] min-h-[50px] md:flex items-center justify-between gap-x-1 px-4 py-2 border border-grey2 bg-white rounded-full">
<div className="hidden lg:w-[26rem] md:w-72 min-h-10 md:flex items-center justify-between gap-x-1 px-4 py-2 border border-neutrals500 bg-white rounded-3xl">
<input
className="w-full h-[100%] border-none outline-none bg-white text-grey2 text-lg placeholder-grey2"
className="w-full h-[100%] border-none outline-none bg-white text-grey2 text-base placeholder-grey2"
type="text"
placeholder="search for anything"
/>
<Search strokeWidth={1.5} className="text-orange" />
</div>

<div className="hidden md:flex items-center justify-end gap-x-10">
<a
href="/login"
className="h-[50px] flex items-center justify-center no-underline text-primary border border-primary rounded-full px-10 text-xl"
>
Login
</a>
<div className="hidden md:flex items-center justify-end md:gap-x-5 lg:gap-x-10">
{!userToken && (
<Link
to="/login"
className="h-10 md:w-24 lg:w-28 flex items-center justify-center no-underline text-primary border border-neutrals500 rounded-full px-9 text-base"
>
Login
</Link>
)}
{userToken && (
<a href="/notification" className="relative cursor-pointer">
<img src={notificationBell} alt="" className="w-7 h-7" />
<span className="absolute min-w-6 h-6 top-0 right-[3px] mt-[-10px] mr-[-15px] bg-orange text-white text-sm flex items-center justify-center rounded-full leading-none p-1">
0
</span>
</a>
)}
<a href="/cart" className="relative cursor-pointer">
<img src={cart} alt="" className="w-[30px] h-[30px]" />
<span className="absolute min-w-[25px] h-[25px] top-0 right-0 mt-[-10px] mr-[-15px] bg-orange text-white flex items-center justify-center rounded-full leading-none p-1">
<img src={cart} alt="" className="w-7 h-7" />
<span className="absolute min-w-6 h-6 top-0 right-[1px] mt-[-10px] mr-[-15px] bg-orange text-white text-sm flex items-center justify-center rounded-full leading-none p-1">
0
</span>
</a>
{userToken && (
<div onClick={desktopMenuHandler} className="relative cursor-pointer">
<img src={user} alt="User-Icon" className="w-8 h-8" />
</div>
)}
</div>
{userToken && showDesktopMenu && (
<div className="absolute right-6 top-[12vh]">
<DesktopMenu />
</div>
)}
</nav>
);
}
Expand Down
20 changes: 20 additions & 0 deletions src/components/SuspendedAccount/SuspendedAccount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import iconXMark from '../../assets/iconoir_xmark-circle (2).svg';

function SuspendedAccount() {
return (
<div className="flex flex-col justify-center items-center min-h-[60vh] bg-background3 text-baseBlack">
<img src={iconXMark} alt="" className="w-24 pb-7" />
<p className="text-center text-xl sm:text-2xl font-semibold">Your account has been suspended!</p>
<p className=" text-base sm:text-lg text-center ">
Please contact our support team at{' '}
<a href="mailto:knights@andela.com" className="italic underline text-orange">
knights@andela.com
</a>{' '}
for more information.
</p>
</div>
);
}

export default SuspendedAccount;
2 changes: 0 additions & 2 deletions src/components/welcome.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import React from 'react';
import logo from '../../public/knights logo.svg';
import ProductList from './ProductList';

const Welcome = () => {
return (
<div>
<h1>Welcome to Knights ecommerce</h1>
<img className="logo" src={logo} alt="Knights Logo" />
<ProductList />
</div>
);
Expand Down
Binary file added src/images/bell.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/images/google.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/images/user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 62bf410

Please sign in to comment.