Skip to content

Commit

Permalink
Merge pull request #31 from atlp-rwanda/password-reset
Browse files Browse the repository at this point in the history
ft: This PR enable password reset feature for user
  • Loading branch information
faid-terence authored and maxCastro1 committed Jun 25, 2024
2 parents 3a91a4b + 9ebd45f commit 3328209
Show file tree
Hide file tree
Showing 51 changed files with 2,350 additions and 32 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
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,19 @@
"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",
"@types/jest": "^29.5.12",
"axios": "^1.7.2",
"axios-mock-adapter": "^1.22.0",
"jwt-decode": "^4.0.0",
"lucide-react": "^0.395.0",
"prettier": "^3.3.1",
"react": "^18.2.0",
Expand Down
5 changes: 5 additions & 0 deletions public/1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions public/Component 3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/Dashboard.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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.
5 changes: 5 additions & 0 deletions public/notification.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions public/search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/user-square.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ import { Toaster } from 'react-hot-toast';
function App() {
return (
<Provider store={store}>
<Layout />
<Toaster position="top-center" reverseOrder={false} />
<div data-testid="app-component">
<Layout />
<Toaster position="top-center" reverseOrder={false} />
</div>
</Provider>
);
}
Expand Down
70 changes: 56 additions & 14 deletions src/Routes/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ 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';
import DashboardLayout from '../layout/DashboardLayout';
import DashboarInnerLayout from '../layout/DashboarInnerLayout';
import DashboardProducts from '../components/Products/DashboardProducts/DashboardProducts';
import DashboardSingleProduct from '../components/Products/DashboardSingleProduct/DashboardSingleProduct';
import DashboardNewProducts from '../components/Products/DashboardNewProducts/DashboardNewProducts';
import MainLayout from '../layout/MainLayout';

const Router = () => {
const { userToken } = useSelector((state: RootState) => state.auth);
Expand All @@ -28,78 +36,112 @@ const Router = () => {
<Route
path="/register"
element={
<>
<MainLayout>
<PageTitle title="Knights Store | Register" />
<Register />
</>
</MainLayout>
}
/>

<Route
path="/register-vendor"
element={
<>
<MainLayout>
<PageTitle title="Knights Store | Register Vendor" />
<RegisterVendor />
</>
</MainLayout>
}
/>

<Route
path="/verify-email/:token"
element={
<>
<MainLayout>
<PageTitle title="Knights Store | Verify Email" />
<VerifyEmail />
</>
</MainLayout>
}
/>

<Route
path="/forgot-password"
element={
<MainLayout>
<PageTitle title="Knights Store | Forgot Password" />
<ForgotPassword />
{userToken && isAdmin && <Navigate to="/admin/dashboard" />}
{userToken && isVendor && <Navigate to="/vendor/dashboard" />}
{userToken && isBuyer && <Navigate to="/" />}
</MainLayout>
}
/>

<Route
path="/reset-password"
element={
<MainLayout>
<PageTitle title="Knights Store | Reset Password" />
<ResetPassword />
{userToken && isAdmin && <Navigate to="/admin/dashboard" />}
{userToken && isVendor && <Navigate to="/vendor/dashboard" />}
{userToken && isBuyer && <Navigate to="/" />}
</MainLayout>
}
/>

<Route
path="/login"
element={
<>
<MainLayout>
<PageTitle title="Knights Store | Login" />
{userToken && isAdmin && <Navigate to="/admin/dashboard" />}
{userToken && isVendor && <Navigate to="/vendor/dashboard" />}
{userToken && isBuyer && <Navigate to="/" />}
{!userToken && <Login />}
</>
</MainLayout>
}
/>
<Route
path="/login/google-auth"
element={
<>
<MainLayout>
<PageTitle title="Knights Store | Login" />
{userToken && isAdmin && <Navigate to="/admin/dashboard" />}
{userToken && isVendor && <Navigate to="/vendor/dashboard" />}
{userToken && isBuyer && <Navigate to="/" />}
{!userToken && <GoogleLoginSuccess />}
</>
</MainLayout>
}
/>
<Route
path="/suspended-account"
element={
<>
<MainLayout>
<PageTitle title="Knights Store | Suspended Account" />
{userToken && <Navigate to="/" />}
<SuspendedAccount />
</>
</MainLayout>
}
/>
<Route
path="/otp-verficaton"
element={
<>
<MainLayout>
<PageTitle title="Knights Store | Verify OTP" />
<OtpPage />
{userToken && isAdmin && <Navigate to="/admin/dashboard" />}
{userToken && isVendor && <Navigate to="/vendor/dashboard" />}
{userToken && isBuyer && <Navigate to="/" />}
</>
</MainLayout>
}
/>
<Route path="/vendor/dashboard" element={<DashboardLayout />}>
<Route path="products" element={<DashboarInnerLayout />}>
<Route path="" element={<DashboardProducts />} />
<Route path=":id" element={<DashboardSingleProduct />} />
<Route path="new" element={<DashboardNewProducts />} />
</Route>
</Route>
</Routes>
);
};
Expand Down
107 changes: 107 additions & 0 deletions src/components/Dashboard/DashboardNavbar/DashboardNavbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { jwtDecode } from 'jwt-decode';
import notificationIcon from '/notification.svg';
import searchIcon from '/search.svg';
import { Link } from 'react-router-dom';
import { AlignJustify } from 'lucide-react';

