@@ -88,7 +89,7 @@ export default function LoginCom() {
{/* {login.mutate(fields)}} disabled={login.isPending} className='px-3 py-2 bg-purple-800 text-white font-bold rounded'>{login.isPending ? 'loading...' : 'Login'} */}
- SIGN IN
+ {loading ? 'Loading...' : 'SIGN IN'}
@@ -107,7 +108,6 @@ export default function LoginCom() {
- }
>
)
}
diff --git a/src/components/breadcrumb/BreadcrumbCom.jsx b/src/components/breadcrumb/BreadcrumbCom.jsx
new file mode 100644
index 0000000..16191c0
--- /dev/null
+++ b/src/components/breadcrumb/BreadcrumbCom.jsx
@@ -0,0 +1,19 @@
+import { MdKeyboardDoubleArrowRight } from 'react-icons/md'
+import { TiHomeOutline } from 'react-icons/ti'
+
+export default function BreadcrumbCom({title, paths}) {
+ return (
+
+
{title}
+
+
+ {paths.map((item, index) => (
+
+ ))}
+
+
+ )
+}
diff --git a/src/components/layouts/DashboardHeader.jsx b/src/components/layouts/DashboardHeader.jsx
new file mode 100644
index 0000000..344cc20
--- /dev/null
+++ b/src/components/layouts/DashboardHeader.jsx
@@ -0,0 +1,83 @@
+import { LuSunDim } from "react-icons/lu";
+import { IoMdSunny } from "react-icons/io";
+
+import { generalLayoutContext } from "../../context/GeneralLayoutContext"
+
+import UserAvatar from '../../assets/user_avatar.jpg'
+import HandBurger from "./HandBurger"
+import { Link } from "react-router-dom"
+import RouteLinks from "../../RouteLinks"
+
+export default function DashboardHeader({showAsideDrawer, setShowAsideDrawer}) {
+ const {theme, handleTheme, handleDrawer, setLogoutModal} = generalLayoutContext()
+
+ return (
+ <>
+ {/* HEADER SECTION*/}
+
+ >
+ )
+}
diff --git a/src/components/layouts/DashboardLayout.jsx b/src/components/layouts/DashboardLayout.jsx
new file mode 100644
index 0000000..09481ca
--- /dev/null
+++ b/src/components/layouts/DashboardLayout.jsx
@@ -0,0 +1,64 @@
+import { useEffect, useState } from 'react'
+import { Outlet } from 'react-router-dom'
+import { FaArrowRight, FaArrowLeft } from "react-icons/fa6";
+
+import DashboardAside from './aside/DashboardAside'
+import DashboardHeader from './DashboardHeader'
+import { generalLayoutContext } from '../../context/GeneralLayoutContext'
+
+export default function DashboardLayout() {
+ const [shrinkAside, setShrinkAside] = useState(false)
+
+ const [showAsideDrawer, setShowAsideDrawer] = useState(false)
+
+ useEffect(()=>{
+ window.addEventListener('resize', ()=>{
+ setShrinkAside(false)
+ setShowAsideDrawer(false)
+ })
+ },[])
+ return (
+
+
+
+
+ setShrinkAside(prev => !prev)}
+ >
+ {shrinkAside ? : }
+
+
+
+
+
+
setShowAsideDrawer(prev => !prev)} className={`${showAsideDrawer ? 'left-0' : '-left-96'} w-72 lg:hidden fixed inset-0 z-[999] bg-black text-white-light`}>
+
+ setShowAsideDrawer(prev => !prev)}
+ >
+
+
+
+
+
+ {/* HEADER SECTION generalLayoutContext*/}
+
+
+ {/* BODY SECTION */}
+ {/* main takes the full width minus that of the header and footer 72 for header, 39 for footer total 111 */}
+
+
+
+
+ {/* FOOTER SECTION */}
+
+
+
+ )
+}
diff --git a/src/components/layouts/HandBurger.jsx b/src/components/layouts/HandBurger.jsx
new file mode 100644
index 0000000..68e6349
--- /dev/null
+++ b/src/components/layouts/HandBurger.jsx
@@ -0,0 +1,36 @@
+
+export default function HandBurger({showAside, asideDisplay, barColor}) {
+ return (
+
+ )
+}
diff --git a/src/components/layouts/LogoutModal.jsx b/src/components/layouts/LogoutModal.jsx
new file mode 100644
index 0000000..06c5f7b
--- /dev/null
+++ b/src/components/layouts/LogoutModal.jsx
@@ -0,0 +1,52 @@
+import { useNavigate } from "react-router-dom"
+import MainBtn from "../MainBtn"
+import ModalWrapper from "../modals/ModalWrapper"
+import RouteLinks from "../../RouteLinks"
+import { updateUserDetails } from "../../store/UserDetails"
+import { useDispatch } from "react-redux"
+
+
+export default function LogoutModal({close}) {
+ const navigate = useNavigate()
+
+ const dispatch = useDispatch()
+
+ const handleLogout = () => {
+ dispatch(updateUserDetails({}))
+ localStorage.clear()
+ navigate(RouteLinks.loginPage, {replace:true})
+ close()
+ // location.reload()
+ }
+ return (
+
+
+ {/* */}
+
+
+ LOGOUT
+
+
+
+
+
+ Close modal
+
+
+ {/* */}
+
+
+
+
+
Are you sure you want to logout?
+
+
+
+
+
+ {/* */}
+
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/layouts/aside/AsideLink.jsx b/src/components/layouts/aside/AsideLink.jsx
new file mode 100644
index 0000000..eee17d9
--- /dev/null
+++ b/src/components/layouts/aside/AsideLink.jsx
@@ -0,0 +1,17 @@
+import { Link, useLocation } from "react-router-dom"
+
+export default function AsideLink({shrinkAside, name, to, icon}) {
+
+ const {pathname} = useLocation()
+
+ return (
+
+ {/* {icon &&
} */}
+ {icon &&
}
+ {shrinkAside ? '' : name}
+
+ )
+}
diff --git a/src/components/layouts/aside/AsideLinkWithSubLinks.jsx b/src/components/layouts/aside/AsideLinkWithSubLinks.jsx
new file mode 100644
index 0000000..aa965c1
--- /dev/null
+++ b/src/components/layouts/aside/AsideLinkWithSubLinks.jsx
@@ -0,0 +1,33 @@
+import { ReactNode } from "react"
+// import { useLocation } from "react-router-dom"
+
+
+export default function AsideLinkWithSubLinks({shrinkAside, name, icon, hideSubMenu, setHideSubMenu, children}) {
+
+ // const btnName = name
+
+// const {pathname} = useLocation()
+
+// const [hideSubMenu, setHideSubMenu] = useState(true)
+
+ return (
+ //
+ // {icon &&
}
+ // {shrinkAside ? '' : name}
+ //
+
+
+ {icon && }{shrinkAside ? '' : name}
+
+
+
+ {children}
+
+
+ )
+}
diff --git a/src/components/layouts/aside/DashboardAside.jsx b/src/components/layouts/aside/DashboardAside.jsx
new file mode 100644
index 0000000..c30ecaa
--- /dev/null
+++ b/src/components/layouts/aside/DashboardAside.jsx
@@ -0,0 +1,96 @@
+import { useState } from "react";
+import {Link, useLocation} from 'react-router-dom'
+import RouteLinks from "../../../RouteLinks";
+import DummyLogo from "../../DummyLogo";
+import MainBtn from "../../MainBtn";
+import AsideLink from "./AsideLink";
+import AsideLinkWithSubLinks from "./AsideLinkWithSubLinks";
+import { useSelector } from "react-redux";
+import { generalLayoutContext } from "../../../context/GeneralLayoutContext";
+
+import { AiOutlineDashboard } from "react-icons/ai";
+import { IoPeople } from "react-icons/io5";
+
+
+export default function DashboardAside({shrinkAside=false}) {
+
+ const {pathname} = useLocation()
+
+ const {setLogoutModal} = generalLayoutContext()
+
+ const {userDetails} = useSelector((state) => state.userDetails) // GETS LOGGED IN USER ROLE DETAILS
+ const {role}= userDetails
+
+ const [hideSubMenu, setHideSubMenu] = useState('')
+
+ const handleHideSubMenu = (e) => {
+ e.stopPropagation()
+ let name = e.target.name
+ setHideSubMenu((prev) => {
+ if(prev == name){
+ return ''
+ }else{
+ return name
+ }
+ })
+ }
+
+ return (
+
+
+
+
+
+
+
+
+
+
{shrinkAside ? '' : 'Dashboard'}
+
+
+
+
Admin
+
+
+ {shrinkAside ? '' : 'Users'}
+
+
+
+ {/*
+
Admin
+
+
+
+
+
+
+
+
+
*/}
+
+ {/*
*/}
+
+
+
+ setLogoutModal(true)}
+ />
+
+
+
+ )
+}
diff --git a/src/components/links/FooterLinks.jsx b/src/components/links/FooterLinks.jsx
new file mode 100644
index 0000000..4f81d93
--- /dev/null
+++ b/src/components/links/FooterLinks.jsx
@@ -0,0 +1,33 @@
+import { Link, useLocation } from "react-router-dom"
+import { layoutDefaultContext } from "../../context/DefaultLayoutContext"
+
+
+export default function FooterLinks({
+ linkName,
+ href,
+}) {
+
+ const {pathname} = useLocation()
+
+ const {subLinkIsActive, setSubLinkIsActive} = layoutDefaultContext() // CONTEXT TO GET WHEN A SUBLINK IS CLICKED
+
+ return (
+
{
+ if(subLinkIsActive){
+ setSubLinkIsActive((prev)=>{
+ if(prev == linkName){
+ return null
+ }else{
+ return linkName
+ }
+ })
+ }
+ }}
+ >
+ {linkName}
+
+ )
+}
diff --git a/src/components/links/NavLinks.jsx b/src/components/links/NavLinks.jsx
new file mode 100644
index 0000000..975047a
--- /dev/null
+++ b/src/components/links/NavLinks.jsx
@@ -0,0 +1,34 @@
+import { Link, useLocation } from "react-router-dom"
+import { layoutDefaultContext } from "../../context/DefaultLayoutContext"
+
+export default function NavLinks({
+ linkName,
+ href,
+}) {
+
+ const {pathname} = useLocation()
+
+ const {subLinkIsActive, setSubLinkIsActive} = layoutDefaultContext() // CONTEXT TO GET WHEN A SUBLINK IS CLICKED
+
+ return (
+
{
+ if(subLinkIsActive){
+ setSubLinkIsActive((prev)=>{
+ // e.stopPropagation()
+ // console.log('bubble')
+ if(prev == linkName){
+ return null
+ }else{
+ return linkName
+ }
+ })
+ }
+ }}
+ >
+ {linkName}
+
+ )
+}
diff --git a/src/components/links/NavLinksWithSubLinks.jsx b/src/components/links/NavLinksWithSubLinks.jsx
new file mode 100644
index 0000000..2b8c4c7
--- /dev/null
+++ b/src/components/links/NavLinksWithSubLinks.jsx
@@ -0,0 +1,109 @@
+// import { useLocation } from "react-router-dom"
+import NavLinks from "./NavLinks"
+import { layoutDefaultContext } from "../../context/DefaultLayoutContext"
+
+
+export default function NavLinksWithSubLinks({
+ linkName,
+ subLink=[],
+ asLink=true,
+}) {
+
+// const {pathname} = useLocation()
+ const {subLinkIsActive, setSubLinkIsActive} = layoutDefaultContext() // CONTEXT TO GET WHEN A SUBLINK IS CLICKED
+
+ return (
+ //
+ //
+ // {linkName}
+ //
+ //
+ // {/* sub links section */}
+ //
+ // {subLink.map(item => (
+ //
+ //
+ //
+ // ))}
+ //
+ //
+
+ <>
+ {asLink ?
+
setSubLinkIsActive((prev)=>{
+ // e.stopPropagation()
+ // console.log('bubble')
+ if(prev == linkName){
+ return null
+ }else{
+ return linkName
+ }
+ })}
+ >
+
+ {linkName}
+
+
+ {/* sub links section */}
+ setSubLinkIsActive((prev)=>{
+ // e.stopPropagation()
+ // console.log('bubble')
+ if(prev == linkName){
+ return null
+ }else{
+ return linkName
+ }
+ })}
+ >
+ {subLink.map(item => (
+
+
+
+ ))}
+
+
+ :
+
setSubLinkIsActive((prev)=>{
+ if(prev == linkName){
+ return null
+ }else{
+ return linkName
+ }
+ })}
+ >
+
+ {linkName}
+
+
+ {/* sub links section */}
+
+ {subLink.map(item => (
+
+ {/* */}
+ coming soon
+
+ ))}
+
+
+ }
+ >
+ )
+}
diff --git a/src/components/modals/DummyModalChildren.jsx b/src/components/modals/DummyModalChildren.jsx
new file mode 100644
index 0000000..9ad15bc
--- /dev/null
+++ b/src/components/modals/DummyModalChildren.jsx
@@ -0,0 +1,34 @@
+export default function DummyModalChildren() {
+ return (
+ <>
+
+ {/* */}
+
+
+ Terms of Service
+
+
+
+
+
+ Close modal
+
+
+ {/* */}
+
+
+ With less than a month to go before the European Union enacts new consumer privacy laws for its citizens, companies around the world are updating their terms of service agreements to comply.
+
+
+ The European Union’s General Data Protection Regulation (G.D.P.R.) goes into effect on May 25 and is meant to ensure a common set of data rights in the European Union. It requires organizations to notify users as soon as possible of high-risk data breaches that could personally affect them.
+
+
+ {/* */}
+
+ I accept
+ Decline
+
+
+ >
+ )
+}
diff --git a/src/components/modals/ModalWrapper.jsx b/src/components/modals/ModalWrapper.jsx
new file mode 100644
index 0000000..81f1791
--- /dev/null
+++ b/src/components/modals/ModalWrapper.jsx
@@ -0,0 +1,15 @@
+
+export default function ModalWrapper({children, maxWidth}) {
+ return (
+
+
+ {/* */}
+
+ {children}
+
+
+
+ )
+}
+
+// id="default-modal" tabIndex={-1} aria-hidden="true"
\ No newline at end of file
diff --git a/src/context/DefaultLayoutContext.jsx b/src/context/DefaultLayoutContext.jsx
new file mode 100644
index 0000000..a5257f8
--- /dev/null
+++ b/src/context/DefaultLayoutContext.jsx
@@ -0,0 +1,25 @@
+import { createContext, Dispatch, ReactNode, SetStateAction, useContext, useState } from 'react'
+
+const DefaultLayoutProvider = createContext
({})
+
+export default function DefaultLayoutContext({children}) {
+
+ const [subLinkIsActive, setSubLinkIsActive] = useState(null)
+
+
+ const values = {
+ subLinkIsActive,
+ setSubLinkIsActive
+ }
+
+ return (
+
+ {children}
+
+ )
+}
+
+
+export const layoutDefaultContext = () => {
+ return useContext(DefaultLayoutProvider)
+}
diff --git a/src/context/GeneralLayoutContext.jsx b/src/context/GeneralLayoutContext.jsx
new file mode 100644
index 0000000..48dc0d0
--- /dev/null
+++ b/src/context/GeneralLayoutContext.jsx
@@ -0,0 +1,77 @@
+import { createContext, useContext, useEffect, useState } from 'react'
+
+const GeneralContextProvider = createContext({})
+
+export default function GeneralLayoutContext({children}) {
+
+ const [theme, setTheme] = useState(null)
+
+ const [drawer, setDrawer] = useState('')
+
+ const [booking, setBooking] = useState('')
+
+ const [alertBox, setAlertBox] = useState({status:false, msg:''}) // USE TO SHOW SUcCESS OR FAILED ALERT MESSAGE
+
+ const [logoutModal, setLogoutModal] = useState(false) // USE TO SHOW LOGOUT MODAL BOX
+
+ const handleDrawer = (drawerToOpen) => { // FUNCTION TO DETERMINE WHICH ASIDE DRAWER TO SHOW
+ setDrawer((prev)=>{
+ if(!prev){
+ return drawerToOpen
+ }else if(drawerToOpen == prev){
+ return ''
+ }else{
+ return drawerToOpen
+ }
+ })
+ }
+
+ const handleBooking = (bookingToOpen) => { // FUNCTION TO DETERMINE WHICH ASIDE DRAWER TO SHOW
+ setBooking((prev)=>{
+ if(!prev){
+ return bookingToOpen
+ }else if(bookingToOpen == prev){
+ return ''
+ }else{
+ return bookingToOpen
+ }
+ })
+ }
+
+ const handleAlertBox = (valObj) => {
+ setAlertBox(valObj)
+ }
+
+ useEffect(() => {
+ if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
+ setTheme("dark");
+ } else {
+ setTheme("light");
+ }
+ }, []);
+
+ useEffect(() => {
+ if (theme === "dark") {
+ document.documentElement.classList.add("dark");
+ } else {
+ document.documentElement.classList.remove("dark");
+ }
+ }, [theme]);
+
+ const handleTheme = () => {
+ setTheme(theme === "dark" ? "light" : "dark");
+ }
+
+ let value = {theme, handleTheme, drawer, handleDrawer, booking, handleBooking, alertBox, handleAlertBox, logoutModal, setLogoutModal}
+
+ return (
+
+ {children}
+
+ )
+}
+
+
+export const generalLayoutContext = () => {
+ return useContext(GeneralContextProvider)
+}
diff --git a/src/index.css b/src/index.css
index aa352e1..0573138 100644
--- a/src/index.css
+++ b/src/index.css
@@ -23,3 +23,39 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
+
+@layer components {
+ .change-text::after{
+ content: '';
+ animation: text-change 5s linear 0s infinite;
+ }
+ .text-slide-in{
+ position: relative;
+ animation: slide-text-in .5s linear 0s forwards;
+ }
+
+ .pop-modal{
+ animation: pop-modal .1s linear 0s forwards;
+ }
+
+
+ /* ANIMATIONS */
+ @keyframes text-change {
+ 0%{content: 'Boundaries';}
+ 100%{content: 'Limitations';}
+ }
+
+ @keyframes slide-text-in {
+ /* 0%{left: -200%;}
+ 100%{left: 0%;} */
+ 0%{transform: scale(0); opacity: 0;}
+ 100%{transform: scale(1); opacity: 1;}
+ }
+
+ @keyframes pop-modal {
+ /* 0%{left: -200%;}
+ 100%{left: 0%;} */
+ 0%{margin-top: 20px; opacity: 0;}
+ 100%{margin-top: 0; opacity: 1;}
+ }
+}
diff --git a/src/index.js b/src/index.js
index 2cb1087..df3ff95 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,11 +1,23 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
+import { BrowserRouter } from 'react-router-dom'
+import { Provider } from "react-redux";
+
import './index.css';
import App from './App';
+import store from './store/store.js'
+import GeneralLayoutContext from './context/GeneralLayoutContext.jsx';
+
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
-
+
+
+
+
+
+
+
);
diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx
new file mode 100644
index 0000000..2ab360c
--- /dev/null
+++ b/src/pages/HomePage.jsx
@@ -0,0 +1,13 @@
+import React from 'react'
+import BreadcrumbCom from '../components/breadcrumb/BreadcrumbCom'
+
+export default function HomePage() {
+ return (
+
+
+
+ coming soon ...
+
+
+ )
+}
diff --git a/src/pages/UsersPage.jsx b/src/pages/UsersPage.jsx
new file mode 100644
index 0000000..aaef9fb
--- /dev/null
+++ b/src/pages/UsersPage.jsx
@@ -0,0 +1,13 @@
+import React from 'react'
+import BreadcrumbCom from '../components/breadcrumb/BreadcrumbCom'
+
+export default function UsersPage() {
+ return (
+
+
+
+ coming soon ...
+
+
+ )
+}
diff --git a/src/store/UserDetails.js b/src/store/UserDetails.js
new file mode 100644
index 0000000..8cb2677
--- /dev/null
+++ b/src/store/UserDetails.js
@@ -0,0 +1,20 @@
+import { createSlice } from "@reduxjs/toolkit";
+
+const initialState = {
+ userDetails: {},
+};
+
+export const userSlice = createSlice({
+ name: "userDetails",
+ initialState,
+ reducers: {
+ updateUserDetails: (state, action) => {
+ state.userDetails = { ...action.payload };
+ },
+ },
+});
+
+// Action creators are generated for each case reducer function
+export const { updateUserDetails } = userSlice.actions;
+
+export default userSlice.reducer;
diff --git a/src/store/store.js b/src/store/store.js
new file mode 100644
index 0000000..e111aec
--- /dev/null
+++ b/src/store/store.js
@@ -0,0 +1,9 @@
+import { configureStore } from "@reduxjs/toolkit";
+
+import userDetailReducer from "./UserDetails";
+
+export default configureStore({
+ reducer: {
+ userDetails: userDetailReducer,
+ },
+});
diff --git a/tailwind.config.js b/tailwind.config.js
index 10c37f7..e496ae3 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -3,7 +3,43 @@ module.exports = {
content: ["./src/**/*.{js,jsx,ts,tsx}"],
darkMode: "class",
theme: {
- extend: {},
+ extend: {
+ colors:{
+ brown:{
+ DEFAULT: '#393536',
+ },
+ black:{
+ DEFAULT: '#2c2e3e',
+ gray: '#a6a9b7'
+ },
+ primary: {
+ DEFAULT: '#0284c7',
+ },
+ orange: {
+ light: '#ED7747',
+ dark: '#9a3412'
+ },
+ white: {
+ DEFAULT: '#fff',
+ light: '#f1f5f9'
+ }
+ },
+ screens: {
+ max_width: '1700px'
+ },
+ fontSize:{
+ 10: '10px',
+ 12: '12px',
+ 14: '14px'
+ },
+ backgroundImage: {
+ login_gradient: 'linear-gradient(to right, #8e54e9 0, #4776e6 100%)'
+ },
+ boxShadow: {
+ round_black: '0 0px 1px 0 rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.19)',
+ round_white: '0 0px 1px 0 rgba(255, 255, 255, 0.2), 0 1px 5px 0 rgba(255, 255, 255, 0.19)'
+ }
+ },
},
plugins: [],
}