File name
Commit message
Commit date
File name
Commit message
Commit date
09-04
File name
Commit message
Commit date
export type AdminMenu = {
id: string;
parentId: string | null;
name: string;
url: string;
description: string;
sortOrder: number;
roleId: string;
roleName: string;
isActive: boolean;
isTopActive: boolean;
};
export type AdminMenuNode = AdminMenu & {
depth: number;
children: AdminMenuNode[];
};
export const ADMIN_MENU_ROOT_PATH = '/admins/menus';
export function toYesNo(value: boolean): string {
return value ? 'Y' : 'N';
}
export const ACTIVE_OPTIONS = [
{ value: 'true', label: '활성' },
{ value: 'false', label: '비활성' },
] as const;
export const NEW_WINDOW_OPTIONS = [
{ value: 'true', label: '예' },
{ value: 'false', label: '아니오' },
] as const;
export function formatActiveLabel(isActive: boolean): string {
return isActive ? '활성' : '비활성';
}
/**
* 백엔드가 준 순서(메뉴 등록순)를 유지한 채 `parentId`로만 중첩한다.
* 부모가 목록에 없는 행은 최상위로 올려 화면에서 사라지지 않게 한다.
*/
export function buildAdminMenuTree(menus: AdminMenu[]): AdminMenuNode[] {
const nodes = new Map<string, AdminMenuNode>();
for (const menu of menus) {
nodes.set(menu.id, { ...menu, depth: 0, children: [] });
}
const roots: AdminMenuNode[] = [];
for (const menu of menus) {
const node = nodes.get(menu.id);
if (!node) continue;
const parent = menu.parentId ? nodes.get(menu.parentId) : undefined;
if (parent && parent !== node) {
parent.children.push(node);
} else {
roots.push(node);
}
}
const assignDepth = (list: AdminMenuNode[], depth: number) => {
for (const node of list) {
node.depth = depth;
assignDepth(node.children, depth + 1);
}
};
assignDepth(roots, 0);
return roots;
}
/** 트리를 화면이 그릴 순서(부모 바로 뒤에 자식)로 편다. 접힌 가지는 건너뛴다. */
export function flattenAdminMenuTree(
roots: AdminMenuNode[],
collapsedIds: ReadonlySet<string>
): AdminMenuNode[] {
const rows: AdminMenuNode[] = [];
const walk = (nodes: AdminMenuNode[]) => {
for (const node of nodes) {
rows.push(node);
if (!collapsedIds.has(node.id)) {
walk(node.children);
}
}
};
walk(roots);
return rows;
}