import type { DropOptions, NodeModel } from "@minoru/react-dnd-treeview"; import type { TreeUserOrganization, TreeUser, } from "@/entities/user-organization"; import type { TreeMenu, TreeNode } from "../model/tree-menu-interface"; export const generateMenuTreeData = ( data: TreeMenu[], ): NodeModel[] => { return data.reduce[]>((acc, menu) => { const node: NodeModel = { id: menu.menuCd ?? 0, parent: menu.parentId ?? 0, text: menu.menuKornNm, droppable: true, data: menu, }; acc.push(node); if (menu.children?.length) { acc.push(...generateMenuTreeData(menu.children)); } return acc; }, []); }; export const generateNewMenuTreeData = ( treeData: NodeModel[], options: DropOptions, ): TreeNode | null => { const { dragSourceId, dragSource, dropTargetId, dropTarget } = options; if (!dragSource?.data) return null; const siblings = treeData.filter((node) => node.parent === dropTargetId); const dragSourceIndex = siblings.findIndex( (node) => node.id === dragSourceId, ); const prevSibling = siblings[dragSourceIndex - 1]; const newSortSeq = dragSourceIndex === 0 ? 1 : (prevSibling?.data?.sortSeq ?? 0) + 1; const childNodes = treeData.filter( (node) => node.parent === dragSourceId && node.data, ); const baseNode = (node: NodeModel): TreeNode => { if (!node.data) return {}; return { menuCd: node.data?.menuCd, name: node.data?.menuKornNm, icon: node.data?.iconNm, }; }; const treeNode: TreeNode = { ...baseNode(dragSource), parentId: dropTargetId === 0 ? null : (dropTargetId as number), sortSeq: newSortSeq, levelNo: (dropTarget?.data?.levelNo ?? 0) + 1, }; const result: TreeNode = { ...treeNode, children: childNodes.map((child) => ({ ...baseNode(child), parentId: child.data?.parentId === 0 ? null : (dropTargetId as number), sortSeq: child.data?.sortSeq, levelNo: (treeNode?.levelNo ?? 0) + 1, })), }; return result; }; export const handleSelectNode = (callback: (node: NodeModel) => void) => (node: NodeModel) => { callback(node); }; export const handleDropNode = (callback: (newTree: TreeNode) => void) => (newTree: NodeModel[], options: DropOptions) => { const newTreeData = generateNewMenuTreeData(newTree, options); if (!newTreeData) return; callback(newTreeData); }; export const handleNodeTextChange = (treeData: NodeModel[]) => (callback: (newTree: NodeModel[]) => void) => (id: NodeModel["id"], value: string) => { const newTree = treeData.map((node) => { if (node.id === id) { return { ...node, text: value, }; } return node; }); callback(newTree); }; export const generateUserOrganizationTreeData = ( organizations: TreeUserOrganization[], ): NodeModel[] => { return organizations.reduce[]>( (acc, org) => { const orgNode: NodeModel = { id: org.ognzSn, parent: org.upOgnzSn ?? 0, text: org.ognzNm, data: org, droppable: false, }; acc.push(orgNode); if (org.children?.length) { acc.push(...generateUserOrganizationTreeData(org.children)); } if (org.users?.length) { acc.push( ...org.users.map((user) => ({ id: user.userUnqId, parent: org.ognzSn, text: user.userNm, data: user, droppable: false, })), ); } return acc; }, [], ); }; export const filterUserOrganizationTreeDataByKeyword = ( treeData: NodeModel[], keyword: string, ): NodeModel[] => { return Array.from( treeData.reduce((matchedNodes, node) => { if (node.data && "ognzSn" in node.data) return matchedNodes; // 조직 노드는 필터링 제외 if (!node.text.includes(keyword)) return matchedNodes; matchedNodes.add(node); let parentId = node.parent; while (parentId) { const parentNode = treeData.find((n) => n.id === parentId); if (!parentNode) break; matchedNodes.add(parentNode); parentId = parentNode.parent; } return matchedNodes; }, new Set>()), ); };