"use client"; import { useCallback } from "react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { ChevronRight, ChevronDown, ChevronUp, Folder, FolderOpen, File, Pencil, Plus, ArrowUpDown, EyeOff, } from "lucide-react"; import type { MenuTreeNode } from "../types"; interface MenuTreeProps { nodes: MenuTreeNode[]; onEdit: (node: MenuTreeNode) => void; onMoveUp: (nodeId: number) => void; onMoveDown: (nodeId: number) => void; onMoveTo: (node: MenuTreeNode) => void; onAddGroup: (parentId: number) => void; expandedIds: Set; onExpandedIdsChange: (ids: Set) => void; isPending?: boolean; } interface TreeItemProps { node: MenuTreeNode; depth: number; isFirst: boolean; isLast: boolean; onEdit: (node: MenuTreeNode) => void; onMoveUp: (nodeId: number) => void; onMoveDown: (nodeId: number) => void; onMoveTo: (node: MenuTreeNode) => void; onAddGroup: (parentId: number) => void; isExpanded: boolean; onToggleExpand: () => void; isPending?: boolean; } function TreeItem({ node, depth, isFirst, isLast, onEdit, onMoveUp, onMoveDown, onMoveTo, onAddGroup, isExpanded, onToggleExpand, isPending, }: TreeItemProps) { const isMenuGroup = node.nodeType === "menu_group"; const isGroup = node.nodeType === "group"; const isMenu = node.nodeType === "menu"; const isTopLevel = node.parentId === null; const hasChildren = node.children && node.children.length > 0; const isExpandable = isMenuGroup || isGroup; // Move To is disabled for: // - menu_group (always at top level, cannot be moved) // - top-level menu (parentId === null, can only reorder with up/down) const canMoveTo = !isMenuGroup && !isTopLevel; const getIcon = () => { if (isMenuGroup || isGroup) { return isExpanded ? ( ) : ( ); } return ; }; const getTypeLabel = () => { switch (node.nodeType) { case "menu_group": return "Menu Group"; case "group": return "Group"; case "menu": return "Menu"; default: return ""; } }; return (
{/* Expand/Collapse */} {isExpandable ? ( ) : (
)} {/* Icon */} {getIcon()} {/* Title */} {node.titleKo} {node.titleEn && ( [{node.titleEn}] )} {/* Hidden indicator */} {!node.isActive && ( )} {/* Path (for menus) */} {isMenu && node.menuPath && ( {node.menuPath} )} {/* Type Badge */} {getTypeLabel()} {/* Active indicator */}
{/* Actions */}
{/* Move Up */} {/* Move Down */} {/* Move To (different parent) - disabled for top level nodes */} {/* Edit */} {/* Add Sub-Group (for menu groups only) */} {isMenuGroup && ( )}
); } export function MenuTree({ nodes, onEdit, onMoveUp, onMoveDown, onMoveTo, onAddGroup, expandedIds, onExpandedIdsChange, isPending, }: MenuTreeProps) { const toggleExpand = useCallback((nodeId: number) => { const next = new Set(expandedIds); if (next.has(nodeId)) { next.delete(nodeId); } else { next.add(nodeId); } onExpandedIdsChange(next); }, [expandedIds, onExpandedIdsChange]); const renderTree = (nodeList: MenuTreeNode[], depth: number) => { return nodeList.map((node, index) => { const isExpanded = expandedIds.has(node.id); const isExpandable = node.nodeType === "menu_group" || node.nodeType === "group"; const hasChildren = node.children && node.children.length > 0; return (
toggleExpand(node.id)} isPending={isPending} /> {isExpandable && isExpanded && hasChildren && (
{renderTree(node.children!, depth + 1)}
)}
); }); }; return
{renderTree(nodes, 0)}
; }