interface DashboardNavBarProps {
setOpenNav: (open: boolean) => void;
}

interface TokenPayload {
email: string;
name: string;
userType: string;
exp: number;
firstName: string;
}

const DashboardNavbar: React.FC<DashboardNavBarProps> = ({ setOpenNav }) => {
const [searchTerm, setSearchTerm] = useState('');
const [currentDateTime, setCurrentDateTime] = useState('');
const [userName, setUserName] = useState('');
const navigate = useNavigate();

const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchTerm(event.target.value);
};

useEffect(() => {
const updateDateTime = () => {
const now = new Date();
const options: Intl.DateTimeFormatOptions = {
day: 'numeric',
month: 'long',
year: 'numeric',
hour: 'numeric',
minute: 'numeric',
hour12: true,
timeZone: 'Etc/GMT-2',
timeZoneName: 'short'
};
const formattedDateTime = new Intl.DateTimeFormat('en-GB', options).format(now);
setCurrentDateTime(formattedDateTime);
};

updateDateTime();
const intervalId = setInterval(updateDateTime, 60000);

return () => clearInterval(intervalId);
}, []);

useEffect(() => {
const tokenString = localStorage.getItem('userToken');
if (!tokenString) {
navigate('/login');
return;
}

try {
const decodedToken = jwtDecode<TokenPayload>(tokenString);
//send name in token
setUserName(decodedToken.firstName);
} catch (error) {
console.error('Failed to decode token:', error);
navigate('/login');
}
}, [navigate]);

return (
<div
className="flex justify-end gap-4 lg:gap-0 flex-col-reverse items-end lg:flex-row lg:justify-between lg:items-center p-8 border-b-[1px] border-[#7c7c7c] text-black relative"
data-testid="navbar"
>
<Link to={'/vendor/dashboard'} className="lg:hidden text-4xl font-bold text-[#070f2b] absolute left-8 top-8">
Knight
</Link>
<div className="flex flex-col items-end lg:items-start">
<p className="font-bold text-xl">Welcome, {userName}</p>
<p className="text-[#7c7c7c] text-sm">{currentDateTime}</p>
</div>
<div className="flex flex-col lg:flex-row gap-4">
<div className="flex gap-4 items-center">
<button className="px-4 lg:border-r-2 border-[#7c7c7c]">
<img src={notificationIcon} alt="Notification" />
</button>
<button onClick={() => setOpenNav(true)} className="lg:hidden">
<AlignJustify />
</button>
</div>
<div className="hidden lg:flex px-4 py-2 rounded-lg border border-[#d1d1d1] gap-2">
<img src={searchIcon} alt="Search" />
<input
type="text"
className="bg-white w-[200px] outline-none"
placeholder="Search..."
value={searchTerm}
onChange={handleSearchChange}
/>
</div>
</div>
</div>
);
};

export default DashboardNavbar;
Loading

0 comments on commit 3328209

Please sign in to comment.