- Enhanced the menu management functionality by adding a new `menu_icon` field in the database schema, allowing for the storage of menu icons. - Updated the `saveMenu` and `updateMenu` functions in the admin controller to handle the new `menu_icon` field during menu creation and updates. - Modified the `AdminService` to include `MENU_ICON` in various queries, ensuring that the icon data is retrieved and processed correctly. - Integrated the `MenuIconPicker` component in the frontend to allow users to select and display menu icons in the `MenuFormModal`. - Updated the sidebar and layout components to utilize the new icon data, enhancing the visual representation of menus across the application.
89 lines
3.1 KiB
TypeScript
89 lines
3.1 KiB
TypeScript
import { ChevronDown, ChevronRight, Home, FileText, Users, BarChart3, Cog, GitBranch } from "lucide-react";
|
|
import { cn } from "@/lib/utils";
|
|
import { MenuItem } from "@/types/menu";
|
|
import { MENU_ICONS, MESSAGES } from "@/constants/layout";
|
|
import { getIconComponent } from "@/components/admin/MenuIconPicker";
|
|
|
|
interface MainSidebarProps {
|
|
menuList: MenuItem[];
|
|
expandedMenus: Set<string>;
|
|
onMenuClick: (menu: MenuItem) => void;
|
|
className?: string;
|
|
}
|
|
|
|
/**
|
|
* 메뉴 아이콘 선택 함수 (DB 아이콘 우선, 없으면 키워드 기반 fallback)
|
|
*/
|
|
const getMenuIcon = (menuName: string, dbIconName?: string | null) => {
|
|
if (dbIconName) {
|
|
const DbIcon = getIconComponent(dbIconName);
|
|
if (DbIcon) return <DbIcon className="h-4 w-4" />;
|
|
}
|
|
|
|
if (MENU_ICONS.HOME.some((keyword) => menuName.includes(keyword))) {
|
|
return <Home className="h-4 w-4" />;
|
|
}
|
|
if (MENU_ICONS.DOCUMENT.some((keyword) => menuName.includes(keyword))) {
|
|
return <FileText className="h-4 w-4" />;
|
|
}
|
|
if (MENU_ICONS.USERS.some((keyword) => menuName.includes(keyword))) {
|
|
return <Users className="h-4 w-4" />;
|
|
}
|
|
if (MENU_ICONS.STATISTICS.some((keyword) => menuName.includes(keyword))) {
|
|
return <BarChart3 className="h-4 w-4" />;
|
|
}
|
|
if (MENU_ICONS.SETTINGS.some((keyword) => menuName.includes(keyword))) {
|
|
return <Cog className="h-4 w-4" />;
|
|
}
|
|
if (MENU_ICONS.DATAFLOW.some((keyword) => menuName.includes(keyword))) {
|
|
return <GitBranch className="h-4 w-4" />;
|
|
}
|
|
return <FileText className="h-4 w-4" />;
|
|
};
|
|
|
|
/**
|
|
* 메인 사이드바 컴포넌트
|
|
*/
|
|
export function MainSidebar({ menuList, expandedMenus, onMenuClick, className = "" }: MainSidebarProps) {
|
|
/**
|
|
* 메뉴 아이템 렌더링
|
|
*/
|
|
const renderMenuItem = (menu: MenuItem, level: number = 0) => {
|
|
const hasChildren = menu.children && menu.children.length > 0;
|
|
const isExpanded = expandedMenus.has(String(menu.OBJID));
|
|
|
|
return (
|
|
<div key={String(menu.OBJID)}>
|
|
<button
|
|
onClick={() => onMenuClick(menu)}
|
|
className={cn(
|
|
"flex w-full cursor-pointer items-center justify-between rounded-lg px-3 py-2 text-sm transition-colors",
|
|
"hover:bg-accent hover:text-accent-foreground",
|
|
level > 0 && "ml-4",
|
|
)}
|
|
>
|
|
<div className="flex items-center gap-2">
|
|
{getMenuIcon(menu.MENU_NAME_KOR || menu.menuNameKor || "", menu.MENU_ICON || menu.menu_icon)}
|
|
<span>{menu.MENU_NAME_KOR || menu.menuNameKor || "메뉴"}</span>
|
|
</div>
|
|
{hasChildren && (isExpanded ? <ChevronDown className="h-4 w-4" /> : <ChevronRight className="h-4 w-4" />)}
|
|
</button>
|
|
|
|
{hasChildren && isExpanded && (
|
|
<div className="mt-1 ml-2 space-y-1">{menu.children!.map((child) => renderMenuItem(child, level + 1))}</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
return (
|
|
<nav className={cn("space-y-1 overflow-y-auto", className)}>
|
|
{menuList.length === 0 ? (
|
|
<div className="text-sm text-gray-500">{MESSAGES.NO_MENUS}</div>
|
|
) : (
|
|
menuList.map((menu) => renderMenuItem(menu))
|
|
)}
|
|
</nav>
|
|
);
|
|
}
|