import 'server-only'; import type { AdminMenu } from '@/lib/domain/admin-menu'; import type { AdminMenuValues } from '@/lib/domain/admin-menu-form'; /** * 관리자페이지 메뉴 Repository — 기획 ADM_MNU_001/002_p/003_p. * * TODO(백엔드): 이 화면의 API가 아직 없다. 아래 mock 저장소를 지우고 다른 Repository처럼 * backendFetch + getSessionAccessToken으로 교체하면 호출부는 그대로 둘 수 있다. * * ⚠️ 종전에는 `/api/v1/mngr/menu`(TB_SYS_MENU)를 불렀는데, 그것은 **앱 메뉴**이고 * 「시스템관리 > 메뉴관리」가 이미 같은 표를 편집한다. 두 화면이 같은 행을 고치게 되어 * 이 화면에서 삭제하면 앱 메뉴가 사라졌다. 전용 API가 나오기 전까지 mock으로 끊어 둔다. * * 이슈: 저장소가 모듈 메모리라 서버를 다시 띄우면 수정·삭제가 초기값으로 되돌아간다. */ const ALL_USERS = { roleId: 'ROLE_USER', roleName: '모든 사용자' }; const STUDENT = { roleId: 'ROLE_STUDENT', roleName: '수강생' }; let MENUS: AdminMenu[] = [ { id: '14', parentId: null, name: 'FOX PAY', url: '/main', description: '메인', sortOrder: 1, isActive: true, isTopActive: true, ...ALL_USERS, }, { id: '15', parentId: '14', name: '서비스 소개', url: '/intro', description: '서비스 소개', sortOrder: 1, isActive: true, isTopActive: false, ...ALL_USERS, }, { id: '1', parentId: null, name: '고객센터', url: '/cs', description: '고객센터', sortOrder: 2, isActive: true, isTopActive: true, ...ALL_USERS, }, { id: '16', parentId: '1', name: '이용 문의', url: '/cs/inquiry', description: '1:1 문의', sortOrder: 1, isActive: true, isTopActive: false, ...ALL_USERS, }, { id: '4', parentId: null, name: '내 강의실', url: '/class', description: '내 강의실', sortOrder: 4, isActive: true, isTopActive: true, ...STUDENT, }, { id: '5', parentId: '4', name: '교육 신청 이력', url: '/class/history', description: '교육 신청 이력', sortOrder: 1, isActive: true, isTopActive: false, ...STUDENT, }, { id: '9', parentId: '4', name: '내 정보 수정', url: '/class/profile', description: '내 정보 수정', sortOrder: 6, isActive: false, isTopActive: false, ...STUDENT, }, ]; /** 기획 ADM_MNU_001 ① — 메뉴 등록순 정렬. */ export async function fetchAdminMenus(): Promise { return MENUS.map((menu) => ({ ...menu })); } function roleNameOf(roleId: string): string { return ( MENUS.find((menu) => menu.roleId === roleId)?.roleName ?? roleId ); } export async function createAdminMenu( parentId: string, values: AdminMenuValues ): Promise { if (!MENUS.some((menu) => menu.id === parentId)) { throw new Error('상위 메뉴를 찾을 수 없습니다.'); } // 전체에서 가장 큰 번호 다음 — 형제 안에서만 세면 다른 가지와 번호가 겹친다. const nextId = MENUS.reduce( (max, menu) => Math.max(max, Number(menu.id) || 0), 0 ) + 1; MENUS = [ ...MENUS, { id: String(nextId), parentId, name: values.name, url: values.url, description: values.description, sortOrder: values.sortOrder, roleId: values.roleId, roleName: roleNameOf(values.roleId), isActive: values.isActive, isTopActive: values.isTopActive, }, ]; } export async function updateAdminMenu( menuId: string, values: AdminMenuValues ): Promise { const index = MENUS.findIndex((menu) => menu.id === menuId); if (index < 0) { throw new Error('메뉴를 찾을 수 없습니다.'); } MENUS[index] = { ...MENUS[index], name: values.name, url: values.url, description: values.description, sortOrder: values.sortOrder, roleId: values.roleId, roleName: roleNameOf(values.roleId), isActive: values.isActive, isTopActive: values.isTopActive, }; } /** 하위 메뉴까지 함께 지운다 — 부모만 지우면 갈 곳 없는 가지가 남는다. */ export async function deleteAdminMenu(menuId: string): Promise { if (!MENUS.some((menu) => menu.id === menuId)) { throw new Error('메뉴를 찾을 수 없습니다.'); } const doomed = new Set([menuId]); let grew = true; while (grew) { grew = false; for (const menu of MENUS) { if (menu.parentId && doomed.has(menu.parentId) && !doomed.has(menu.id)) { doomed.add(menu.id); grew = true; } } } MENUS = MENUS.filter((menu) => !doomed.has(menu.id)); }