File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
export interface SystemMenu {
id: string;
parentId: string | null;
name: string;
idPath: string | null;
url: string | null;
roleId: string | null;
roleName: string | null;
sortOrder: number;
isActive: boolean;
isTopActive: boolean;
isDefault: boolean;
description: string | null;
largeIconId: string | null;
largeIconName: string | null;
largeIconUrl: string | null;
smallIconId: string | null;
smallIconName: string | null;
smallIconUrl: string | null;
}
export interface SystemMenuNode extends SystemMenu {
depth: number;
children: SystemMenuNode[];
}
export const EMPTY_FIELD_PLACEHOLDER = '-';
export function formatOptionalValue(
value: string | number | null | undefined
): string {
return value === null || value === undefined || value === ''
? EMPTY_FIELD_PLACEHOLDER
: String(value);
}
export function formatActiveLabel(isActive: boolean): string {
return isActive ? '활성' : '비활성';
}
/**
* 평면 목록을 트리로 세운다. 부모가 목록에 없는 항목(권한 때문에 잘려 온 가지 등)은 뿌리로
* 올려 화면에서 사라지지 않게 한다.
*/
export function buildMenuTree(items: SystemMenu[]): SystemMenuNode[] {
const byId = new Map<string, SystemMenuNode>();
for (const item of items) {
byId.set(item.id, { ...item, depth: 0, children: [] });
}
const roots: SystemMenuNode[] = [];
for (const node of byId.values()) {
const parent = node.parentId ? byId.get(node.parentId) : undefined;
if (parent && parent !== node) {
parent.children.push(node);
} else {
roots.push(node);
}
}
function order(nodes: SystemMenuNode[], depth: number): SystemMenuNode[] {
nodes.sort((a, b) => a.sortOrder - b.sortOrder || a.id.localeCompare(b.id));
for (const node of nodes) {
node.depth = depth;
order(node.children, depth + 1);
}
return nodes;
}
return order(roots, 0);
}
/** 펼쳐진 가지만 위에서부터 한 줄씩 늘어놓는다. */
export function flattenOpenNodes(
nodes: SystemMenuNode[],
isOpen: (id: string) => boolean
): SystemMenuNode[] {
const rows: SystemMenuNode[] = [];
function walk(list: SystemMenuNode[]) {
for (const node of list) {
rows.push(node);
if (node.children.length > 0 && isOpen(node.id)) {
walk(node.children);
}
}
}
walk(nodes);
return rows;
}
export function collectNodeIds(nodes: SystemMenuNode[]): string[] {
const ids: string[] = [];
function walk(list: SystemMenuNode[]) {
for (const node of list) {
if (node.children.length > 0) {
ids.push(node.id);
walk(node.children);
}
}
}
walk(nodes);
return ids;
}
/**
* 메뉴권한 셀렉트의 선택지 — 역할 목록 API가 없어 목록 응답에 실제로 쓰인 역할만 모은다
* (사용자 확정 사항).
*/
export function collectRoleOptions(
items: SystemMenu[]
): { value: string; label: string }[] {
const byId = new Map<string, string>();
for (const item of items) {
if (item.roleId && !byId.has(item.roleId)) {
byId.set(item.roleId, item.roleName ?? item.roleId);
}
}
return [...byId.entries()]
.map(([value, label]) => ({ value, label }))
.sort((a, b) => a.label.localeCompare(b.label, 'ko-KR'));
}
/** 수정·추가 팝업의 상위메뉴명 표시 — 기획의 `[3] 고객센터` 형식. */
export function formatParentLabel(parent: SystemMenu | null): string {
return parent ? `[${parent.id}] ${parent.name}` : '';
}