From 7ece0dd180f0cb5517bf12f10d1d4ab1bc5967df Mon Sep 17 00:00:00 2001 From: pgb Date: Thu, 19 Dec 2024 11:45:22 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A9=94=EB=89=B4=20=EC=82=AC=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=EB=B0=94=20=EA=B5=AC=EC=A1=B0=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fems-app/src/components/layout/SideNav.tsx | 282 ++++++++++++--------- 1 file changed, 160 insertions(+), 122 deletions(-) diff --git a/fems-app/src/components/layout/SideNav.tsx b/fems-app/src/components/layout/SideNav.tsx index 8a8e887..33cb7a3 100644 --- a/fems-app/src/components/layout/SideNav.tsx +++ b/fems-app/src/components/layout/SideNav.tsx @@ -11,9 +11,6 @@ import { api } from "@/lib/api"; import { Building2, Box, - DollarSign, - Users, - Sliders, ChevronDown, ChevronRight, Calendar, @@ -42,100 +39,138 @@ interface DBApiResponse { } } -interface MenuItem { - id: string; // id 추가 +interface ProcessedMenuItem { + id: string; title: string; - items: { - id: string; // id 추가 - title: string; - href: string; - icon: React.ComponentType<{ className?: string }>; - }[]; + href?: string; + icon: React.ComponentType<{ className?: string }>; + items?: ProcessedMenuItem[]; } interface MenuItemProps { - item: MenuItem; - isOpen: boolean; - onToggle: () => void; + item: ProcessedMenuItem; + depth: number; + openSections: { [key: string]: boolean }; + onToggle: (id: string) => void; pathname: string; } + const MenuItemComponent: React.FC = ({ - item, - isOpen, - onToggle, - pathname, -}) => { - const IconComponent = Box; - - return ( -
- - - {isOpen && ( -
- {item.items.map((subItem) => ( - - - {subItem.title} - - ))} -
- )} -
- ); -}; -function processMenuItems(responseData: DBApiResponse, role: string): MenuItem[] { + + ); + + return ( +
+ {item.href ? ( + 0 && "bg-gray-50/50" + ) + )} + > + {itemContent} + + ) : ( + + )} + + {hasChildren && isOpen && ( +
+ {item.items?.map((subItem) => ( + + ))} +
+ )} +
+ ); + }; + +function processMenuItems(responseData: DBApiResponse, role: string): ProcessedMenuItem[] { if (!responseData?.data?.data) { return []; } const menuData = responseData.data.data; - // 트리 구조로 된 메뉴 데이터로부터 MenuItem[] 생성 - const buildMenuItem = (menu: DBMenuItem): MenuItem => { - const children = menu.children || []; - + const buildMenuItem = (menu: DBMenuItem): ProcessedMenuItem => { // 자식 메뉴들을 seq 기준으로 정렬 - const sortedChildren = [...children].sort((a, b) => { - const seqA = parseInt(a.seq) || 0; - const seqB = parseInt(b.seq) || 0; - return seqA - seqB; - }); + const sortedChildren = menu.children + ? [...menu.children].sort((a, b) => { + const seqA = parseInt(a.seq) || 0; + const seqB = parseInt(b.seq) || 0; + return seqA - seqB; + }) + : []; - // 메뉴 아이템 생성 - const result = { + // 재귀적으로 자식 메뉴들을 처리 + const processedChildren = sortedChildren.map(buildMenuItem); + + return { id: menu.id, title: menu.menu_name_kor, - items: sortedChildren.map(child => ({ - id: child.id, - title: child.menu_name_kor, - href: child.menu_url || '#', - icon: Box - })) + href: menu.menu_url || undefined, + icon: Box, + ...(processedChildren.length > 0 && { items: processedChildren }), }; - return result; }; // 최상위 메뉴들을 찾고 권한 체크 후 seq로 정렬 @@ -151,6 +186,7 @@ function processMenuItems(responseData: DBApiResponse, role: string): MenuItem[] }) .map(buildMenuItem); } + export function SideNav() { const pathname = usePathname(); const { user } = useAuth(); @@ -170,36 +206,40 @@ export function SideNav() { const [openSections, setOpenSections] = useState<{ [key: string]: boolean }>( () => { - return menuItems.reduce((acc: { [key: string]: boolean }, item) => { - if (item.items?.some((subItem) => subItem.href === pathname)) { - acc[item.title] = true; - } - return acc; - }, {}); + const findOpenSections = (items: ProcessedMenuItem[], path: string): string[] => { + const openSections: string[] = []; + + const findPath = (items: ProcessedMenuItem[]): boolean => { + for (const item of items) { + if (item.href === path) { + return true; + } + if (item.items) { + if (findPath(item.items)) { + openSections.push(item.id); + return true; + } + } + } + return false; + }; + + findPath(items); + return openSections; + }; + + const openIds = findOpenSections(menuItems, pathname); + return openIds.reduce((acc, id) => ({ ...acc, [id]: true }), {}); } ); - const toggleSection = (title: string) => { + const toggleSection = (id: string) => { setOpenSections((prev) => ({ ...prev, - [title]: !prev[title], + [id]: !prev[id], })); }; - const formatDate = (dateString: string) => { - return new Date(dateString).toLocaleDateString("ko-KR", { - year: "numeric", - month: "long", - day: "numeric", - }); - }; - - const formatBusinessNumber = (number: string) => { - if (!number) return ""; - const cleaned = number.replace(/[^0-9]/g, ""); - return `${cleaned.slice(0, 3)}-${cleaned.slice(3, 5)}-${cleaned.slice(5)}`; - }; - return (