From efc4768ed7c2ae197b6df9cd32732bb2dd56011e Mon Sep 17 00:00:00 2001 From: kjs Date: Thu, 26 Feb 2026 13:45:56 +0900 Subject: [PATCH] Merge branch 'feature/v2-renewal' of http://39.117.244.52:3000/kjs/ERP-node into jskim-node --- .../screen/panels/V2PropertiesPanel.tsx | 2 + .../v2/config-panels/V2SelectConfigPanel.tsx | 210 +++++++++++++++--- 2 files changed, 182 insertions(+), 30 deletions(-) diff --git a/frontend/components/screen/panels/V2PropertiesPanel.tsx b/frontend/components/screen/panels/V2PropertiesPanel.tsx index 2999ed74..6242cd89 100644 --- a/frontend/components/screen/panels/V2PropertiesPanel.tsx +++ b/frontend/components/screen/panels/V2PropertiesPanel.tsx @@ -237,6 +237,8 @@ export const V2PropertiesPanel: React.FC = ({ const extraProps: Record = {}; if (componentId === "v2-select") { extraProps.inputType = inputType; + extraProps.tableName = selectedComponent.tableName || currentTable?.tableName || currentTableName; + extraProps.columnName = selectedComponent.columnName || currentConfig.fieldKey || currentConfig.columnName; } if (componentId === "v2-list") { extraProps.currentTableName = currentTableName; diff --git a/frontend/components/v2/config-panels/V2SelectConfigPanel.tsx b/frontend/components/v2/config-panels/V2SelectConfigPanel.tsx index f808ecf1..d631f454 100644 --- a/frontend/components/v2/config-panels/V2SelectConfigPanel.tsx +++ b/frontend/components/v2/config-panels/V2SelectConfigPanel.tsx @@ -20,41 +20,111 @@ interface ColumnOption { columnLabel: string; } +interface CategoryValueOption { + valueCode: string; + valueLabel: string; +} + interface V2SelectConfigPanelProps { config: Record; onChange: (config: Record) => void; - /** 컬럼의 inputType (entity 타입인 경우에만 엔티티 소스 표시) */ + /** 컬럼의 inputType (entity/category 타입 확인용) */ inputType?: string; + /** 현재 테이블명 (카테고리 값 조회용) */ + tableName?: string; + /** 현재 컬럼명 (카테고리 값 조회용) */ + columnName?: string; } -export const V2SelectConfigPanel: React.FC = ({ config, onChange, inputType }) => { - // 엔티티 타입인지 확인 +export const V2SelectConfigPanel: React.FC = ({ + config, + onChange, + inputType, + tableName, + columnName, +}) => { const isEntityType = inputType === "entity"; - // 엔티티 테이블의 컬럼 목록 + const isCategoryType = inputType === "category"; + const [entityColumns, setEntityColumns] = useState([]); const [loadingColumns, setLoadingColumns] = useState(false); - // 설정 업데이트 핸들러 + // 카테고리 값 목록 + const [categoryValues, setCategoryValues] = useState([]); + const [loadingCategoryValues, setLoadingCategoryValues] = useState(false); + const updateConfig = (field: string, value: any) => { onChange({ ...config, [field]: value }); }; + // 카테고리 타입이면 source를 자동으로 category로 설정 + useEffect(() => { + if (isCategoryType && config.source !== "category") { + onChange({ ...config, source: "category" }); + } + }, [isCategoryType]); + + // 카테고리 값 로드 + const loadCategoryValues = useCallback(async (catTable: string, catColumn: string) => { + if (!catTable || !catColumn) { + setCategoryValues([]); + return; + } + + setLoadingCategoryValues(true); + try { + const response = await apiClient.get(`/table-categories/${catTable}/${catColumn}/values`); + const data = response.data; + if (data.success && data.data) { + const flattenTree = (items: any[], depth: number = 0): CategoryValueOption[] => { + const result: CategoryValueOption[] = []; + for (const item of items) { + result.push({ + valueCode: item.valueCode, + valueLabel: depth > 0 ? `${" ".repeat(depth)}${item.valueLabel}` : item.valueLabel, + }); + if (item.children && item.children.length > 0) { + result.push(...flattenTree(item.children, depth + 1)); + } + } + return result; + }; + setCategoryValues(flattenTree(data.data)); + } + } catch (error) { + console.error("카테고리 값 조회 실패:", error); + setCategoryValues([]); + } finally { + setLoadingCategoryValues(false); + } + }, []); + + // 카테고리 소스일 때 값 로드 + useEffect(() => { + if (config.source === "category") { + const catTable = config.categoryTable || tableName; + const catColumn = config.categoryColumn || columnName; + if (catTable && catColumn) { + loadCategoryValues(catTable, catColumn); + } + } + }, [config.source, config.categoryTable, config.categoryColumn, tableName, columnName, loadCategoryValues]); + // 엔티티 테이블 변경 시 컬럼 목록 조회 - const loadEntityColumns = useCallback(async (tableName: string) => { - if (!tableName) { + const loadEntityColumns = useCallback(async (tblName: string) => { + if (!tblName) { setEntityColumns([]); return; } setLoadingColumns(true); try { - const response = await apiClient.get(`/table-management/tables/${tableName}/columns?size=500`); + const response = await apiClient.get(`/table-management/tables/${tblName}/columns?size=500`); const data = response.data.data || response.data; const columns = data.columns || data || []; const columnOptions: ColumnOption[] = columns.map((col: any) => { const name = col.columnName || col.column_name || col.name; - // displayName 우선 사용 const label = col.displayName || col.display_name || col.columnLabel || col.column_label || name; return { @@ -72,7 +142,6 @@ export const V2SelectConfigPanel: React.FC = ({ config } }, []); - // 엔티티 테이블이 변경되면 컬럼 목록 로드 useEffect(() => { if (config.source === "entity" && config.entityTable) { loadEntityColumns(config.entityTable); @@ -98,6 +167,9 @@ export const V2SelectConfigPanel: React.FC = ({ config updateConfig("options", newOptions); }; + // 현재 source 결정 (카테고리 타입이면 강제 category) + const effectiveSource = isCategoryType ? "category" : config.source || "static"; + return (
{/* 선택 모드 */} @@ -125,21 +197,102 @@ export const V2SelectConfigPanel: React.FC = ({ config {/* 데이터 소스 */}
- + {isCategoryType ? ( +
+ 카테고리 (자동 설정) +
+ ) : ( + + )}
+ {/* 카테고리 설정 */} + {effectiveSource === "category" && ( +
+
+ +
+
+
+

테이블

+

{config.categoryTable || tableName || "-"}

+
+
+

컬럼

+

{config.categoryColumn || columnName || "-"}

+
+
+
+
+ + {/* 카테고리 값 로딩 중 */} + {loadingCategoryValues && ( +
+ + 카테고리 값 로딩 중... +
+ )} + + {/* 카테고리 값 목록 표시 */} + {categoryValues.length > 0 && ( +
+ +
+ {categoryValues.map((cv) => ( +
+ {cv.valueCode} + {cv.valueLabel} +
+ ))} +
+
+ )} + + {/* 기본값 설정 */} + {categoryValues.length > 0 && ( +
+ + +

화면 로드 시 자동 선택될 카테고리 값

+
+ )} + + {/* 카테고리 값 없음 안내 */} + {!loadingCategoryValues && categoryValues.length === 0 && ( +

+ 카테고리 값이 없습니다. 테이블 카테고리 관리에서 값을 추가해주세요. +

+ )} +
+ )} + {/* 정적 옵션 관리 */} - {(config.source || "static") === "static" && ( + {effectiveSource === "static" && (
@@ -199,8 +352,8 @@ export const V2SelectConfigPanel: React.FC = ({ config
)} - {/* 공통 코드 설정 - 테이블 타입 관리에서 설정되므로 정보만 표시 */} - {config.source === "code" && ( + {/* 공통 코드 설정 */} + {effectiveSource === "code" && (
{config.codeGroup ? ( @@ -212,7 +365,7 @@ export const V2SelectConfigPanel: React.FC = ({ config )} {/* 엔티티(참조 테이블) 설정 */} - {config.source === "entity" && ( + {effectiveSource === "entity" && (
@@ -228,7 +381,6 @@ export const V2SelectConfigPanel: React.FC = ({ config

- {/* 컬럼 로딩 중 표시 */} {loadingColumns && (
@@ -236,7 +388,6 @@ export const V2SelectConfigPanel: React.FC = ({ config
)} - {/* 컬럼 선택 - 테이블이 설정되어 있고 컬럼 목록이 있는 경우 Select로 표시 */}
@@ -296,18 +447,17 @@ export const V2SelectConfigPanel: React.FC = ({ config
- {/* 컬럼이 없는 경우 안내 */} {config.entityTable && !loadingColumns && entityColumns.length === 0 && (

테이블 컬럼을 조회할 수 없습니다. 테이블 타입 관리에서 참조 테이블을 설정해주세요.

)} - {/* 자동 채움 안내 */} {config.entityTable && entityColumns.length > 0 && (

- 같은 폼에 참조 테이블({config.entityTable})의 컬럼이 배치되어 있으면, 엔티티 선택 시 해당 필드가 자동으로 채워집니다. + 같은 폼에 참조 테이블({config.entityTable})의 컬럼이 배치되어 있으면, 엔티티 선택 시 해당 필드가 자동으로 + 채워집니다.

)}