/** * 다국어 관리 API 클라이언트 * 카테고리, 키 자동 생성, 오버라이드 등 확장 기능 포함 */ import { apiClient } from "./client"; import { isCrossTenantMode } from "@/lib/auth/crossTenantMode"; // ===================================================== // 타입 정의 // ===================================================== export interface Language { lang_code: string; lang_name: string; lang_native: string; is_active: string; sort_order?: number; } export interface LangCategory { category_id: number; category_code: string; category_name: string; parent_id?: number | null; level: number; key_prefix: string; description?: string; sort_order: number; is_active: string; children?: LangCategory[]; } export interface LangKey { keyId?: number; companyCode: string; menuName?: string; langKey: string; description?: string; isActive: string; categoryId?: number; keyMeaning?: string; usageNote?: string; baseKeyId?: number; createdDate?: Date; } export interface LangText { textId?: number; keyId: number; langCode: string; langText: string; isActive: string; } export interface GenerateKeyRequest { companyCode: string; categoryId: number; keyMeaning: string; usageNote?: string; texts: Array<{ langCode: string; langText: string; }>; } export interface CreateOverrideKeyRequest { companyCode: string; baseKeyId: number; texts: Array<{ langCode: string; langText: string; }>; } export interface KeyPreview { langKey: string; exists: boolean; isOverride: boolean; baseKeyId?: number; } export interface ApiResponse { success: boolean; message?: string; data?: T; error?: { code: string; details?: any; }; } // ===================================================== // 카테고리 관련 API // ===================================================== /** * 카테고리 트리 조회 */ export async function getCategories(): Promise> { try { const response = await apiClient.get("/multilang/categories"); return response.data; } catch (error: any) { return { success: false, error: { code: "CATEGORY_FETCH_ERROR", details: error.message, }, }; } } /** * 카테고리 상세 조회 */ export async function getCategoryById(categoryId: number): Promise> { try { const response = await apiClient.get(`/multilang/categories/${categoryId}`); return response.data; } catch (error: any) { return { success: false, error: { code: "CATEGORY_FETCH_ERROR", details: error.message, }, }; } } /** * 카테고리 경로 조회 (부모 포함) */ export async function getCategoryPath(categoryId: number): Promise> { try { const response = await apiClient.get(`/multilang/categories/${categoryId}/path`); return response.data; } catch (error: any) { return { success: false, error: { code: "CATEGORY_PATH_ERROR", details: error.message, }, }; } } // ===================================================== // 언어 관련 API // ===================================================== /** * 언어 목록 조회 */ export async function getLanguages(): Promise> { try { const response = await apiClient.get("/multilang/languages"); return response.data; } catch (error: any) { return { success: false, error: { code: "LANGUAGE_FETCH_ERROR", details: error.message, }, }; } } // ===================================================== // 키 관련 API // ===================================================== /** * 다국어 키 목록 조회 * * 분기: * - cross-tenant 모드 → /admin/cross-tenant/lang-keys * 응답 행마다 company_code 박혀있어 화면에서 회사 컬럼/필터 가능. * 1차 구현은 categoryId 재귀 필터 비지원 (필요해지면 후속 추가). * - 단일 회사 모드 → /multilang/keys (기존) */ export async function getLangKeys(params?: { company_code?: string; menuCode?: string; categoryId?: number; searchText?: string; }): Promise> { try { if (isCrossTenantMode()) { const ctParams = new URLSearchParams(); // cross-tenant mapper 의 파라미터명 (snake_case) 으로 매핑 if (params?.menuCode) ctParams.append("menu_code", params.menuCode); if (params?.searchText) ctParams.append("search", params.searchText); const url = `/admin/cross-tenant/lang-keys${ctParams.toString() ? `?${ctParams.toString()}` : ""}`; const ctResponse = await apiClient.get(url); const ct = ctResponse.data; if (ct && ct.success && ct.data) { return { success: true, data: (ct.data.rows || []) as LangKey[], } as ApiResponse; } } const queryParams = new URLSearchParams(); if (params?.company_code) queryParams.append("companyCode", params.company_code); if (params?.menuCode) queryParams.append("menuCode", params.menuCode); if (params?.categoryId) queryParams.append("categoryId", params.categoryId.toString()); if (params?.searchText) queryParams.append("searchText", params.searchText); const url = `/multilang/keys${queryParams.toString() ? `?${queryParams.toString()}` : ""}`; const response = await apiClient.get(url); return response.data; } catch (error: any) { return { success: false, error: { code: "KEYS_FETCH_ERROR", details: error.message, }, }; } } /** * 키의 텍스트 조회 */ export async function getLangTexts(keyId: number): Promise> { try { const response = await apiClient.get(`/multilang/keys/${keyId}/texts`); return response.data; } catch (error: any) { return { success: false, error: { code: "TEXTS_FETCH_ERROR", details: error.message, }, }; } } /** * 키 자동 생성 */ export async function generateKey(data: GenerateKeyRequest): Promise> { try { const response = await apiClient.post("/multilang/keys/generate", data); return response.data; } catch (error: any) { return { success: false, error: { code: "KEY_GENERATE_ERROR", details: error.response?.data?.error?.details || error.message, }, }; } } /** * 키 미리보기 */ export async function previewKey( categoryId: number, keyMeaning: string, companyCode: string ): Promise> { try { const response = await apiClient.post("/multilang/keys/preview", { categoryId, keyMeaning, companyCode, }); return response.data; } catch (error: any) { return { success: false, error: { code: "KEY_PREVIEW_ERROR", details: error.message, }, }; } } /** * 오버라이드 키 생성 */ export async function createOverrideKey( data: CreateOverrideKeyRequest ): Promise> { try { const response = await apiClient.post("/multilang/keys/override", data); return response.data; } catch (error: any) { return { success: false, error: { code: "OVERRIDE_CREATE_ERROR", details: error.response?.data?.error?.details || error.message, }, }; } } /** * 회사별 오버라이드 키 목록 조회 */ export async function getOverrideKeys(companyCode: string): Promise> { try { const response = await apiClient.get(`/multilang/keys/overrides/${companyCode}`); return response.data; } catch (error: any) { return { success: false, error: { code: "OVERRIDE_KEYS_FETCH_ERROR", details: error.message, }, }; } } /** * 키 텍스트 저장 */ export async function saveLangTexts( keyId: number, texts: Array<{ langCode: string; langText: string }> ): Promise> { try { const response = await apiClient.post(`/multilang/keys/${keyId}/texts`, { texts }); return response.data; } catch (error: any) { return { success: false, error: { code: "TEXTS_SAVE_ERROR", details: error.message, }, }; } } /** * 키 삭제 */ export async function deleteLangKey(keyId: number): Promise> { try { const response = await apiClient.delete(`/multilang/keys/${keyId}`); return response.data; } catch (error: any) { return { success: false, error: { code: "KEY_DELETE_ERROR", details: error.message, }, }; } } /** * 키 상태 토글 */ export async function toggleLangKey(keyId: number): Promise> { try { const response = await apiClient.put(`/multilang/keys/${keyId}/toggle`); return response.data; } catch (error: any) { return { success: false, error: { code: "KEY_TOGGLE_ERROR", details: error.message, }, }; } } // ===================================================== // 화면 라벨 다국어 자동 생성 API // ===================================================== export interface ScreenLabelKeyResult { componentId: string; keyId: number; langKey: string; } export interface GenerateScreenLabelKeysRequest { screenId: number; menuObjId?: string; labels: Array<{ componentId: string; label: string; type?: string; }>; } /** * 화면 라벨 다국어 키 자동 생성 */ export async function generateScreenLabelKeys( params: GenerateScreenLabelKeysRequest ): Promise> { try { const response = await apiClient.post("/multilang/screen-labels", params); return response.data; } catch (error: any) { return { success: false, error: { code: "SCREEN_LABEL_KEY_GENERATION_ERROR", details: error.message, }, }; } }