2026-01-14 13:08:44 +09:00
|
|
|
/**
|
|
|
|
|
* 다국어 라벨 추출 유틸리티
|
|
|
|
|
* 화면 디자이너의 컴포넌트에서 다국어 처리가 필요한 라벨을 추출합니다.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { ComponentData } from "@/types/screen";
|
|
|
|
|
|
|
|
|
|
// 추출된 라벨 타입
|
|
|
|
|
export interface ExtractedLabel {
|
|
|
|
|
id: string;
|
|
|
|
|
componentId: string;
|
|
|
|
|
label: string;
|
|
|
|
|
type: "label" | "title" | "button" | "placeholder" | "column" | "filter" | "field" | "tab" | "action";
|
|
|
|
|
parentType?: string;
|
|
|
|
|
parentLabel?: string;
|
|
|
|
|
langKeyId?: number;
|
|
|
|
|
langKey?: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 입력 폼 컴포넌트인지 확인
|
|
|
|
|
const INPUT_COMPONENT_TYPES = new Set([
|
|
|
|
|
"text-field",
|
|
|
|
|
"number-field",
|
|
|
|
|
"date-field",
|
|
|
|
|
"datetime-field",
|
|
|
|
|
"select-field",
|
|
|
|
|
"checkbox-field",
|
|
|
|
|
"radio-field",
|
|
|
|
|
"textarea-field",
|
|
|
|
|
"file-field",
|
|
|
|
|
"email-field",
|
|
|
|
|
"tel-field",
|
|
|
|
|
"password-field",
|
|
|
|
|
"entity-field",
|
|
|
|
|
"code-field",
|
|
|
|
|
"category-field",
|
|
|
|
|
"input-field",
|
|
|
|
|
"widget",
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
const isInputComponent = (comp: any): boolean => {
|
|
|
|
|
const compType = comp.componentType || comp.type;
|
|
|
|
|
if (INPUT_COMPONENT_TYPES.has(compType)) return true;
|
|
|
|
|
if (compType === "widget" && comp.widgetType) return true;
|
|
|
|
|
if (comp.inputType || comp.webType) return true;
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 컬럼 라벨 맵 타입
|
|
|
|
|
export type ColumnLabelMap = Record<string, Record<string, string>>;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 컴포넌트에서 다국어 라벨 추출
|
|
|
|
|
* @param components 컴포넌트 배열
|
|
|
|
|
* @param columnLabelMap 테이블별 컬럼 라벨 맵 (선택사항)
|
|
|
|
|
* @returns 추출된 라벨 배열
|
|
|
|
|
*/
|
|
|
|
|
export function extractMultilangLabels(
|
|
|
|
|
components: ComponentData[],
|
|
|
|
|
columnLabelMap: ColumnLabelMap = {}
|
|
|
|
|
): ExtractedLabel[] {
|
|
|
|
|
const labels: ExtractedLabel[] = [];
|
|
|
|
|
const addedLabels = new Set<string>();
|
|
|
|
|
|
|
|
|
|
const addLabel = (
|
|
|
|
|
componentId: string,
|
|
|
|
|
label: string,
|
|
|
|
|
type: ExtractedLabel["type"],
|
|
|
|
|
parentType?: string,
|
|
|
|
|
parentLabel?: string,
|
|
|
|
|
langKeyId?: number,
|
|
|
|
|
langKey?: string
|
|
|
|
|
) => {
|
|
|
|
|
const key = `${componentId}_${type}_${label}`;
|
|
|
|
|
if (label && label.trim() && !addedLabels.has(key)) {
|
|
|
|
|
addedLabels.add(key);
|
|
|
|
|
labels.push({
|
|
|
|
|
id: key,
|
|
|
|
|
componentId,
|
|
|
|
|
label: label.trim(),
|
|
|
|
|
type,
|
|
|
|
|
parentType,
|
|
|
|
|
parentLabel,
|
|
|
|
|
langKeyId,
|
|
|
|
|
langKey,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const extractFromComponent = (comp: ComponentData, parentType?: string, parentLabel?: string) => {
|
|
|
|
|
const anyComp = comp as any;
|
2026-01-16 11:02:27 +09:00
|
|
|
const config = anyComp.componentConfig || anyComp.config;
|
2026-01-14 13:08:44 +09:00
|
|
|
const compType = anyComp.componentType || anyComp.type;
|
|
|
|
|
const compLabel = anyComp.label || anyComp.title || compType;
|
|
|
|
|
|
|
|
|
|
// 1. 기본 라벨 - 입력 폼 컴포넌트인 경우에만 추출
|
|
|
|
|
if (isInputComponent(anyComp)) {
|
|
|
|
|
if (anyComp.label && typeof anyComp.label === "string") {
|
|
|
|
|
addLabel(comp.id, anyComp.label, "label", parentType, parentLabel, anyComp.langKeyId, anyComp.langKey);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 제목
|
|
|
|
|
if (anyComp.title && typeof anyComp.title === "string") {
|
|
|
|
|
addLabel(comp.id, anyComp.title, "title", parentType, parentLabel);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 13:26:41 +09:00
|
|
|
// 3. 버튼 텍스트 (componentId에 _button 접미사 추가하여 라벨과 구분)
|
2026-01-14 13:08:44 +09:00
|
|
|
if (config?.text && typeof config.text === "string") {
|
2026-01-14 13:26:41 +09:00
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_button`,
|
|
|
|
|
config.text,
|
|
|
|
|
"button",
|
|
|
|
|
parentType,
|
|
|
|
|
parentLabel,
|
|
|
|
|
config.langKeyId,
|
|
|
|
|
config.langKey
|
|
|
|
|
);
|
2026-01-14 13:08:44 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. placeholder
|
|
|
|
|
if (anyComp.placeholder && typeof anyComp.placeholder === "string") {
|
|
|
|
|
addLabel(comp.id, anyComp.placeholder, "placeholder", parentType, parentLabel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 5. 테이블 컬럼 - columnLabelMap에서 한글 라벨 조회
|
|
|
|
|
const tableName = config?.selectedTable || config?.tableName || config?.table || anyComp.tableName;
|
|
|
|
|
if (config?.columns && Array.isArray(config.columns)) {
|
|
|
|
|
config.columns.forEach((col: any, index: number) => {
|
|
|
|
|
const colName = col.columnName || col.field || col.name;
|
|
|
|
|
// columnLabelMap에서 한글 라벨 조회, 없으면 displayName 사용
|
|
|
|
|
const colLabel = columnLabelMap[tableName]?.[colName] || col.displayName || col.label || colName;
|
|
|
|
|
|
|
|
|
|
if (colLabel && typeof colLabel === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_col_${index}`,
|
|
|
|
|
colLabel,
|
|
|
|
|
"column",
|
|
|
|
|
compType,
|
|
|
|
|
compLabel,
|
|
|
|
|
col.langKeyId,
|
|
|
|
|
col.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 16:33:22 +09:00
|
|
|
// 6. 분할 패널 제목 및 컬럼 - columnLabelMap에서 한글 라벨 조회
|
|
|
|
|
// 6-1. 좌측 패널 제목
|
|
|
|
|
if (config?.leftPanel?.title && typeof config.leftPanel.title === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_left_title`,
|
|
|
|
|
config.leftPanel.title,
|
|
|
|
|
"title",
|
|
|
|
|
compType,
|
|
|
|
|
`${compLabel} (좌측)`,
|
|
|
|
|
config.leftPanel.langKeyId,
|
|
|
|
|
config.leftPanel.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
// 6-2. 우측 패널 제목
|
|
|
|
|
if (config?.rightPanel?.title && typeof config.rightPanel.title === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_right_title`,
|
|
|
|
|
config.rightPanel.title,
|
|
|
|
|
"title",
|
|
|
|
|
compType,
|
|
|
|
|
`${compLabel} (우측)`,
|
|
|
|
|
config.rightPanel.langKeyId,
|
|
|
|
|
config.rightPanel.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
// 6-3. 좌측 패널 컬럼
|
2026-01-14 13:08:44 +09:00
|
|
|
const leftTableName = config?.leftPanel?.selectedTable || config?.leftPanel?.tableName || tableName;
|
|
|
|
|
if (config?.leftPanel?.columns && Array.isArray(config.leftPanel.columns)) {
|
|
|
|
|
config.leftPanel.columns.forEach((col: any, index: number) => {
|
|
|
|
|
const colName = col.columnName || col.field || col.name;
|
|
|
|
|
const colLabel = columnLabelMap[leftTableName]?.[colName] || col.displayName || col.label || colName;
|
|
|
|
|
if (colLabel && typeof colLabel === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_left_col_${index}`,
|
|
|
|
|
colLabel,
|
|
|
|
|
"column",
|
|
|
|
|
compType,
|
|
|
|
|
`${compLabel} (좌측)`,
|
|
|
|
|
col.langKeyId,
|
|
|
|
|
col.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
const rightTableName = config?.rightPanel?.selectedTable || config?.rightPanel?.tableName || tableName;
|
|
|
|
|
if (config?.rightPanel?.columns && Array.isArray(config.rightPanel.columns)) {
|
|
|
|
|
config.rightPanel.columns.forEach((col: any, index: number) => {
|
|
|
|
|
const colName = col.columnName || col.field || col.name;
|
|
|
|
|
const colLabel = columnLabelMap[rightTableName]?.[colName] || col.displayName || col.label || colName;
|
|
|
|
|
if (colLabel && typeof colLabel === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_right_col_${index}`,
|
|
|
|
|
colLabel,
|
|
|
|
|
"column",
|
|
|
|
|
compType,
|
|
|
|
|
`${compLabel} (우측)`,
|
|
|
|
|
col.langKeyId,
|
|
|
|
|
col.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 16:33:22 +09:00
|
|
|
// 6-5. 추가 탭 (additionalTabs) 제목 및 컬럼 - rightPanel.additionalTabs 확인
|
|
|
|
|
const additionalTabs = config?.rightPanel?.additionalTabs || config?.additionalTabs;
|
|
|
|
|
if (additionalTabs && Array.isArray(additionalTabs)) {
|
|
|
|
|
additionalTabs.forEach((tab: any, tabIndex: number) => {
|
|
|
|
|
// 탭 라벨
|
|
|
|
|
if (tab.label && typeof tab.label === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_addtab_${tabIndex}_label`,
|
|
|
|
|
tab.label,
|
|
|
|
|
"tab",
|
|
|
|
|
compType,
|
|
|
|
|
compLabel,
|
|
|
|
|
tab.langKeyId,
|
|
|
|
|
tab.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
// 탭 제목
|
|
|
|
|
if (tab.title && typeof tab.title === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_addtab_${tabIndex}_title`,
|
|
|
|
|
tab.title,
|
|
|
|
|
"title",
|
|
|
|
|
compType,
|
|
|
|
|
`${compLabel} (탭: ${tab.label || tabIndex})`,
|
|
|
|
|
tab.titleLangKeyId,
|
|
|
|
|
tab.titleLangKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
// 탭 컬럼
|
|
|
|
|
const tabTableName = tab.tableName || tab.selectedTable || rightTableName;
|
|
|
|
|
if (tab.columns && Array.isArray(tab.columns)) {
|
|
|
|
|
tab.columns.forEach((col: any, colIndex: number) => {
|
|
|
|
|
const colName = col.columnName || col.field || col.name;
|
|
|
|
|
const colLabel = columnLabelMap[tabTableName]?.[colName] || col.displayName || col.label || colName;
|
|
|
|
|
if (colLabel && typeof colLabel === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_addtab_${tabIndex}_col_${colIndex}`,
|
|
|
|
|
colLabel,
|
|
|
|
|
"column",
|
|
|
|
|
compType,
|
|
|
|
|
`${compLabel} (탭: ${tab.label || tabIndex})`,
|
|
|
|
|
col.langKeyId,
|
|
|
|
|
col.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 13:08:44 +09:00
|
|
|
// 7. 검색 필터
|
|
|
|
|
if (config?.filter?.filters && Array.isArray(config.filter.filters)) {
|
|
|
|
|
config.filter.filters.forEach((filter: any, index: number) => {
|
|
|
|
|
if (filter.label && typeof filter.label === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_filter_${index}`,
|
|
|
|
|
filter.label,
|
|
|
|
|
"filter",
|
|
|
|
|
compType,
|
|
|
|
|
compLabel,
|
|
|
|
|
filter.langKeyId,
|
|
|
|
|
filter.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 8. 폼 필드
|
|
|
|
|
if (config?.fields && Array.isArray(config.fields)) {
|
|
|
|
|
config.fields.forEach((field: any, index: number) => {
|
|
|
|
|
if (field.label && typeof field.label === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_field_${index}`,
|
|
|
|
|
field.label,
|
|
|
|
|
"field",
|
|
|
|
|
compType,
|
|
|
|
|
compLabel,
|
|
|
|
|
field.langKeyId,
|
|
|
|
|
field.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 9. 탭
|
|
|
|
|
if (config?.tabs && Array.isArray(config.tabs)) {
|
|
|
|
|
config.tabs.forEach((tab: any, index: number) => {
|
|
|
|
|
if (tab.label && typeof tab.label === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_tab_${index}`,
|
|
|
|
|
tab.label,
|
|
|
|
|
"tab",
|
|
|
|
|
compType,
|
|
|
|
|
compLabel,
|
|
|
|
|
tab.langKeyId,
|
|
|
|
|
tab.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 10. 액션 버튼
|
|
|
|
|
if (config?.actions?.actions && Array.isArray(config.actions.actions)) {
|
|
|
|
|
config.actions.actions.forEach((action: any, index: number) => {
|
|
|
|
|
if (action.label && typeof action.label === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_action_${index}`,
|
|
|
|
|
action.label,
|
|
|
|
|
"action",
|
|
|
|
|
compType,
|
|
|
|
|
compLabel,
|
|
|
|
|
action.langKeyId,
|
|
|
|
|
action.langKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 14:05:47 +09:00
|
|
|
// 11. 집계 위젯 (aggregation-widget) 항목 라벨
|
2026-01-16 11:02:27 +09:00
|
|
|
if (compType === "aggregation-widget" && config?.items && Array.isArray(config.items)) {
|
|
|
|
|
config.items.forEach((item: any, index: number) => {
|
|
|
|
|
if (item.columnLabel && typeof item.columnLabel === "string") {
|
|
|
|
|
addLabel(
|
|
|
|
|
`${comp.id}_agg_${item.id || index}`,
|
|
|
|
|
item.columnLabel,
|
|
|
|
|
"label",
|
|
|
|
|
compType,
|
|
|
|
|
compLabel,
|
|
|
|
|
item.labelLangKeyId,
|
|
|
|
|
item.labelLangKey
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 13:08:44 +09:00
|
|
|
// 자식 컴포넌트 재귀 탐색
|
|
|
|
|
if (anyComp.children && Array.isArray(anyComp.children)) {
|
|
|
|
|
anyComp.children.forEach((child: ComponentData) => {
|
|
|
|
|
extractFromComponent(child, compType, compLabel);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
components.forEach((comp) => extractFromComponent(comp));
|
|
|
|
|
return labels;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 컴포넌트에서 테이블명 추출
|
|
|
|
|
* @param components 컴포넌트 배열
|
|
|
|
|
* @returns 테이블명 Set
|
|
|
|
|
*/
|
|
|
|
|
export function extractTableNames(components: ComponentData[]): Set<string> {
|
|
|
|
|
const tableNames = new Set<string>();
|
|
|
|
|
|
|
|
|
|
const extractTableName = (comp: any) => {
|
|
|
|
|
const config = comp.componentConfig;
|
|
|
|
|
|
|
|
|
|
// 1. 컴포넌트 직접 tableName
|
|
|
|
|
if (comp.tableName) tableNames.add(comp.tableName);
|
|
|
|
|
|
|
|
|
|
// 2. componentConfig 직접 tableName
|
|
|
|
|
if (config?.tableName) tableNames.add(config.tableName);
|
|
|
|
|
|
|
|
|
|
// 3. 테이블 리스트 컴포넌트 - selectedTable (주요!)
|
|
|
|
|
if (config?.selectedTable) tableNames.add(config.selectedTable);
|
|
|
|
|
|
|
|
|
|
// 4. 테이블 리스트 컴포넌트 - table 속성
|
|
|
|
|
if (config?.table) tableNames.add(config.table);
|
|
|
|
|
|
|
|
|
|
// 5. 분할 패널의 leftPanel/rightPanel
|
|
|
|
|
if (config?.leftPanel?.tableName) tableNames.add(config.leftPanel.tableName);
|
|
|
|
|
if (config?.rightPanel?.tableName) tableNames.add(config.rightPanel.tableName);
|
|
|
|
|
if (config?.leftPanel?.table) tableNames.add(config.leftPanel.table);
|
|
|
|
|
if (config?.rightPanel?.table) tableNames.add(config.rightPanel.table);
|
|
|
|
|
if (config?.leftPanel?.selectedTable) tableNames.add(config.leftPanel.selectedTable);
|
|
|
|
|
if (config?.rightPanel?.selectedTable) tableNames.add(config.rightPanel.selectedTable);
|
|
|
|
|
|
|
|
|
|
// 6. 검색 필터의 tableName
|
|
|
|
|
if (config?.filter?.tableName) tableNames.add(config.filter.tableName);
|
|
|
|
|
|
|
|
|
|
// 7. properties 안의 tableName
|
|
|
|
|
if (comp.properties?.tableName) tableNames.add(comp.properties.tableName);
|
|
|
|
|
if (comp.properties?.selectedTable) tableNames.add(comp.properties.selectedTable);
|
|
|
|
|
|
|
|
|
|
// 자식 컴포넌트 탐색
|
|
|
|
|
if (comp.children && Array.isArray(comp.children)) {
|
|
|
|
|
comp.children.forEach(extractTableName);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
components.forEach(extractTableName);
|
|
|
|
|
return tableNames;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 다국어 키 매핑 결과를 컴포넌트에 적용
|
|
|
|
|
* @param components 원본 컴포넌트 배열
|
|
|
|
|
* @param mappings 다국어 키 매핑 결과 [{componentId, keyId, langKey}]
|
|
|
|
|
* @returns 업데이트된 컴포넌트 배열
|
|
|
|
|
*/
|
|
|
|
|
export function applyMultilangMappings(
|
|
|
|
|
components: ComponentData[],
|
|
|
|
|
mappings: Array<{ componentId: string; keyId: number; langKey: string }>
|
|
|
|
|
): ComponentData[] {
|
|
|
|
|
// 매핑을 빠르게 찾기 위한 맵 생성
|
|
|
|
|
const mappingMap = new Map(mappings.map((m) => [m.componentId, m]));
|
|
|
|
|
|
|
|
|
|
const updateComponent = (comp: ComponentData): ComponentData => {
|
|
|
|
|
const anyComp = comp as any;
|
2026-01-16 11:02:27 +09:00
|
|
|
const config = anyComp.componentConfig || anyComp.config;
|
2026-01-14 13:08:44 +09:00
|
|
|
let updated = { ...comp } as any;
|
|
|
|
|
|
|
|
|
|
// 기본 컴포넌트 라벨 매핑 확인
|
|
|
|
|
const labelMapping = mappingMap.get(comp.id);
|
|
|
|
|
if (labelMapping) {
|
|
|
|
|
updated.langKeyId = labelMapping.keyId;
|
|
|
|
|
updated.langKey = labelMapping.langKey;
|
2026-01-14 13:26:41 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 버튼 텍스트 매핑 (componentId_button 형식으로 조회)
|
|
|
|
|
const buttonMapping = mappingMap.get(`${comp.id}_button`);
|
|
|
|
|
if (buttonMapping && config?.text) {
|
|
|
|
|
updated.componentConfig = {
|
|
|
|
|
...updated.componentConfig,
|
|
|
|
|
langKeyId: buttonMapping.keyId,
|
|
|
|
|
langKey: buttonMapping.langKey,
|
|
|
|
|
};
|
2026-01-14 13:08:44 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 컬럼 매핑
|
|
|
|
|
if (config?.columns && Array.isArray(config.columns)) {
|
|
|
|
|
const updatedColumns = config.columns.map((col: any, index: number) => {
|
|
|
|
|
const colMapping = mappingMap.get(`${comp.id}_col_${index}`);
|
|
|
|
|
if (colMapping) {
|
|
|
|
|
return { ...col, langKeyId: colMapping.keyId, langKey: colMapping.langKey };
|
|
|
|
|
}
|
|
|
|
|
return col;
|
|
|
|
|
});
|
|
|
|
|
updated.componentConfig = { ...config, columns: updatedColumns };
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 16:33:22 +09:00
|
|
|
// 분할 패널 좌측 제목 매핑
|
|
|
|
|
const leftTitleMapping = mappingMap.get(`${comp.id}_left_title`);
|
|
|
|
|
if (leftTitleMapping && config?.leftPanel?.title) {
|
|
|
|
|
updated.componentConfig = {
|
|
|
|
|
...updated.componentConfig,
|
|
|
|
|
leftPanel: {
|
|
|
|
|
...updated.componentConfig?.leftPanel,
|
|
|
|
|
langKeyId: leftTitleMapping.keyId,
|
|
|
|
|
langKey: leftTitleMapping.langKey,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 분할 패널 우측 제목 매핑
|
|
|
|
|
const rightTitleMapping = mappingMap.get(`${comp.id}_right_title`);
|
|
|
|
|
if (rightTitleMapping && config?.rightPanel?.title) {
|
|
|
|
|
updated.componentConfig = {
|
|
|
|
|
...updated.componentConfig,
|
|
|
|
|
rightPanel: {
|
|
|
|
|
...updated.componentConfig?.rightPanel,
|
|
|
|
|
langKeyId: rightTitleMapping.keyId,
|
|
|
|
|
langKey: rightTitleMapping.langKey,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 분할 패널 좌측 컬럼 매핑 (이미 업데이트된 leftPanel 사용)
|
|
|
|
|
const currentLeftPanel = updated.componentConfig?.leftPanel || config?.leftPanel;
|
|
|
|
|
if (currentLeftPanel?.columns && Array.isArray(currentLeftPanel.columns)) {
|
|
|
|
|
const updatedLeftColumns = currentLeftPanel.columns.map((col: any, index: number) => {
|
2026-01-14 13:08:44 +09:00
|
|
|
const colMapping = mappingMap.get(`${comp.id}_left_col_${index}`);
|
|
|
|
|
if (colMapping) {
|
|
|
|
|
return { ...col, langKeyId: colMapping.keyId, langKey: colMapping.langKey };
|
|
|
|
|
}
|
|
|
|
|
return col;
|
|
|
|
|
});
|
|
|
|
|
updated.componentConfig = {
|
|
|
|
|
...updated.componentConfig,
|
2026-01-14 16:33:22 +09:00
|
|
|
leftPanel: { ...currentLeftPanel, columns: updatedLeftColumns },
|
2026-01-14 13:08:44 +09:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 16:33:22 +09:00
|
|
|
// 분할 패널 우측 컬럼 매핑 (이미 업데이트된 rightPanel 사용)
|
|
|
|
|
const currentRightPanel = updated.componentConfig?.rightPanel || config?.rightPanel;
|
|
|
|
|
if (currentRightPanel?.columns && Array.isArray(currentRightPanel.columns)) {
|
|
|
|
|
const updatedRightColumns = currentRightPanel.columns.map((col: any, index: number) => {
|
2026-01-14 13:08:44 +09:00
|
|
|
const colMapping = mappingMap.get(`${comp.id}_right_col_${index}`);
|
|
|
|
|
if (colMapping) {
|
|
|
|
|
return { ...col, langKeyId: colMapping.keyId, langKey: colMapping.langKey };
|
|
|
|
|
}
|
|
|
|
|
return col;
|
|
|
|
|
});
|
|
|
|
|
updated.componentConfig = {
|
|
|
|
|
...updated.componentConfig,
|
2026-01-14 16:33:22 +09:00
|
|
|
rightPanel: { ...currentRightPanel, columns: updatedRightColumns },
|
2026-01-14 13:08:44 +09:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 필터 매핑
|
|
|
|
|
if (config?.filter?.filters && Array.isArray(config.filter.filters)) {
|
|
|
|
|
const updatedFilters = config.filter.filters.map((filter: any, index: number) => {
|
|
|
|
|
const filterMapping = mappingMap.get(`${comp.id}_filter_${index}`);
|
|
|
|
|
if (filterMapping) {
|
|
|
|
|
return { ...filter, langKeyId: filterMapping.keyId, langKey: filterMapping.langKey };
|
|
|
|
|
}
|
|
|
|
|
return filter;
|
|
|
|
|
});
|
|
|
|
|
updated.componentConfig = {
|
|
|
|
|
...updated.componentConfig,
|
|
|
|
|
filter: { ...config.filter, filters: updatedFilters },
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 폼 필드 매핑
|
|
|
|
|
if (config?.fields && Array.isArray(config.fields)) {
|
|
|
|
|
const updatedFields = config.fields.map((field: any, index: number) => {
|
|
|
|
|
const fieldMapping = mappingMap.get(`${comp.id}_field_${index}`);
|
|
|
|
|
if (fieldMapping) {
|
|
|
|
|
return { ...field, langKeyId: fieldMapping.keyId, langKey: fieldMapping.langKey };
|
|
|
|
|
}
|
|
|
|
|
return field;
|
|
|
|
|
});
|
|
|
|
|
updated.componentConfig = { ...updated.componentConfig, fields: updatedFields };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 탭 매핑
|
|
|
|
|
if (config?.tabs && Array.isArray(config.tabs)) {
|
|
|
|
|
const updatedTabs = config.tabs.map((tab: any, index: number) => {
|
|
|
|
|
const tabMapping = mappingMap.get(`${comp.id}_tab_${index}`);
|
|
|
|
|
if (tabMapping) {
|
|
|
|
|
return { ...tab, langKeyId: tabMapping.keyId, langKey: tabMapping.langKey };
|
|
|
|
|
}
|
|
|
|
|
return tab;
|
|
|
|
|
});
|
|
|
|
|
updated.componentConfig = { ...updated.componentConfig, tabs: updatedTabs };
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 16:33:22 +09:00
|
|
|
// 추가 탭 (additionalTabs) 매핑 - rightPanel.additionalTabs 또는 additionalTabs 확인
|
|
|
|
|
const currentRightPanelForAddTabs = updated.componentConfig?.rightPanel || config?.rightPanel;
|
|
|
|
|
const configAdditionalTabs = currentRightPanelForAddTabs?.additionalTabs || config?.additionalTabs;
|
|
|
|
|
if (configAdditionalTabs && Array.isArray(configAdditionalTabs)) {
|
|
|
|
|
const updatedAdditionalTabs = configAdditionalTabs.map((tab: any, tabIndex: number) => {
|
|
|
|
|
let updatedTab = { ...tab };
|
|
|
|
|
|
|
|
|
|
// 탭 라벨 매핑
|
|
|
|
|
const labelMapping = mappingMap.get(`${comp.id}_addtab_${tabIndex}_label`);
|
|
|
|
|
if (labelMapping) {
|
|
|
|
|
updatedTab.langKeyId = labelMapping.keyId;
|
|
|
|
|
updatedTab.langKey = labelMapping.langKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 탭 제목 매핑
|
|
|
|
|
const titleMapping = mappingMap.get(`${comp.id}_addtab_${tabIndex}_title`);
|
|
|
|
|
if (titleMapping) {
|
|
|
|
|
updatedTab.titleLangKeyId = titleMapping.keyId;
|
|
|
|
|
updatedTab.titleLangKey = titleMapping.langKey;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 탭 컬럼 매핑
|
|
|
|
|
if (tab.columns && Array.isArray(tab.columns)) {
|
|
|
|
|
updatedTab.columns = tab.columns.map((col: any, colIndex: number) => {
|
|
|
|
|
const colMapping = mappingMap.get(`${comp.id}_addtab_${tabIndex}_col_${colIndex}`);
|
|
|
|
|
if (colMapping) {
|
|
|
|
|
return { ...col, langKeyId: colMapping.keyId, langKey: colMapping.langKey };
|
|
|
|
|
}
|
|
|
|
|
return col;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return updatedTab;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// rightPanel.additionalTabs에 저장하거나 additionalTabs에 저장
|
|
|
|
|
if (currentRightPanelForAddTabs?.additionalTabs) {
|
|
|
|
|
updated.componentConfig = {
|
|
|
|
|
...updated.componentConfig,
|
|
|
|
|
rightPanel: { ...currentRightPanelForAddTabs, additionalTabs: updatedAdditionalTabs },
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
updated.componentConfig = { ...updated.componentConfig, additionalTabs: updatedAdditionalTabs };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 13:08:44 +09:00
|
|
|
// 액션 버튼 매핑
|
|
|
|
|
if (config?.actions?.actions && Array.isArray(config.actions.actions)) {
|
|
|
|
|
const updatedActions = config.actions.actions.map((action: any, index: number) => {
|
|
|
|
|
const actionMapping = mappingMap.get(`${comp.id}_action_${index}`);
|
|
|
|
|
if (actionMapping) {
|
|
|
|
|
return { ...action, langKeyId: actionMapping.keyId, langKey: actionMapping.langKey };
|
|
|
|
|
}
|
|
|
|
|
return action;
|
|
|
|
|
});
|
|
|
|
|
updated.componentConfig = {
|
|
|
|
|
...updated.componentConfig,
|
|
|
|
|
actions: { ...config.actions, actions: updatedActions },
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-16 14:05:47 +09:00
|
|
|
// 집계 위젯 (aggregation-widget) 항목 라벨 매핑
|
2026-01-16 11:02:27 +09:00
|
|
|
if (compType === "aggregation-widget" && config?.items && Array.isArray(config.items)) {
|
|
|
|
|
const updatedItems = config.items.map((item: any, index: number) => {
|
|
|
|
|
const itemMapping = mappingMap.get(`${comp.id}_agg_${item.id || index}`);
|
|
|
|
|
if (itemMapping) {
|
|
|
|
|
return {
|
|
|
|
|
...item,
|
|
|
|
|
labelLangKeyId: itemMapping.keyId,
|
|
|
|
|
labelLangKey: itemMapping.langKey,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return item;
|
|
|
|
|
});
|
|
|
|
|
updated.componentConfig = {
|
|
|
|
|
...updated.componentConfig,
|
|
|
|
|
items: updatedItems,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-14 13:08:44 +09:00
|
|
|
// 자식 컴포넌트 재귀 처리
|
|
|
|
|
if (anyComp.children && Array.isArray(anyComp.children)) {
|
|
|
|
|
updated.children = anyComp.children.map((child: ComponentData) => updateComponent(child));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return updated;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return components.map(updateComponent);
|
|
|
|
|
}
|
|
|
|
|
|