From 9e27a7bc44c25b21b26399e417bc41a9b4d2723f Mon Sep 17 00:00:00 2001 From: pray4skylark Date: Mon, 8 Jun 2026 10:51:23 +0900 Subject: [PATCH] feat: sidebar uses menu permissions API for role-based visibility --- src/components/layout/Sidebar.tsx | 43 +++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx index 44f525c..8df3e19 100644 --- a/src/components/layout/Sidebar.tsx +++ b/src/components/layout/Sidebar.tsx @@ -1,6 +1,6 @@ "use client"; -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import Link from 'next/link'; import { usePathname, useRouter } from 'next/navigation'; import { @@ -26,19 +26,38 @@ import { Shield, } from 'lucide-react'; import { useAuthStore } from '@/stores'; +import apiClient from '@/services/apiClient'; +import type { MenuPermission } from '@/types'; -/** - * SR 시스템의 메인 사이드바 컴포넌트 - * ITAM 하위 메뉴를 포함하도록 고도화되었습니다. - */ const Sidebar = ({ isCollapsed }: { isCollapsed?: boolean }) => { const pathname = usePathname(); const router = useRouter(); const [isItamOpen, setIsItamOpen] = useState(pathname.startsWith('/itam')); const [isAdminOpen, setIsAdminOpen] = useState(pathname.startsWith('/settings')); + const [menuPerms, setMenuPerms] = useState>({}); + const [permsLoaded, setPermsLoaded] = useState(false); const user = useAuthStore((s) => s.user); const role = user?.roleCode; - const isMgmt = role === 'MANAGER' || role === 'ADMIN'; + + useEffect(() => { + apiClient.get('/menu-permissions').then((res: any) => { + if (res?.success && res.data) { + const map: Record = {}; + res.data.forEach((p: MenuPermission) => { map[p.menuKey] = p; }); + setMenuPerms(map); + } + }).catch(() => {}).finally(() => setPermsLoaded(true)); + }, []); + + const canAccess = (menuKey: string): boolean => { + if (!permsLoaded) return true; // show all until loaded + const perm = menuPerms[menuKey]; + if (!perm) return true; + try { + const roles: string[] = JSON.parse(perm.allowedRoles); + return role ? roles.includes(role) : false; + } catch { return true; } + }; const menuItems = [ { name: 'Dashboard', href: '/', icon: LayoutDashboard }, @@ -103,8 +122,8 @@ const Sidebar = ({ isCollapsed }: { isCollapsed?: boolean }) => { ); })} - {/* Projects link (management only) */} - {isMgmt && ( + {/* Projects */} + {canAccess('projects') && ( { )} {/* ITAM Group Menu */} + {canAccess('itam') && (
)} + )} { {!isCollapsed && Daily Report} - {isMgmt && ( + {canAccess('reports-review') && ( { )} - {role === 'ADMIN' && ( + {canAccess('settings') && ( <>