2025-08-21 09:41:46 +09:00
|
|
|
import { apiClient } from "./client";
|
|
|
|
|
|
|
|
|
|
export interface MenuItem {
|
|
|
|
|
objid?: string;
|
|
|
|
|
OBJID?: string;
|
|
|
|
|
parent_obj_id?: string;
|
|
|
|
|
PARENT_OBJ_ID?: string;
|
|
|
|
|
menu_name_kor?: string;
|
|
|
|
|
MENU_NAME_KOR?: string;
|
|
|
|
|
menu_url?: string;
|
|
|
|
|
MENU_URL?: string;
|
|
|
|
|
menu_desc?: string;
|
|
|
|
|
MENU_DESC?: string;
|
|
|
|
|
seq?: number;
|
|
|
|
|
SEQ?: number;
|
|
|
|
|
menu_type?: string;
|
|
|
|
|
MENU_TYPE?: string;
|
|
|
|
|
status?: string;
|
|
|
|
|
STATUS?: string;
|
|
|
|
|
lev?: number;
|
|
|
|
|
LEV?: number;
|
|
|
|
|
lpad_menu_name_kor?: string;
|
|
|
|
|
LPAD_MENU_NAME_KOR?: string;
|
|
|
|
|
status_title?: string;
|
|
|
|
|
STATUS_TITLE?: string;
|
|
|
|
|
writer?: string;
|
|
|
|
|
WRITER?: string;
|
|
|
|
|
regdate?: string;
|
|
|
|
|
REGDATE?: string;
|
|
|
|
|
company_code?: string;
|
|
|
|
|
COMPANY_CODE?: string;
|
|
|
|
|
company_name?: string;
|
|
|
|
|
COMPANY_NAME?: string;
|
|
|
|
|
// 다국어 관련 필드 추가
|
|
|
|
|
lang_key?: string;
|
|
|
|
|
LANG_KEY?: string;
|
|
|
|
|
lang_key_desc?: string;
|
|
|
|
|
LANG_KEY_DESC?: string;
|
|
|
|
|
translated_name?: string;
|
|
|
|
|
TRANSLATED_NAME?: string;
|
|
|
|
|
translated_desc?: string;
|
|
|
|
|
TRANSLATED_DESC?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface MenuFormData {
|
|
|
|
|
objid?: string;
|
|
|
|
|
parentObjId: string;
|
|
|
|
|
menuNameKor: string;
|
|
|
|
|
menuUrl: string;
|
|
|
|
|
menuDesc: string;
|
|
|
|
|
seq: number;
|
|
|
|
|
menuType: string;
|
|
|
|
|
status: string;
|
|
|
|
|
companyCode: string;
|
|
|
|
|
langKey?: string; // 다국어 키 추가
|
2025-11-13 12:22:33 +09:00
|
|
|
screenCode?: string; // 화면 코드 추가
|
2025-08-21 09:41:46 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface LangKey {
|
|
|
|
|
keyId: number;
|
|
|
|
|
companyCode: string;
|
|
|
|
|
menuName: string;
|
|
|
|
|
langKey: string;
|
|
|
|
|
description: string;
|
|
|
|
|
isActive: string;
|
|
|
|
|
createdDate: string;
|
|
|
|
|
createdBy: string;
|
|
|
|
|
updatedDate: string;
|
|
|
|
|
updatedBy: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ApiResponse<T> {
|
|
|
|
|
success: boolean;
|
|
|
|
|
data?: T;
|
|
|
|
|
message: string;
|
|
|
|
|
errorCode?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const menuApi = {
|
2025-10-30 17:55:55 +09:00
|
|
|
// 관리자 메뉴 목록 조회 (좌측 사이드바용 - active만 표시)
|
2025-08-21 09:41:46 +09:00
|
|
|
getAdminMenus: async (): Promise<ApiResponse<MenuItem[]>> => {
|
2025-10-13 19:18:01 +09:00
|
|
|
const response = await apiClient.get("/admin/menus", { params: { menuType: "0" } });
|
2025-08-21 09:41:46 +09:00
|
|
|
if (response.data.success && response.data.data && response.data.data.length > 0) {
|
|
|
|
|
}
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2025-10-30 17:55:55 +09:00
|
|
|
// 사용자 메뉴 목록 조회 (좌측 사이드바용 - active만 표시)
|
2025-08-21 09:41:46 +09:00
|
|
|
getUserMenus: async (): Promise<ApiResponse<MenuItem[]>> => {
|
2025-10-13 19:18:01 +09:00
|
|
|
const response = await apiClient.get("/admin/menus", { params: { menuType: "1" } });
|
2025-08-21 09:41:46 +09:00
|
|
|
return response.data;
|
2025-10-30 17:55:55 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 관리자 메뉴 목록 조회 (메뉴 관리 화면용 - 모든 상태 표시)
|
|
|
|
|
getAdminMenusForManagement: async (): Promise<ApiResponse<MenuItem[]>> => {
|
|
|
|
|
const response = await apiClient.get("/admin/menus", { params: { menuType: "0", includeInactive: "true" } });
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 사용자 메뉴 목록 조회 (메뉴 관리 화면용 - 모든 상태 표시)
|
|
|
|
|
getUserMenusForManagement: async (): Promise<ApiResponse<MenuItem[]>> => {
|
|
|
|
|
const response = await apiClient.get("/admin/menus", { params: { menuType: "1", includeInactive: "true" } });
|
|
|
|
|
return response.data;
|
2025-08-21 09:41:46 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 메뉴 정보 조회
|
|
|
|
|
getMenuInfo: async (menuId: string): Promise<ApiResponse<MenuItem>> => {
|
|
|
|
|
const response = await apiClient.get(`/admin/menus/${menuId}`);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 메뉴 등록/수정
|
|
|
|
|
saveMenu: async (menuData: MenuFormData): Promise<ApiResponse<void>> => {
|
|
|
|
|
const response = await apiClient.post("/admin/menus", menuData);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-25 11:07:39 +09:00
|
|
|
// 메뉴 수정
|
|
|
|
|
updateMenu: async (menuId: string, menuData: MenuFormData): Promise<ApiResponse<void>> => {
|
|
|
|
|
const response = await apiClient.put(`/admin/menus/${menuId}`, menuData);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
2025-08-21 09:41:46 +09:00
|
|
|
// 메뉴 삭제
|
|
|
|
|
deleteMenu: async (menuId: string): Promise<ApiResponse<void>> => {
|
|
|
|
|
const response = await apiClient.delete(`/admin/menus/${menuId}`);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 메뉴 일괄 삭제
|
|
|
|
|
deleteMenusBatch: async (menuIds: string[]): Promise<ApiResponse<{ deletedCount: number; failedCount: number }>> => {
|
|
|
|
|
const response = await apiClient.delete("/admin/menus/batch", {
|
|
|
|
|
data: menuIds,
|
|
|
|
|
});
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 메뉴 활성/비활성 토글
|
|
|
|
|
toggleMenuStatus: async (menuId: string): Promise<ApiResponse<string>> => {
|
|
|
|
|
const response = await apiClient.put(`/admin/menus/${menuId}/toggle`);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 메뉴 권한 그룹 목록 조회
|
|
|
|
|
getMenuAuthGroups: async (menuId: string): Promise<ApiResponse<any[]>> => {
|
|
|
|
|
const response = await apiClient.get(`/admin/menus/${menuId}/auth-groups`);
|
|
|
|
|
return response.data;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 다국어 키 목록 조회
|
|
|
|
|
getLangKeys: async (params?: {
|
|
|
|
|
companyCode?: string;
|
|
|
|
|
menuCode?: string;
|
|
|
|
|
keyType?: string;
|
|
|
|
|
}): Promise<ApiResponse<LangKey[]>> => {
|
2025-08-25 11:07:39 +09:00
|
|
|
try {
|
|
|
|
|
// Node.js 백엔드의 실제 라우팅과 일치하도록 수정
|
2025-08-25 17:22:20 +09:00
|
|
|
const response = await apiClient.get("/multilang/keys", { params });
|
2025-08-25 11:07:39 +09:00
|
|
|
return response.data;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("❌ 다국어 키 목록 조회 실패:", error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
2025-08-21 09:41:46 +09:00
|
|
|
},
|
2025-11-21 14:37:09 +09:00
|
|
|
|
|
|
|
|
// 메뉴 복사
|
|
|
|
|
copyMenu: async (
|
|
|
|
|
menuObjid: number,
|
2025-11-21 15:38:59 +09:00
|
|
|
targetCompanyCode: string,
|
|
|
|
|
screenNameConfig?: {
|
|
|
|
|
removeText?: string;
|
|
|
|
|
addPrefix?: string;
|
|
|
|
|
}
|
2025-11-21 14:37:09 +09:00
|
|
|
): Promise<ApiResponse<MenuCopyResult>> => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await apiClient.post(
|
|
|
|
|
`/admin/menus/${menuObjid}/copy`,
|
2025-11-21 15:38:59 +09:00
|
|
|
{
|
|
|
|
|
targetCompanyCode,
|
|
|
|
|
screenNameConfig
|
|
|
|
|
}
|
2025-11-21 14:37:09 +09:00
|
|
|
);
|
|
|
|
|
return response.data;
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error("❌ 메뉴 복사 실패:", error);
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
message: error.response?.data?.message || "메뉴 복사 중 오류가 발생했습니다",
|
|
|
|
|
errorCode: error.response?.data?.error?.code || "MENU_COPY_ERROR",
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
},
|
2025-08-21 09:41:46 +09:00
|
|
|
};
|
2025-11-21 14:37:09 +09:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 메뉴 복사 결과
|
|
|
|
|
*/
|
|
|
|
|
export interface MenuCopyResult {
|
|
|
|
|
copiedMenus: number;
|
|
|
|
|
copiedScreens: number;
|
|
|
|
|
copiedFlows: number;
|
|
|
|
|
menuIdMap: Record<number, number>;
|
|
|
|
|
screenIdMap: Record<number, number>;
|
|
|
|
|
flowIdMap: Record<number, number>;
|
|
|
|
|
warnings?: string[];
|
|
|
|
|
}
|