Files
frontend/apps/cs/src/pages/menu/lib/utils.ts
T

173 lines
4.5 KiB
TypeScript

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<TreeMenu>[] => {
return data.reduce<NodeModel<TreeMenu>[]>((acc, menu) => {
const node: NodeModel<TreeMenu> = {
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<TreeMenu>[],
options: DropOptions<TreeMenu>,
): 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<TreeMenu>): 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<TreeMenu>) => void) =>
(node: NodeModel<TreeMenu>) => {
callback(node);
};
export const handleDropNode =
(callback: (newTree: TreeNode) => void) =>
(newTree: NodeModel<TreeMenu>[], options: DropOptions<TreeMenu>) => {
const newTreeData = generateNewMenuTreeData(newTree, options);
if (!newTreeData) return;
callback(newTreeData);
};
export const handleNodeTextChange =
(treeData: NodeModel<TreeMenu>[]) =>
(callback: (newTree: NodeModel<TreeMenu>[]) => 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<TreeUserOrganization | TreeUser>[] => {
return organizations.reduce<NodeModel<TreeUserOrganization | TreeUser>[]>(
(acc, org) => {
const orgNode: NodeModel<TreeUserOrganization> = {
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<TreeUserOrganization | TreeUser>[],
keyword: string,
): NodeModel<TreeUserOrganization | TreeUser>[] => {
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<NodeModel<TreeUserOrganization | TreeUser>>()),
);
};