Merge branch 'main' of http://39.117.244.52:3000/kjs/ERP-node into feature/v2-unified-renewal
This commit is contained in:
@@ -13,6 +13,7 @@ import type { SplitPanelPosition } from "@/contexts/SplitPanelContext";
|
||||
interface ScreenContextValue {
|
||||
screenId?: number;
|
||||
tableName?: string;
|
||||
menuObjid?: number; // 메뉴 OBJID (카테고리 값 조회 시 필요)
|
||||
splitPanelPosition?: SplitPanelPosition; // 🆕 분할 패널 위치 (left/right)
|
||||
|
||||
// 🆕 폼 데이터 (RepeaterFieldGroup 등 컴포넌트 데이터 저장)
|
||||
@@ -39,6 +40,7 @@ const ScreenContext = createContext<ScreenContextValue | null>(null);
|
||||
interface ScreenContextProviderProps {
|
||||
screenId?: number;
|
||||
tableName?: string;
|
||||
menuObjid?: number; // 메뉴 OBJID
|
||||
splitPanelPosition?: SplitPanelPosition; // 🆕 분할 패널 위치
|
||||
children: React.ReactNode;
|
||||
}
|
||||
@@ -49,6 +51,7 @@ interface ScreenContextProviderProps {
|
||||
export function ScreenContextProvider({
|
||||
screenId,
|
||||
tableName,
|
||||
menuObjid,
|
||||
splitPanelPosition,
|
||||
children,
|
||||
}: ScreenContextProviderProps) {
|
||||
@@ -112,6 +115,7 @@ export function ScreenContextProvider({
|
||||
() => ({
|
||||
screenId,
|
||||
tableName,
|
||||
menuObjid,
|
||||
splitPanelPosition,
|
||||
formData,
|
||||
updateFormData,
|
||||
@@ -127,6 +131,7 @@ export function ScreenContextProvider({
|
||||
[
|
||||
screenId,
|
||||
tableName,
|
||||
menuObjid,
|
||||
splitPanelPosition,
|
||||
formData,
|
||||
updateFormData,
|
||||
|
||||
182
frontend/contexts/ScreenMultiLangContext.tsx
Normal file
182
frontend/contexts/ScreenMultiLangContext.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext, useState, useEffect, useMemo, ReactNode } from "react";
|
||||
import { apiClient } from "@/lib/api/client";
|
||||
import { useMultiLang } from "@/hooks/useMultiLang";
|
||||
import { ComponentData } from "@/types/screen";
|
||||
|
||||
interface ScreenMultiLangContextValue {
|
||||
translations: Record<string, string>;
|
||||
loading: boolean;
|
||||
getTranslatedText: (langKey: string | undefined, fallback: string) => string;
|
||||
}
|
||||
|
||||
const ScreenMultiLangContext = createContext<ScreenMultiLangContextValue | null>(null);
|
||||
|
||||
interface ScreenMultiLangProviderProps {
|
||||
children: ReactNode;
|
||||
components: ComponentData[];
|
||||
companyCode?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 화면 컴포넌트들의 다국어 번역을 제공하는 Provider
|
||||
* 모든 langKey를 수집하여 한 번에 배치 조회하고, 하위 컴포넌트에서 번역 텍스트를 사용할 수 있게 함
|
||||
*/
|
||||
export const ScreenMultiLangProvider: React.FC<ScreenMultiLangProviderProps> = ({
|
||||
children,
|
||||
components,
|
||||
companyCode = "*",
|
||||
}) => {
|
||||
const { userLang } = useMultiLang();
|
||||
const [translations, setTranslations] = useState<Record<string, string>>({});
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
// 모든 컴포넌트에서 langKey 수집
|
||||
const langKeys = useMemo(() => {
|
||||
const keys: string[] = [];
|
||||
|
||||
const collectLangKeys = (comps: ComponentData[]) => {
|
||||
comps.forEach((comp) => {
|
||||
// 컴포넌트 라벨의 langKey
|
||||
if ((comp as any).langKey) {
|
||||
keys.push((comp as any).langKey);
|
||||
}
|
||||
// componentConfig 내의 langKey (버튼 텍스트 등)
|
||||
if ((comp as any).componentConfig?.langKey) {
|
||||
keys.push((comp as any).componentConfig.langKey);
|
||||
}
|
||||
// properties 내의 langKey (레거시)
|
||||
if ((comp as any).properties?.langKey) {
|
||||
keys.push((comp as any).properties.langKey);
|
||||
}
|
||||
// 테이블 리스트 컬럼의 langKey 수집
|
||||
if ((comp as any).componentConfig?.columns) {
|
||||
(comp as any).componentConfig.columns.forEach((col: any) => {
|
||||
if (col.langKey) {
|
||||
keys.push(col.langKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 분할패널 좌측/우측 제목 langKey 수집
|
||||
const config = (comp as any).componentConfig;
|
||||
if (config?.leftPanel?.langKey) {
|
||||
keys.push(config.leftPanel.langKey);
|
||||
}
|
||||
if (config?.rightPanel?.langKey) {
|
||||
keys.push(config.rightPanel.langKey);
|
||||
}
|
||||
// 분할패널 좌측/우측 컬럼 langKey 수집
|
||||
if (config?.leftPanel?.columns) {
|
||||
config.leftPanel.columns.forEach((col: any) => {
|
||||
if (col.langKey) {
|
||||
keys.push(col.langKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (config?.rightPanel?.columns) {
|
||||
config.rightPanel.columns.forEach((col: any) => {
|
||||
if (col.langKey) {
|
||||
keys.push(col.langKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 추가 탭 langKey 수집
|
||||
if (config?.additionalTabs) {
|
||||
config.additionalTabs.forEach((tab: any) => {
|
||||
if (tab.langKey) {
|
||||
keys.push(tab.langKey);
|
||||
}
|
||||
if (tab.titleLangKey) {
|
||||
keys.push(tab.titleLangKey);
|
||||
}
|
||||
if (tab.columns) {
|
||||
tab.columns.forEach((col: any) => {
|
||||
if (col.langKey) {
|
||||
keys.push(col.langKey);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// 자식 컴포넌트 재귀 처리
|
||||
if ((comp as any).children) {
|
||||
collectLangKeys((comp as any).children);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
collectLangKeys(components);
|
||||
return [...new Set(keys)]; // 중복 제거
|
||||
}, [components]);
|
||||
|
||||
// langKey가 있으면 배치 조회
|
||||
useEffect(() => {
|
||||
const loadTranslations = async () => {
|
||||
if (langKeys.length === 0 || !userLang) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
console.log("🌐 [ScreenMultiLang] 다국어 배치 로드:", { langKeys: langKeys.length, userLang, companyCode });
|
||||
|
||||
const response = await apiClient.post(
|
||||
"/multilang/batch",
|
||||
{ langKeys },
|
||||
{
|
||||
params: {
|
||||
userLang,
|
||||
companyCode,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (response.data?.success && response.data?.data) {
|
||||
console.log("✅ [ScreenMultiLang] 다국어 로드 완료:", Object.keys(response.data.data).length, "개");
|
||||
setTranslations(response.data.data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ [ScreenMultiLang] 다국어 로드 실패:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadTranslations();
|
||||
}, [langKeys, userLang, companyCode]);
|
||||
|
||||
// 번역 텍스트 가져오기 헬퍼
|
||||
const getTranslatedText = (langKey: string | undefined, fallback: string): string => {
|
||||
if (!langKey) return fallback;
|
||||
return translations[langKey] || fallback;
|
||||
};
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
translations,
|
||||
loading,
|
||||
getTranslatedText,
|
||||
}),
|
||||
[translations, loading]
|
||||
);
|
||||
|
||||
return <ScreenMultiLangContext.Provider value={value}>{children}</ScreenMultiLangContext.Provider>;
|
||||
};
|
||||
|
||||
/**
|
||||
* 화면 다국어 컨텍스트 사용 훅
|
||||
*/
|
||||
export const useScreenMultiLang = (): ScreenMultiLangContextValue => {
|
||||
const context = useContext(ScreenMultiLangContext);
|
||||
if (!context) {
|
||||
// 컨텍스트가 없으면 기본값 반환 (fallback)
|
||||
return {
|
||||
translations: {},
|
||||
loading: false,
|
||||
getTranslatedText: (_, fallback) => fallback,
|
||||
};
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user