Files
lti-web-client/src/components/MainDrawer.tsx
T
2025-10-23 12:51:20 +07:00

261 lines
6.1 KiB
TypeScript

'use client';
import { useCallback, useState } from 'react';
import { usePathname } from 'next/navigation';
import Image from 'next/image';
import { Icon } from '@iconify/react';
import Drawer from '@/components/Drawer';
import Menu from '@/components/menu/Menu';
import MenuItem from '@/components/menu/MenuItem';
import Navbar from '@/components/Navbar';
import Collapse from '@/components/Collapse';
import Button from '@/components/Button';
import { useUiStore } from '@/stores/ui/ui.store';
import { MAIN_DRAWER_LINKS } from '@/config/constant';
import { cn } from '@/lib/helper';
type CollapseMenuProps = {
title: string;
link: string;
icon: string;
submenu?: CollapseMenuProps[];
depth?: number;
};
const isPathActive = (pathname: string, link?: string) => {
if (!link) return false;
const splittedPathname = pathname.split('/');
const splittedLink = link.split('/');
const isActiveLinkValid = splittedLink.every((linkChunk, idx) => {
return linkChunk === splittedPathname[idx];
});
return pathname.startsWith(link) && isActiveLinkValid;
};
const CollapseMenu = ({
title,
link,
icon,
submenu,
depth = 0,
}: CollapseMenuProps) => {
const pathname = usePathname();
const isActive = isPathActive(pathname, link);
const [open, setOpen] = useState(isActive);
const menuCollapseTitle = (
<div
className={cn(
'w-full px-3 py-2 rounded-md text-base font-semibold transition-colors flex flex-row justify-between items-center gap-2 hover:bg-primary/10 opacity-40',
{
'bg-primary/10 opacity-100': open || isActive,
}
)}
>
<div className='flex flex-row items-center gap-2'>
<Icon icon={icon} width={20} height={20} />
<span>{title}</span>
</div>
<Icon
icon='cuida:caret-up-outline'
width={20}
height={20}
className={cn('transition-transform', {
'rotate-90': !open,
'rotate-180': open,
})}
/>
</div>
);
return (
<Collapse
open={open}
title={menuCollapseTitle}
onOpenChange={setOpen}
className='w-full'
titleClassName='w-full p-0!'
>
<Menu>
<div
className='w-full py-0.5 flex flex-col gap-0.5'
style={{
paddingLeft: `${0.5 * (depth + 1)}rem`,
}}
>
{submenu?.map((item, idx) => {
const hasSubmenu = item.submenu && item.submenu.length > 0;
if (!hasSubmenu) {
return (
<MenuItem
key={idx}
title={item.title}
href={item.link}
icon={item.icon}
active={isPathActive(pathname, item.link)}
/>
);
}
return (
<CollapseMenu
key={idx}
title={item.title}
link={item.link}
icon={item.icon}
submenu={item.submenu}
depth={depth + 1}
/>
);
})}
</div>
</Menu>
</Collapse>
);
};
const MainDrawerMenu = () => {
const pathname = usePathname();
return (
<Menu>
{MAIN_DRAWER_LINKS.map((item, idx) => {
const hasSubmenu = item.submenu && item.submenu.length > 0;
if (!hasSubmenu) {
return (
<MenuItem
key={idx}
title={item.title}
href={item.link}
icon={item.icon}
active={pathname.startsWith(item.link)}
/>
);
}
return (
<CollapseMenu
key={idx}
title={item.title}
link={item.link}
icon={item.icon}
submenu={item.submenu}
/>
);
})}
</Menu>
);
};
const MainDrawerContent = () => {
const { setMainDrawerOpen } = useUiStore();
const closeMainDrawerHandler = () => {
setMainDrawerOpen(false);
};
return (
<div className='w-full p-4 flex flex-col gap-4'>
<div className='flex flex-row items-center gap-4'>
<Image
src='/assets/img/lti-logo.png'
alt='MBU Logo'
width={256}
height={256}
className='w-full max-w-16 h-auto'
/>
<h1 className='text-xl font-bold'>LTI ERP</h1>
<div className='grow flex flex-row justify-end sm:hidden'>
<Button
variant='soft'
color='error'
onClick={closeMainDrawerHandler}
className='rounded-full'
>
<Icon
icon='material-symbols:close-rounded'
width={24}
height={24}
/>
</Button>
</div>
</div>
<MainDrawerMenu />
</div>
);
};
const MainDrawer = ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => {
const { mainDrawerOpen, setMainDrawerOpen } = useUiStore();
const pathname = usePathname();
const getPageTitle = useCallback(() => {
let title = '';
const activeMenu = MAIN_DRAWER_LINKS.find((item) =>
isPathActive(pathname, item.link)
);
const traverseMenuTitle = (menu: typeof activeMenu) => {
if (!menu) return;
const hasSubmenu = menu?.submenu && menu?.submenu.length > 0;
if (!title) {
title += menu?.title;
} else {
title += ' - ' + menu?.title;
}
if (!hasSubmenu || !menu.submenu) return;
const activeSubmenu = menu.submenu?.find((item) =>
isPathActive(pathname, item.link)
);
traverseMenuTitle(activeSubmenu);
};
traverseMenuTitle(activeMenu);
return title;
}, [pathname]);
const pageTitle = getPageTitle();
const toggleSidebar = () => {
setMainDrawerOpen(!mainDrawerOpen);
};
return (
<Drawer
open={mainDrawerOpen}
setOpen={setMainDrawerOpen}
openOnLarge
sidebarContent={<MainDrawerContent />}
>
<main className='w-full h-full flex flex-col'>
<Navbar title={pageTitle as string} toggleSidebar={toggleSidebar} />
{children}
</main>
</Drawer>
);
};
export default MainDrawer;