Skip to content

Commit

Permalink
Merge pull request #29 from atlp-rwanda/ft-login
Browse files Browse the repository at this point in the history
#16 Implement Login Functionality
  • Loading branch information
faid-terence authored Jun 21, 2024
2 parents f8c3b3b + 4967da3 commit e1792f9
Show file tree
Hide file tree
Showing 26 changed files with 553 additions and 39 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,18 @@
"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
38 changes: 37 additions & 1 deletion src/Routes/Router.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
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';

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 Down Expand Up @@ -40,6 +52,30 @@ 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/success"
element={
<>
<PageTitle title="Knights Store | Login" />
{userToken && isAdmin && <Navigate to="/admin/dashboard" />}
{userToken && isVendor && <Navigate to="/vendor/dashboard" />}
{userToken && isBuyer && <Navigate to="/" />}
{!userToken && <GoogleLoginSuccess />}
</>
}
/>
</Routes>
);
};
Expand Down
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
26 changes: 26 additions & 0 deletions src/components/Menu/DesktopMenu.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import { AppDispatch } from '../../redux/store';
import { clearCredentials } from '../../redux/reducers/authReducer';
import { useDispatch } from 'react-redux';

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

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

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
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.
33 changes: 33 additions & 0 deletions src/pages/Authentication/GoogleLoginSuccess.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { useEffect } from 'react';
import axios from 'axios';
import { useDispatch } from 'react-redux';
import { AppDispatch } from '../../redux/store';
import PropagateLoader from 'react-spinners/PropagateLoader';
import { clearCredentials, setCredentials } from '../../redux/reducers/authReducer';

const GoogleLoginSuccess = () => {
const dispatch = useDispatch<AppDispatch>();
useEffect(() => {
axios
.get(`${import.meta.env.VITE_APP_API_URL}/user/login/success`, { withCredentials: true })
.then((response) => {
dispatch(setCredentials(response.data.data.token));
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.catch((error) => {
dispatch(clearCredentials(''));
});
}, [dispatch]);

return (
<div className="flex flex-col justify-center items-center min-h-[60vh] bg-background3 text-baseBlack">
<div className="bg-black mb-10">
<PropagateLoader color="#070F2B" />
</div>
<span className="text-lg pl-4">Signing in with Google </span>
<span className="text-neutrals700 italic text-sm pl-4">Please wait a moment.</span>
</div>
);
};

export default GoogleLoginSuccess;
Loading

0 comments on commit e1792f9

Please sign in to comment.