feat(FE-40): create MainDrawer component

This commit is contained in:
ValdiANS
2025-10-01 13:44:26 +07:00
parent c068fe5166
commit 2456d64a68
+205
View File
@@ -0,0 +1,205 @@
'use client';
import { 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 { 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('/');
return splittedPathname.every((pathnameChunk, idx) => {
return pathnameChunk === splittedLink[idx];
});
};
const isCollapseActive = (pathname: string, link?: string) => {
if (!link) return false;
return pathname === link || pathname.startsWith(link);
};
const CollapseMenu = ({
title,
link,
icon,
submenu,
depth = 0,
}: CollapseMenuProps) => {
const pathname = usePathname();
const [open, setOpen] = useState(isCollapseActive(pathname, link));
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',
{
'bg-primary/10': open,
}
)}
>
<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>
);
const paddingLeftDepth = `pl-${4 * (depth + 1)}`;
return (
<Collapse
open={open}
title={menuCollapseTitle}
onOpenChange={setOpen}
titleClassName='p-0!'
>
<Menu className={cn('py-0.5', paddingLeftDepth)}>
{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}
/>
);
})}
</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 = () => {
return (
<div className='w-full p-4 flex flex-col gap-4'>
<div className='flex 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>
<MainDrawerMenu />
</div>
);
};
const MainDrawer = ({
children,
}: Readonly<{
children: React.ReactNode;
}>) => {
const { mainDrawerOpen, setMainDrawerOpen } = useUiStore();
const pathname = usePathname();
const pageTitle = MAIN_DRAWER_LINKS.find((item) =>
pathname.startsWith(item.link)
)?.title;
const toggleSidebar = () => {
setMainDrawerOpen(!mainDrawerOpen);
};
return (
<Drawer
open={mainDrawerOpen}
setOpen={setMainDrawerOpen}
openOnLarge
sidebarContent={<MainDrawerContent />}
>
<main className='w-full h-full flex flex-col gap-4'>
<Navbar title={pageTitle as string} toggleSidebar={toggleSidebar} />
{children}
</main>
</Drawer>
);
};
export default MainDrawer;