From 9f93ac80768ab945e065479d9b4da2e866a6d9ce Mon Sep 17 00:00:00 2001 From: Ossama9 Date: Sun, 19 Nov 2023 23:16:47 +0100 Subject: [PATCH 1/3] Separate front by roles ok & add layout --- back/api/src/Entity/User.php | 18 +++- back/api/src/State/UserPasswordHasher.php | 8 +- front/next.config.js | 8 +- front/src/components/ChooseLayout.jsx | 88 +++++++++++++++++++ front/src/components/Header.jsx | 58 ++++++++++++ front/src/components/Sidebar.jsx | 40 +++++++++ front/src/hooks/useAuth.js | 12 ++- front/src/hooks/useRequestsProvider.js | 2 - front/src/hooks/useResetPassword.js | 7 +- front/src/layouts/AdminLayout.jsx | 24 +++++ front/src/layouts/DefaultLayout.jsx | 13 +++ front/src/layouts/ProviderLayout.jsx | 20 +++++ front/src/layouts/UserLayout.jsx | 22 +++++ front/src/pages/_app.js | 16 ++-- .../{provider => admin/requests}/[id].jsx | 3 - .../{provider => admin/requests}/index.js | 2 +- front/src/pages/auth/login.jsx | 4 +- front/src/pages/auth/logout.jsx | 14 ++- front/src/pages/index.jsx | 3 + front/src/pages/provider/index.jsx | 7 ++ .../pages/reset-password/update/[token].jsx | 1 + .../apply-to-be-provider.jsx} | 2 +- front/src/pages/{auth => user}/profile.jsx | 25 ++++-- front/src/providers/AuthProvider.jsx | 59 +++++++++---- front/src/services/authService.js | 28 +++++- front/src/services/httpClient.js | 18 ++-- front/src/styles/globals.css | 3 + 27 files changed, 428 insertions(+), 77 deletions(-) create mode 100644 front/src/components/ChooseLayout.jsx create mode 100644 front/src/components/Header.jsx create mode 100644 front/src/components/Sidebar.jsx create mode 100644 front/src/layouts/AdminLayout.jsx create mode 100644 front/src/layouts/DefaultLayout.jsx create mode 100644 front/src/layouts/ProviderLayout.jsx create mode 100644 front/src/layouts/UserLayout.jsx rename front/src/pages/{provider => admin/requests}/[id].jsx (96%) rename front/src/pages/{provider => admin/requests}/index.js (92%) create mode 100644 front/src/pages/provider/index.jsx rename front/src/pages/{provider/apply.jsx => user/apply-to-be-provider.jsx} (97%) rename front/src/pages/{auth => user}/profile.jsx (83%) diff --git a/back/api/src/Entity/User.php b/back/api/src/Entity/User.php index e8d1b02f..7b4513b8 100644 --- a/back/api/src/Entity/User.php +++ b/back/api/src/Entity/User.php @@ -85,9 +85,11 @@ class User implements UserInterface, PasswordAuthenticatedUserInterface * @var string The hashed password */ #[ORM\Column] + private ?string $password = null; + #[Assert\NotBlank(groups: ['user:create'])] #[Groups(['user:create', 'user:update'])] - private ?string $password = null; + private ?string $plainPassword = null; #[ORM\Column] private ?bool $is_active = false; @@ -214,6 +216,18 @@ public function setPassword(string $password): static return $this; } + public function getPlainPassword(): ?string + { + return $this->plainPassword; + } + + public function setPlainPassword(?string $plainPassword): self + { + $this->plainPassword = $plainPassword; + + return $this; + } + public function getBirthdate(): ?\DateTimeInterface { return $this->birthdate; @@ -386,7 +400,7 @@ public function getUserIdentifier(): string public function eraseCredentials(): void { // If you store any temporary, sensitive data on the user, clear it here - // $this->plainPassword = null; + $this->plainPassword = null; } public function getIsActive(): ?bool diff --git a/back/api/src/State/UserPasswordHasher.php b/back/api/src/State/UserPasswordHasher.php index d34149c9..05cb7218 100644 --- a/back/api/src/State/UserPasswordHasher.php +++ b/back/api/src/State/UserPasswordHasher.php @@ -18,19 +18,17 @@ public function __construct(private readonly ProcessorInterface $processor, priv */ public function process($data, Operation $operation, array $uriVariables = [], array $context = []) { - if (!$data->getPassword()) { + if (!$data->getPlainPassword()) { return $this->processor->process($data, $operation, $uriVariables, $context); } + $hashedPassword = $this->passwordHasher->hashPassword( $data, - $data->getPassword() + $data->getPlainPassword() ); $data->setPassword($hashedPassword); $data->eraseCredentials(); - -// $email = new Email(); -// $email->sendEmail("maxime.pietrucci@gmail.com", $data->getEmail(), 'Welcome to Resend', 'Welcome to Resend'); return $this->processor->process($data, $operation, $uriVariables, $context); } } diff --git a/front/next.config.js b/front/next.config.js index 767719fc..6547d431 100644 --- a/front/next.config.js +++ b/front/next.config.js @@ -1,4 +1,10 @@ /** @type {import('next').NextConfig} */ -const nextConfig = {} +const nextConfig = { + async rewrites() { + return { + fallback: [{source: '/:path*', destination: '/user/:path*'}] + } + }, +} module.exports = nextConfig diff --git a/front/src/components/ChooseLayout.jsx b/front/src/components/ChooseLayout.jsx new file mode 100644 index 00000000..e98ad8b5 --- /dev/null +++ b/front/src/components/ChooseLayout.jsx @@ -0,0 +1,88 @@ +import {useAuthContext} from "@/providers/AuthProvider"; +import {useRouter} from "next/router"; +import AdminLayout from "@/layouts/AdminLayout"; +import UserLayout from "@/layouts/UserLayout"; +import ProviderLayout from "@/layouts/ProviderLayout"; +import DefaultLayout from "@/layouts/DefaultLayout"; +import Sidebar from "@/components/Sidebar"; +import Header from "@/components/Header"; +import {Flowbite} from "flowbite-react"; +import {useEffect} from "react"; + +const canAccessTo = (path, roles) => { + const lowerCaseRoles = roles.join(',').toLowerCase() ?? "" + if (path.startsWith('/admin')) { + return lowerCaseRoles.includes('admin'); + } + else if (path.startsWith('/provider')) { + return lowerCaseRoles.includes('provider') || lowerCaseRoles.includes('admin'); + } + else if (path.startsWith('/user')) { + return lowerCaseRoles.includes('user') || lowerCaseRoles.includes('admin') || lowerCaseRoles.includes('provider'); + } + return true +}; + +const ChooseLayout = ({children}) => { + const {user, isLogged} = useAuthContext() + const router = useRouter() + const path = router.pathname + const needsAuth = path.startsWith('/admin') || path.startsWith('/provider') || path.startsWith('/user'); + useEffect(() => { + if (user !== undefined && needsAuth && !isLogged) { + router.push('/auth/login'); + } + if (user !== undefined && needsAuth && !canAccessTo(path, user?.roles)) { + router.push('/auth/login'); + } + }, [user, path]); + + let Layout; + + if (path.startsWith('/admin')) { + Layout = AdminLayout + } else if (path.startsWith('/provider')) { + Layout = ProviderLayout + } else if (path.startsWith('/user')) { + Layout = UserLayout + } else { + Layout = DefaultLayout + } + if (!needsAuth) { + return ( + <> + +
+
+
+
+
+ + + {children} + +
+
+
+ + ); + } + return ( + <> + {isLogged && +
+
+
+
+
+ + + {children} + +
+
+
} + + ) +} +export default ChooseLayout \ No newline at end of file diff --git a/front/src/components/Header.jsx b/front/src/components/Header.jsx new file mode 100644 index 00000000..4894e280 --- /dev/null +++ b/front/src/components/Header.jsx @@ -0,0 +1,58 @@ +import {Avatar, DarkThemeToggle, Dropdown, Navbar} from "flowbite-react"; +import Image from "next/image"; +import {useAuthContext} from "@/providers/AuthProvider"; + +export default function Header() { + const {user, isLogged} = useAuthContext(); + return ( +
+ + + Flowbite logo + Flowbite + + +
+ + { user && + } + > + + {user.lastname} {user.firstname} + {user.email} + + Dashboard + Settings + Earnings + + Sign out + } + +
+ + + + Home + + About + Services + Pricing + Contact + +
+
+ ) +} diff --git a/front/src/components/Sidebar.jsx b/front/src/components/Sidebar.jsx new file mode 100644 index 00000000..64d4e96a --- /dev/null +++ b/front/src/components/Sidebar.jsx @@ -0,0 +1,40 @@ +import {useState} from "react"; +import {Button, DarkThemeToggle} from "flowbite-react"; +import {HiArrowSmRight, HiChartPie, HiInbox, HiShoppingBag, HiTable, HiUser, HiViewBoards} from "react-icons/hi"; +import {Sidebar as FlowbiteSidebar} from "flowbite-react"; +import {BiBuoy} from "react-icons/bi"; + +const Sidebar = function ({content = []}) { + const [isOpen, setOpen] = useState(false); + + const toggle = () => { + setOpen(!isOpen); + } + + return ( + + + + {content.map((element, index)=>( + + {element.text} + + ))} + + + + Upgrade to Pro + + + Documentation + + + Help + + + + + ); +}; + +export default Sidebar; diff --git a/front/src/hooks/useAuth.js b/front/src/hooks/useAuth.js index b50cf432..77cf825e 100644 --- a/front/src/hooks/useAuth.js +++ b/front/src/hooks/useAuth.js @@ -6,11 +6,13 @@ export const useAuth = () => { const login = async (credentials) => { try { + sessionStorage.removeItem('token'); + sessionStorage.removeItem('refreshToken'); const response = await loginService(credentials); const token = response.token const refreshToken = response.refresh_token - localStorage.setItem('token', token) - localStorage.setItem('refreshToken', refreshToken) + sessionStorage.setItem('token', token, {expires: 1}); + sessionStorage.setItem('refreshToken', refreshToken, {expires: 7}); const user = await fetchCurrentUser() setIsLogged(true); setUser(user); @@ -24,10 +26,12 @@ export const useAuth = () => { await registerService(payload); }; - const logout = async () => { - // Logique de déconnexion + const logout = () => { + sessionStorage.removeItem('user') setIsLogged(false); setUser(null); + sessionStorage.removeItem("token") + sessionStorage.removeItem("refreshToken") }; return {login, register, logout}; diff --git a/front/src/hooks/useRequestsProvider.js b/front/src/hooks/useRequestsProvider.js index 2e30a59f..e5d51abb 100644 --- a/front/src/hooks/useRequestsProvider.js +++ b/front/src/hooks/useRequestsProvider.js @@ -13,7 +13,6 @@ export default function useRequestsProvider() { } const getListOfRequests = async () =>{ - const response = await getListOfRequestsService() setRequests(response["hydra:member"]) } @@ -21,7 +20,6 @@ export default function useRequestsProvider() { const getRequest = async (payload) =>{ const response = await getRequestService(payload) setRequest(response) - console.log(response) } const approveRequest = async (payload) =>{ diff --git a/front/src/hooks/useResetPassword.js b/front/src/hooks/useResetPassword.js index f651836a..e7892633 100644 --- a/front/src/hooks/useResetPassword.js +++ b/front/src/hooks/useResetPassword.js @@ -22,12 +22,10 @@ export function useResetPassword() { async function checkResetToken(token) { setIsLoading(true); try { - const response = await checkResetTokenRequest(token) - console.log("re", response) + await checkResetTokenRequest(token) setIsLoading(false); setIsTokenValid(true); } catch (err) { - createToast('error', 'Error recuperation token') setIsLoading(false); setIsTokenValid(false); } @@ -36,8 +34,7 @@ export function useResetPassword() { async function updatePassword(token, newPassword) { setIsLoading(true); try { - const response = await updatePasswordRequest(token, newPassword) - createToast('success', 'Vous avez bien modifié votre password') + await updatePasswordRequest(token, newPassword) return true } catch (err) { return false diff --git a/front/src/layouts/AdminLayout.jsx b/front/src/layouts/AdminLayout.jsx new file mode 100644 index 00000000..1f6fc31a --- /dev/null +++ b/front/src/layouts/AdminLayout.jsx @@ -0,0 +1,24 @@ +import Sidebar from "@/components/Sidebar"; +import {HiArrowSmRight, HiChartPie, HiInbox, HiShoppingBag, HiTable, HiUser, HiViewBoards} from "react-icons/hi"; + +const AdminLayout = ({children}) => { + + const sidebarContent = [ + {icon: HiChartPie, text: "Dashboard", href: "/admin", label: "Pro", labelColor: "gray"}, + {icon: HiViewBoards, text: "Requests", href: "/admin/requests", label: "Pro", labelColor: "gray"}, + {icon: HiInbox, text: "Establishment", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiShoppingBag, text: "Services", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiUser, text: "Users", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiTable, text: "Reviews", href: "#", label: "Pro", labelColor: "gray"} + ] + return ( + <> + + +
+ {children} +
+ + ) +} +export default AdminLayout \ No newline at end of file diff --git a/front/src/layouts/DefaultLayout.jsx b/front/src/layouts/DefaultLayout.jsx new file mode 100644 index 00000000..69ae80a4 --- /dev/null +++ b/front/src/layouts/DefaultLayout.jsx @@ -0,0 +1,13 @@ +import Sidebar from "@/components/Sidebar"; + +const DefaultLayout = ({children}) => { + return ( + <> +
+ {children} +
+ + ) +} + +export default DefaultLayout \ No newline at end of file diff --git a/front/src/layouts/ProviderLayout.jsx b/front/src/layouts/ProviderLayout.jsx new file mode 100644 index 00000000..a3965717 --- /dev/null +++ b/front/src/layouts/ProviderLayout.jsx @@ -0,0 +1,20 @@ +import Sidebar from "@/components/Sidebar"; +import {HiChartPie, HiInbox, HiShoppingBag, HiTable, HiUser, HiViewBoards} from "react-icons/hi"; + +const sidebarContent = [ + {icon: HiChartPie, text: "Dashboard", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiInbox, text: "My Establishment", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiShoppingBag, text: "My Services", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiTable, text: "My Reviews", href: "#", label: "Pro", labelColor: "gray"} +] +const ProviderLayout = ({children}) => { + return ( + <> + +
+ {children} +
+ + ) +} +export default ProviderLayout; \ No newline at end of file diff --git a/front/src/layouts/UserLayout.jsx b/front/src/layouts/UserLayout.jsx new file mode 100644 index 00000000..6fe5d74b --- /dev/null +++ b/front/src/layouts/UserLayout.jsx @@ -0,0 +1,22 @@ +import Sidebar from "@/components/Sidebar"; +import {HiChartPie, HiInbox, HiShoppingBag, HiTable, HiUser, HiViewBoards} from "react-icons/hi"; + +const UserLayout = ({children}) => { + const sidebarContent = [ + {icon: HiChartPie, text: "Dashboard", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiViewBoards, text: "Requests", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiInbox, text: "Establishment", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiShoppingBag, text: "Services", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiUser, text: "Users", href: "#", label: "Pro", labelColor: "gray"}, + {icon: HiTable, text: "Reviews", href: "#", label: "Pro", labelColor: "gray"} + ] + return ( + <> + +
+ {children} +
+ + ) +} +export default UserLayout; \ No newline at end of file diff --git a/front/src/pages/_app.js b/front/src/pages/_app.js index d67bed29..980ab44e 100644 --- a/front/src/pages/_app.js +++ b/front/src/pages/_app.js @@ -1,16 +1,20 @@ -import {AuthProvider} from "@/providers/AuthProvider"; +import {AuthProvider, useAuthContext} from "@/providers/AuthProvider"; import '@/styles/globals.css' import {ToastProvider} from "@/providers/ToastProvider"; +import DefaultLayout from "@/layouts/DefaultLayout"; +import {useEffect} from "react"; +import ChooseLayout from "@/components/ChooseLayout"; function MyApp({Component, pageProps}) { - return ( <> - - + + + - - + + + ); } diff --git a/front/src/pages/provider/[id].jsx b/front/src/pages/admin/requests/[id].jsx similarity index 96% rename from front/src/pages/provider/[id].jsx rename to front/src/pages/admin/requests/[id].jsx index 9d05a51f..33148c04 100644 --- a/front/src/pages/provider/[id].jsx +++ b/front/src/pages/admin/requests/[id].jsx @@ -15,9 +15,6 @@ export default function Request() { getRequest({id}) }, []); - useEffect(() => { - console.log("request", request) - }, [request]); const handleApproveRequest = () => { try { diff --git a/front/src/pages/provider/index.js b/front/src/pages/admin/requests/index.js similarity index 92% rename from front/src/pages/provider/index.js rename to front/src/pages/admin/requests/index.js index 69f625d0..036dea36 100644 --- a/front/src/pages/provider/index.js +++ b/front/src/pages/admin/requests/index.js @@ -26,7 +26,7 @@ export default function Index() { {request.status} {request.createdAt} - + Voir en detail diff --git a/front/src/pages/auth/login.jsx b/front/src/pages/auth/login.jsx index ded4d43b..29b4fa2c 100644 --- a/front/src/pages/auth/login.jsx +++ b/front/src/pages/auth/login.jsx @@ -45,8 +45,8 @@ export default function Login() { useEffect(() => { const goToProfilePage = async () => { - if (isLogged === true && user !== null) { - await router.push("/auth/profile"); + if (isLogged === true && user) { + await router.push("/profile"); } }; diff --git a/front/src/pages/auth/logout.jsx b/front/src/pages/auth/logout.jsx index 41949e67..d451f81a 100644 --- a/front/src/pages/auth/logout.jsx +++ b/front/src/pages/auth/logout.jsx @@ -1,19 +1,15 @@ +"use client" import {useEffect} from "react"; import {useRouter} from "next/router"; +import {useAuth} from "@/hooks/useAuth"; export default function Logout() { const router = useRouter(); - + const {logout} = useAuth() useEffect(() => { - localStorage.removeItem('token') - localStorage.removeItem('refreshToken') + logout() + router.push('/') }, []); - router.push('/') - return ( - <> -
profile role
- - ) } diff --git a/front/src/pages/index.jsx b/front/src/pages/index.jsx index e6152c12..600ceca1 100644 --- a/front/src/pages/index.jsx +++ b/front/src/pages/index.jsx @@ -1,6 +1,8 @@ import Link from "next/link"; +import Cookie from "js-cookie"; export default function Index(){ + return ( <>
@@ -8,6 +10,7 @@ export default function Index(){
Login +

Register diff --git a/front/src/pages/provider/index.jsx b/front/src/pages/provider/index.jsx new file mode 100644 index 00000000..d9003569 --- /dev/null +++ b/front/src/pages/provider/index.jsx @@ -0,0 +1,7 @@ +export default function Provider() { + return ( + <> +

Provider

+ + ) +} \ No newline at end of file diff --git a/front/src/pages/reset-password/update/[token].jsx b/front/src/pages/reset-password/update/[token].jsx index d84177f5..081299d3 100644 --- a/front/src/pages/reset-password/update/[token].jsx +++ b/front/src/pages/reset-password/update/[token].jsx @@ -5,6 +5,7 @@ import PasswordResetForm from "@/components/PasswordResetForm"; import {useToast} from "@/hooks/useToast"; export default function UpdatePassword() { + console.log("ok") const router = useRouter(); const {token} = router.query; const { createToastMessage } = useToast(); diff --git a/front/src/pages/provider/apply.jsx b/front/src/pages/user/apply-to-be-provider.jsx similarity index 97% rename from front/src/pages/provider/apply.jsx rename to front/src/pages/user/apply-to-be-provider.jsx index 6c914047..473eb451 100644 --- a/front/src/pages/provider/apply.jsx +++ b/front/src/pages/user/apply-to-be-provider.jsx @@ -7,7 +7,7 @@ import useRequestsProvider from "@/hooks/useRequestsProvider"; import {useToast} from "@/hooks/useToast"; -export default function Apply() { +export default function ApplyToBeProvider() { const {applyToBeProvider} = useRequestsProvider() const {createToastMessage} = useToast() const [kbis, setKbis] = useState("") diff --git a/front/src/pages/auth/profile.jsx b/front/src/pages/user/profile.jsx similarity index 83% rename from front/src/pages/auth/profile.jsx rename to front/src/pages/user/profile.jsx index 25fe63d0..45e9e6ef 100644 --- a/front/src/pages/auth/profile.jsx +++ b/front/src/pages/user/profile.jsx @@ -6,12 +6,21 @@ import {Button as FlowbiteButton} from 'flowbite-react'; import PasswordResetForm from "@/components/PasswordResetForm"; import useUserAccount from "@/hooks/useUserAccount"; import Link from "next/link"; +import {useRouter} from "next/router"; export default function Profile() { - const { user, isLogged } = useAuthContext(); - const { userProfile, updateProfile, loading } = useUserAccount(user.id); + const { user, verifyUser, fetchUser } = useAuthContext(); + const { userProfile, updateProfile, loading } = useUserAccount(user?.id); const [formData, setFormData] = useState({ ...userProfile }); + useEffect(() => { + if (!user) { + verifyUser(); + } else { + setFormData({ ...userProfile }); + } + }, [user, userProfile, verifyUser]); + const handleFirstNameChange = (value) => { setFormData({...formData, firstname: value}) } @@ -25,6 +34,7 @@ export default function Profile() { const handleProfileUpdateSubmit = async (event) => { event.preventDefault() await updateProfile(formData); + fetchUser() } const handlePasswordResetSubmit = async (password) => { @@ -35,14 +45,13 @@ export default function Profile() { setFormData({ ...userProfile }); }, [userProfile]); - // TODO - // Add history for payments etc... - - + if (!user) { + return
Chargement...
; + } return ( <> Logout -
profile role {user?.roles}
+

profile role {user?.roles}

{user ? ( <>
@@ -73,7 +82,7 @@ export default function Profile() {
Chargement
)} - + Faire une demande pour devenir prestataire diff --git a/front/src/providers/AuthProvider.jsx b/front/src/providers/AuthProvider.jsx index 9e2c539f..dd7284f5 100644 --- a/front/src/providers/AuthProvider.jsx +++ b/front/src/providers/AuthProvider.jsx @@ -1,30 +1,51 @@ -import httpClient from "@/services/httpClient"; -import {fetchCurrentUser, loginService, registerService} from "@/services/authService"; +import { + fetchCurrentUser, + getUserFromSession, + loginService, + registerService, + storeUserInSession +} from "@/services/authService"; -import { useState, useEffect, createContext, useContext } from 'react'; +import {useState, useEffect, createContext, useContext} from 'react'; const AuthContext = createContext(null); -export const AuthProvider = ({ children }) => { - const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - const [isLogged, setIsLogged] = useState(false); - - useEffect(() => { - verifyUser(); - }, []); +export const AuthProvider = ({children}) => { + const [user, setUser] = useState(undefined); + const [isLogged, setIsLogged] = useState(undefined); const verifyUser = async () => { - try { - const user = await fetchCurrentUser() - setUser(user); - } catch (error) { - console.error('Erreur lors de la vérification', error); + console.log("called") + let storedUser = getUserFromSession(); + if (!storedUser) { + console.log("cc") + try { + await fetchUser() + } catch (error) { + console.error('Erreur lors de la vérification', error); + setUser(null) + setIsLogged(false) + } + } else { + setUser(storedUser); + setIsLogged(true); } - setLoading(false); }; + const fetchUser = async () => { + try { + const fetchedUser = await fetchCurrentUser(); + setUser(fetchedUser); + setIsLogged(true); + storeUserInSession(fetchedUser); + } catch { + throw "error recuperation user" + } + } + useEffect(() => { + verifyUser(); + }, []); return ( - - {!loading && children} + + {children} ); }; diff --git a/front/src/services/authService.js b/front/src/services/authService.js index 8309ecc8..0d96943a 100644 --- a/front/src/services/authService.js +++ b/front/src/services/authService.js @@ -1,4 +1,6 @@ import httpClient from "./httpClient"; +import Cookie from "js-cookie"; + const loginService = async ({email, password}) => { return await httpClient.post('auth/login', {email, password}); }; @@ -8,12 +10,34 @@ const registerService = async ({lastname, firstname, email, password}) => { }; const fetchCurrentUser = async () => { - if (localStorage.getItem('token')) return await httpClient.get('auth/me') + return await httpClient.get('auth/me'); }; const refreshToken = async (refreshToken) => { return await httpClient.post('/token/refresh', {refresh_token: refreshToken}) }; +const getUserFromSession = () => { + const userStr = sessionStorage.getItem('user'); + if (!userStr) return null; + + const userWithExpiry = JSON.parse(userStr); + const now = new Date(); + + if (now.getTime() > userWithExpiry.expiresAt) { + sessionStorage.removeItem('user'); + return null; + } + return userWithExpiry; +}; +const storeUserInSession = (user) => { + if (user) { + const now = new Date(); + const userWithExpiry = { + ...user, expiresAt: now.getTime() + 120000, + }; + sessionStorage.setItem('user', JSON.stringify(userWithExpiry)); + } +}; -export {loginService, registerService, fetchCurrentUser, refreshToken}; \ No newline at end of file +export {loginService, registerService, fetchCurrentUser, refreshToken, storeUserInSession, getUserFromSession}; \ No newline at end of file diff --git a/front/src/services/httpClient.js b/front/src/services/httpClient.js index 8f30f5a1..a1f80cbd 100644 --- a/front/src/services/httpClient.js +++ b/front/src/services/httpClient.js @@ -1,4 +1,5 @@ import axios from 'axios' +import Cookie from 'js-cookie'; const httpClient = axios.create({ @@ -12,7 +13,7 @@ const httpClient = axios.create({ httpClient.interceptors.request.use( (config) => { - const jwtToken = localStorage.getItem('token') ?? false; + const jwtToken = sessionStorage.getItem('token'); if (jwtToken) { config.headers['Authorization'] = `Bearer ${jwtToken}`; } @@ -23,8 +24,10 @@ httpClient.interceptors.request.use( } ); -const handleResponseSucces = (response) => { - return response?.data; +const handleResponseSuccess = (response) => { + const result = response?.data; + + return result } const handleResponseError = async (error) => { @@ -34,7 +37,7 @@ const handleResponseError = async (error) => { if (status === 401 && message === 'Expired JWT Token' && !originalRequest._retry) { console.log('Expired JWT Token'); originalRequest._retry = true; - const refreshToken = localStorage.getItem('refreshToken'); + const refreshToken = ses('refreshToken'); if (!refreshToken) { return Promise.reject(error); } @@ -42,8 +45,8 @@ const handleResponseError = async (error) => { const newJwtToken = response?.data?.token; const newRefreshToken = response?.data?.refresh_token; if (newJwtToken.length > 0 && newRefreshToken.length > 0) { - localStorage.setItem('token', newJwtToken); - localStorage.setItem('refreshToken', newRefreshToken); + sessionStorage.setItem('token', newJwtToken); + sessionStorage.setItem('refreshToken', newRefreshToken); originalRequest.headers['Authorization'] = `Bearer ${newJwtToken}`; return httpClient(originalRequest); } @@ -51,7 +54,7 @@ const handleResponseError = async (error) => { return Promise.reject(error); } -httpClient.interceptors.response.use(handleResponseSucces, handleResponseError); +httpClient.interceptors.response.use(handleResponseSuccess, handleResponseError); const makeRequest = async (method, url, data, config) => { @@ -106,4 +109,5 @@ httpClient.delete = async function (url, config) { return makeRequest('delete', url, null, config); }; + export default httpClient; \ No newline at end of file diff --git a/front/src/styles/globals.css b/front/src/styles/globals.css index 05ca8b1e..c064db44 100644 --- a/front/src/styles/globals.css +++ b/front/src/styles/globals.css @@ -5,3 +5,6 @@ @tailwind utilities; +body { + @apply text-gray-900 dark:text-white +} From 764b40463990a963093b0f6083f0727751ae0367 Mon Sep 17 00:00:00 2001 From: Ossama9 Date: Sun, 19 Nov 2023 23:19:24 +0100 Subject: [PATCH 2/3] fix changes --- .DS_Store | Bin 0 -> 6148 bytes front/package.json | 1 + front/pnpm-lock.yaml | 8 ++++++++ front/src/components/ChooseLayout.jsx | 1 - front/src/components/Header.jsx | 2 +- front/src/components/Sidebar.jsx | 3 +-- front/src/layouts/AdminLayout.jsx | 2 +- front/src/layouts/DefaultLayout.jsx | 2 -- front/src/layouts/ProviderLayout.jsx | 2 +- front/src/pages/_app.js | 4 +--- front/src/pages/auth/login.jsx | 2 +- front/src/pages/index.jsx | 1 - .../src/pages/reset-password/update/[token].jsx | 4 ++-- front/src/pages/user/apply-to-be-provider.jsx | 2 -- front/src/pages/user/profile.jsx | 3 +-- front/src/providers/AuthProvider.jsx | 2 -- front/src/services/authService.js | 1 - front/src/services/httpClient.js | 5 +---- 18 files changed, 19 insertions(+), 26 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..a319bc75430c3d2a4a622ea088713e0901432745 GIT binary patch literal 6148 zcmeH~L2uJA6vv;pjHa6=^#Ia@gcONu8EuIsCN80D2d)Ic0Z^B$1;Ro+HR*bYD&@?^ z_nG)0d>!~d+ndsE<2b6xPqP2E-+NB{QytetB>IzKm#9TVE()u&j_Mj?KbI@EVn!NJ z!E?M$a5*sxD9v1{Sml^be@+jN_Q*2A*wb^YD8_oUbJXKkSAYq}2}KYj6L zaFl+}jk#vIJOM&(_eWRQ?El}cl>Xi7Q)|QQc z*%|J}vb~snA4|Xz`0oVx`{1Fl21YBb>eGQrT>+pDhLxeM{`x~b9)Jc$E3FuT37rbn zslr?_gt<8goeqCs;w!B>orHQa<}pte=7u87#Y1>1oJ642)|P-JFi&7rcUye^@85j? zpHH$qOTZGiQv`&w7w+}3By+Z2Ssb6W9?Ag<8^={zRSGI|9IFN&#ak%K;Pbcv4UAS= RF#@xH1QZ5aSpv68;4f-wtjz!b literal 0 HcmV?d00001 diff --git a/front/package.json b/front/package.json index cf1173b6..f632c91d 100644 --- a/front/package.json +++ b/front/package.json @@ -18,6 +18,7 @@ "axios": "^1.5.1", "flowbite": "^2.0.0", "flowbite-react": "^0.6.4", + "js-cookie": "^3.0.5", "next": "14.0.2", "react": "^18", "react-dom": "^18", diff --git a/front/pnpm-lock.yaml b/front/pnpm-lock.yaml index dcf91453..c9fd76d0 100644 --- a/front/pnpm-lock.yaml +++ b/front/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: flowbite-react: specifier: ^0.6.4 version: 0.6.4(react-dom@18.2.0)(react@18.2.0)(tailwindcss@3.3.5) + js-cookie: + specifier: ^3.0.5 + version: 3.0.5 next: specifier: 14.0.2 version: 14.0.2(@babel/core@7.23.2)(react-dom@18.2.0)(react@18.2.0) @@ -3459,6 +3462,11 @@ packages: nopt: 6.0.0 dev: false + /js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + dev: false + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} diff --git a/front/src/components/ChooseLayout.jsx b/front/src/components/ChooseLayout.jsx index e98ad8b5..7f8683e6 100644 --- a/front/src/components/ChooseLayout.jsx +++ b/front/src/components/ChooseLayout.jsx @@ -4,7 +4,6 @@ import AdminLayout from "@/layouts/AdminLayout"; import UserLayout from "@/layouts/UserLayout"; import ProviderLayout from "@/layouts/ProviderLayout"; import DefaultLayout from "@/layouts/DefaultLayout"; -import Sidebar from "@/components/Sidebar"; import Header from "@/components/Header"; import {Flowbite} from "flowbite-react"; import {useEffect} from "react"; diff --git a/front/src/components/Header.jsx b/front/src/components/Header.jsx index 4894e280..b7225f5b 100644 --- a/front/src/components/Header.jsx +++ b/front/src/components/Header.jsx @@ -3,7 +3,7 @@ import Image from "next/image"; import {useAuthContext} from "@/providers/AuthProvider"; export default function Header() { - const {user, isLogged} = useAuthContext(); + const {user} = useAuthContext(); return (
diff --git a/front/src/components/Sidebar.jsx b/front/src/components/Sidebar.jsx index 64d4e96a..88df2164 100644 --- a/front/src/components/Sidebar.jsx +++ b/front/src/components/Sidebar.jsx @@ -1,6 +1,5 @@ import {useState} from "react"; -import {Button, DarkThemeToggle} from "flowbite-react"; -import {HiArrowSmRight, HiChartPie, HiInbox, HiShoppingBag, HiTable, HiUser, HiViewBoards} from "react-icons/hi"; +import {HiChartPie, HiViewBoards} from "react-icons/hi"; import {Sidebar as FlowbiteSidebar} from "flowbite-react"; import {BiBuoy} from "react-icons/bi"; diff --git a/front/src/layouts/AdminLayout.jsx b/front/src/layouts/AdminLayout.jsx index 1f6fc31a..be178ea0 100644 --- a/front/src/layouts/AdminLayout.jsx +++ b/front/src/layouts/AdminLayout.jsx @@ -1,5 +1,5 @@ import Sidebar from "@/components/Sidebar"; -import {HiArrowSmRight, HiChartPie, HiInbox, HiShoppingBag, HiTable, HiUser, HiViewBoards} from "react-icons/hi"; +import {HiChartPie, HiInbox, HiShoppingBag, HiTable, HiUser, HiViewBoards} from "react-icons/hi"; const AdminLayout = ({children}) => { diff --git a/front/src/layouts/DefaultLayout.jsx b/front/src/layouts/DefaultLayout.jsx index 69ae80a4..a94a8a29 100644 --- a/front/src/layouts/DefaultLayout.jsx +++ b/front/src/layouts/DefaultLayout.jsx @@ -1,5 +1,3 @@ -import Sidebar from "@/components/Sidebar"; - const DefaultLayout = ({children}) => { return ( <> diff --git a/front/src/layouts/ProviderLayout.jsx b/front/src/layouts/ProviderLayout.jsx index a3965717..c0513b1d 100644 --- a/front/src/layouts/ProviderLayout.jsx +++ b/front/src/layouts/ProviderLayout.jsx @@ -1,5 +1,5 @@ import Sidebar from "@/components/Sidebar"; -import {HiChartPie, HiInbox, HiShoppingBag, HiTable, HiUser, HiViewBoards} from "react-icons/hi"; +import {HiChartPie, HiInbox, HiShoppingBag, HiTable} from "react-icons/hi"; const sidebarContent = [ {icon: HiChartPie, text: "Dashboard", href: "#", label: "Pro", labelColor: "gray"}, diff --git a/front/src/pages/_app.js b/front/src/pages/_app.js index 980ab44e..081963eb 100644 --- a/front/src/pages/_app.js +++ b/front/src/pages/_app.js @@ -1,8 +1,6 @@ -import {AuthProvider, useAuthContext} from "@/providers/AuthProvider"; +import {AuthProvider} from "@/providers/AuthProvider"; import '@/styles/globals.css' import {ToastProvider} from "@/providers/ToastProvider"; -import DefaultLayout from "@/layouts/DefaultLayout"; -import {useEffect} from "react"; import ChooseLayout from "@/components/ChooseLayout"; function MyApp({Component, pageProps}) { diff --git a/front/src/pages/auth/login.jsx b/front/src/pages/auth/login.jsx index 29b4fa2c..0260f647 100644 --- a/front/src/pages/auth/login.jsx +++ b/front/src/pages/auth/login.jsx @@ -11,7 +11,7 @@ import {useToast} from "@/hooks/useToast"; export default function Login() { const {createToastMessage} = useToast(); const {user, isLogged} = useAuthContext(); - const {login, logout, register} = useAuth(); + const {login} = useAuth(); const [formData, setFormData] = useState({ firstname: "email@email.com", diff --git a/front/src/pages/index.jsx b/front/src/pages/index.jsx index 600ceca1..b3955ede 100644 --- a/front/src/pages/index.jsx +++ b/front/src/pages/index.jsx @@ -1,5 +1,4 @@ import Link from "next/link"; -import Cookie from "js-cookie"; export default function Index(){ diff --git a/front/src/pages/reset-password/update/[token].jsx b/front/src/pages/reset-password/update/[token].jsx index 081299d3..95c7e1c7 100644 --- a/front/src/pages/reset-password/update/[token].jsx +++ b/front/src/pages/reset-password/update/[token].jsx @@ -1,5 +1,5 @@ import {useRouter} from 'next/router'; -import {useEffect, useState} from "react"; +import {useEffect} from "react"; import {useResetPassword} from "@/hooks/useResetPassword"; import PasswordResetForm from "@/components/PasswordResetForm"; import {useToast} from "@/hooks/useToast"; @@ -10,7 +10,7 @@ export default function UpdatePassword() { const {token} = router.query; const { createToastMessage } = useToast(); - const {isTokenValid, isLoading, checkResetToken, updatePassword} = useResetPassword() + const {isTokenValid, checkResetToken, updatePassword} = useResetPassword() useEffect(() => { if (token) { diff --git a/front/src/pages/user/apply-to-be-provider.jsx b/front/src/pages/user/apply-to-be-provider.jsx index 473eb451..c0a95e0c 100644 --- a/front/src/pages/user/apply-to-be-provider.jsx +++ b/front/src/pages/user/apply-to-be-provider.jsx @@ -2,7 +2,6 @@ import {FileInput, Label} from "flowbite-react"; import Input from "@/components/Input"; import {useState} from "react"; import GenericButton from "@/components/GenericButton"; -import httpClient from "@/services/httpClient"; import useRequestsProvider from "@/hooks/useRequestsProvider"; import {useToast} from "@/hooks/useToast"; @@ -12,7 +11,6 @@ export default function ApplyToBeProvider() { const {createToastMessage} = useToast() const [kbis, setKbis] = useState("") const [file, setFile] = useState(null); - const [isLoading, setIsLoading] = useState(false); const handleFileChange = (e) => { const file = e.target.files[0]; // Récupérer le premier fichier setFile(file); // Mettre à jour l'état avec ce fichier diff --git a/front/src/pages/user/profile.jsx b/front/src/pages/user/profile.jsx index 45e9e6ef..9cd31c38 100644 --- a/front/src/pages/user/profile.jsx +++ b/front/src/pages/user/profile.jsx @@ -6,11 +6,10 @@ import {Button as FlowbiteButton} from 'flowbite-react'; import PasswordResetForm from "@/components/PasswordResetForm"; import useUserAccount from "@/hooks/useUserAccount"; import Link from "next/link"; -import {useRouter} from "next/router"; export default function Profile() { const { user, verifyUser, fetchUser } = useAuthContext(); - const { userProfile, updateProfile, loading } = useUserAccount(user?.id); + const { userProfile, updateProfile } = useUserAccount(user?.id); const [formData, setFormData] = useState({ ...userProfile }); useEffect(() => { diff --git a/front/src/providers/AuthProvider.jsx b/front/src/providers/AuthProvider.jsx index dd7284f5..12e0a338 100644 --- a/front/src/providers/AuthProvider.jsx +++ b/front/src/providers/AuthProvider.jsx @@ -1,8 +1,6 @@ import { fetchCurrentUser, getUserFromSession, - loginService, - registerService, storeUserInSession } from "@/services/authService"; diff --git a/front/src/services/authService.js b/front/src/services/authService.js index 0d96943a..477df953 100644 --- a/front/src/services/authService.js +++ b/front/src/services/authService.js @@ -1,5 +1,4 @@ import httpClient from "./httpClient"; -import Cookie from "js-cookie"; const loginService = async ({email, password}) => { return await httpClient.post('auth/login', {email, password}); diff --git a/front/src/services/httpClient.js b/front/src/services/httpClient.js index a1f80cbd..5aa48f97 100644 --- a/front/src/services/httpClient.js +++ b/front/src/services/httpClient.js @@ -1,5 +1,4 @@ import axios from 'axios' -import Cookie from 'js-cookie'; const httpClient = axios.create({ @@ -25,9 +24,7 @@ httpClient.interceptors.request.use( ); const handleResponseSuccess = (response) => { - const result = response?.data; - - return result + return response?.data } const handleResponseError = async (error) => { From 887a72027b1fa5b5ac7d790f861ba05120b4cfe0 Mon Sep 17 00:00:00 2001 From: Ossama9 Date: Sun, 19 Nov 2023 23:22:47 +0100 Subject: [PATCH 3/3] add changelog for project --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..055b8a33 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +| Feature Name | Author | Link PR | +|-------------------------------------------------------------------------------|--------------|---------| +| Separer le front par role & rajouter la securité & rajouter layout par niveau | Ossama DAHBI | https://github.com/Pietrucci-Blacher/Challenge-Semestriel-1-5IW/pull/152 |