feat: sidebar uses menu permissions API for role-based visibility

This commit is contained in:
pray4skylark
2026-06-08 10:51:23 +09:00
parent fe90547457
commit 9e27a7bc44
+32 -11
View File
@@ -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<Record<string, MenuPermission>>({});
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<string, MenuPermission> = {};
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') && (
<Link
href="/projects"
className={`flex items-center rounded-xl font-bold text-xs transition-all ${
@@ -121,6 +140,7 @@ const Sidebar = ({ isCollapsed }: { isCollapsed?: boolean }) => {
)}
{/* ITAM Group Menu */}
{canAccess('itam') && (
<div className="space-y-1">
<button
onClick={() => !isCollapsed && setIsItamOpen(!isItamOpen)}
@@ -165,6 +185,7 @@ const Sidebar = ({ isCollapsed }: { isCollapsed?: boolean }) => {
</div>
)}
</div>
)}
<Link
href="/work-reports"
@@ -194,7 +215,7 @@ const Sidebar = ({ isCollapsed }: { isCollapsed?: boolean }) => {
{!isCollapsed && <span className="whitespace-nowrap">Daily Report</span>}
</Link>
{isMgmt && (
{canAccess('reports-review') && (
<Link
href="/work-reports/review"
className={`flex items-center rounded-xl font-bold text-xs transition-all ${
@@ -210,7 +231,7 @@ const Sidebar = ({ isCollapsed }: { isCollapsed?: boolean }) => {
</Link>
)}
{role === 'ADMIN' && (
{canAccess('settings') && (
<>
<button
onClick={() => !isCollapsed && setIsAdminOpen(!isAdminOpen)}
@@ -272,7 +293,7 @@ const Sidebar = ({ isCollapsed }: { isCollapsed?: boolean }) => {
{!isCollapsed && <span className="whitespace-nowrap">Calendar</span>}
</Link>
{isMgmt && (
{canAccess('employees') && (
<Link
href="/employees"
className={`flex items-center rounded-xl font-bold text-xs transition-all ${