29921/frontend/src/layouts/Authenticated.tsx
2025-03-15 19:50:20 +00:00

119 lines
3.7 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { ReactNode, useEffect } from 'react';
import { useState } from 'react';
import jwt from 'jsonwebtoken';
import { mdiForwardburger, mdiBackburger, mdiMenu } from '@mdi/js';
import menuAside from '../menuAside';
import menuNavBar from '../menuNavBar';
import BaseIcon from '../components/BaseIcon';
import NavBar from '../components/NavBar';
import NavBarItemPlain from '../components/NavBarItemPlain';
import AsideMenu from '../components/AsideMenu';
import FooterBar from '../components/FooterBar';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
import Search from '../components/Search';
import { useRouter } from 'next/router';
import { findMe, logoutUser } from '../stores/authSlice';
type Props = {
children: ReactNode;
};
export default function LayoutAuthenticated({ children }: Props) {
const dispatch = useAppDispatch();
const router = useRouter();
const { token, currentUser } = useAppSelector((state) => state.auth);
const bgColor = useAppSelector((state) => state.style.bgLayoutColor);
let localToken;
if (typeof window !== 'undefined') {
// Perform localStorage action
localToken = localStorage.getItem('token');
}
const isTokenValid = () => {
const token = localStorage.getItem('token');
if (!token) return;
const date = new Date().getTime() / 1000;
const data = jwt.decode(token);
if (!data) return;
return date < data.exp;
};
useEffect(() => {
dispatch(findMe());
if (!isTokenValid()) {
dispatch(logoutUser());
router.push('/login');
}
}, [token, localToken]);
const darkMode = useAppSelector((state) => state.style.darkMode);
const [isAsideMobileExpanded, setIsAsideMobileExpanded] = useState(false);
const [isAsideLgActive, setIsAsideLgActive] = useState(false);
useEffect(() => {
const handleRouteChangeStart = () => {
setIsAsideMobileExpanded(false);
setIsAsideLgActive(false);
};
router.events.on('routeChangeStart', handleRouteChangeStart);
// If the component is unmounted, unsubscribe
// from the event with the `off` method:
return () => {
router.events.off('routeChangeStart', handleRouteChangeStart);
};
}, [router.events, dispatch]);
const layoutAsidePadding = 'xl:pl-60';
return (
<div
className={`${
darkMode ? 'dark' : ''
} overflow-hidden lg:overflow-visible`}
>
<div
className={`${layoutAsidePadding} ${
isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''
} pt-14 min-h-screen w-screen transition-position lg:w-auto ${bgColor} dark:bg-dark-800 dark:text-slate-100`}
>
<NavBar
menu={menuNavBar}
className={`${layoutAsidePadding} ${
isAsideMobileExpanded ? 'ml-60 lg:ml-0' : ''
}`}
>
<NavBarItemPlain
display='flex lg:hidden'
onClick={() => setIsAsideMobileExpanded(!isAsideMobileExpanded)}
>
<BaseIcon
path={isAsideMobileExpanded ? mdiBackburger : mdiForwardburger}
size='24'
/>
</NavBarItemPlain>
<NavBarItemPlain
display='hidden lg:flex xl:hidden'
onClick={() => setIsAsideLgActive(true)}
>
<BaseIcon path={mdiMenu} size='24' />
</NavBarItemPlain>
<NavBarItemPlain useMargin>
<Search />
</NavBarItemPlain>
</NavBar>
<AsideMenu
isAsideMobileExpanded={isAsideMobileExpanded}
isAsideLgActive={isAsideLgActive}
menu={menuAside}
onAsideLgClose={() => setIsAsideLgActive(false)}
/>
{children}
<FooterBar>Hand-crafted & Made with </FooterBar>
</div>
</div>
);
}