From f6a02b5182a4f0b3900dc8da9188ad995dcce1e7 Mon Sep 17 00:00:00 2001 From: kjs Date: Mon, 9 Mar 2026 13:46:38 +0900 Subject: [PATCH] refactor: Update references from table_column_category_values to category_values - Changed all occurrences of `table_column_category_values` to `category_values` in the backend services and controllers to standardize the terminology. - Updated SQL queries to reflect the new table name, ensuring proper data retrieval and management. - Adjusted comments and documentation to clarify the purpose of the `category_values` table in the context of category management. These changes enhance code clarity and maintain consistency across the application. --- .../categoryValueCascadingController.ts | 8 +- .../src/controllers/entityJoinController.ts | 4 +- .../src/services/entityJoinService.ts | 27 +-- backend-node/src/services/menuCopyService.ts | 6 +- .../src/services/tableCategoryValueService.ts | 158 +++++++++++------- .../src/services/tableManagementService.ts | 6 +- .../screen/InteractiveDataTable.tsx | 2 +- .../components/webtypes/RepeaterInput.tsx | 4 +- .../card-display/CardDisplayComponent.tsx | 2 +- .../modal-repeater-table/RepeaterTable.tsx | 2 +- .../SplitPanelLayoutComponent.tsx | 4 +- .../table-list/TableListComponent.tsx | 5 +- .../UniversalFormModalComponent.tsx | 2 +- .../modals/FieldDetailSettingsModal.tsx | 2 +- .../modals/TableSectionSettingsModal.tsx | 2 +- .../components/universal-form-modal/types.ts | 2 +- .../BomItemEditorComponent.tsx | 2 +- .../v2-bom-tree/BomTreeComponent.tsx | 2 +- .../v2-card-display/CardDisplayComponent.tsx | 2 +- .../SplitPanelLayoutComponent.tsx | 4 +- .../v2-table-list/TableListComponent.tsx | 5 +- 21 files changed, 144 insertions(+), 107 deletions(-) diff --git a/backend-node/src/controllers/categoryValueCascadingController.ts b/backend-node/src/controllers/categoryValueCascadingController.ts index 66250bf9..f57b6822 100644 --- a/backend-node/src/controllers/categoryValueCascadingController.ts +++ b/backend-node/src/controllers/categoryValueCascadingController.ts @@ -818,13 +818,13 @@ export const getCategoryValueCascadingParentOptions = async ( const group = groupResult.rows[0]; - // 부모 카테고리 값 조회 (table_column_category_values에서) + // 부모 카테고리 값 조회 (category_values에서) let optionsQuery = ` SELECT value_code as value, value_label as label, value_order as display_order - FROM table_column_category_values + FROM category_values WHERE table_name = $1 AND column_name = $2 AND is_active = true @@ -916,13 +916,13 @@ export const getCategoryValueCascadingChildOptions = async ( const group = groupResult.rows[0]; - // 자식 카테고리 값 조회 (table_column_category_values에서) + // 자식 카테고리 값 조회 (category_values에서) let optionsQuery = ` SELECT value_code as value, value_label as label, value_order as display_order - FROM table_column_category_values + FROM category_values WHERE table_name = $1 AND column_name = $2 AND is_active = true diff --git a/backend-node/src/controllers/entityJoinController.ts b/backend-node/src/controllers/entityJoinController.ts index 15e05473..de9ee95f 100644 --- a/backend-node/src/controllers/entityJoinController.ts +++ b/backend-node/src/controllers/entityJoinController.ts @@ -417,10 +417,10 @@ export class EntityJoinController { // 1. 현재 테이블의 Entity 조인 설정 조회 const allJoinConfigs = await entityJoinService.detectEntityJoins(tableName, undefined, companyCode); - // 🆕 화면 디자이너용: table_column_category_values는 카테고리 드롭다운용이므로 제외 + // 🆕 화면 디자이너용: category_values는 카테고리 드롭다운용이므로 제외 // 카테고리 값은 엔티티 조인 컬럼이 아니라 셀렉트박스 옵션으로 사용됨 const joinConfigs = allJoinConfigs.filter( - (config) => config.referenceTable !== "table_column_category_values" + (config) => config.referenceTable !== "category_values" ); if (joinConfigs.length === 0) { diff --git a/backend-node/src/services/entityJoinService.ts b/backend-node/src/services/entityJoinService.ts index a37942e1..1f345727 100644 --- a/backend-node/src/services/entityJoinService.ts +++ b/backend-node/src/services/entityJoinService.ts @@ -92,7 +92,7 @@ export class EntityJoinService { if (column.input_type === "category") { // 카테고리 타입: reference 정보가 비어있어도 자동 설정 - referenceTable = referenceTable || "table_column_category_values"; + referenceTable = referenceTable || "category_values"; referenceColumn = referenceColumn || "value_code"; displayColumn = displayColumn || "value_label"; @@ -308,7 +308,7 @@ export class EntityJoinService { const usedAliasesForColumns = new Set(); // joinConfigs를 참조 테이블 + 소스 컬럼별로 중복 제거하여 별칭 생성 - // (table_column_category_values는 같은 테이블이라도 sourceColumn마다 별도 JOIN 필요) + // (category_values는 같은 테이블이라도 sourceColumn마다 별도 JOIN 필요) const uniqueReferenceTableConfigs = joinConfigs.reduce((acc, config) => { if ( !acc.some( @@ -336,7 +336,7 @@ export class EntityJoinService { counter++; } usedAliasesForColumns.add(alias); - // 같은 테이블이라도 sourceColumn이 다르면 별도 별칭 생성 (table_column_category_values 대응) + // 같은 테이블이라도 sourceColumn이 다르면 별도 별칭 생성 (category_values 대응) const aliasKey = `${config.referenceTable}:${config.sourceColumn}`; aliasMap.set(aliasKey, alias); logger.info( @@ -455,9 +455,10 @@ export class EntityJoinService { const aliasKey = `${config.referenceTable}:${config.sourceColumn}`; const alias = aliasMap.get(aliasKey); - // table_column_category_values는 특별한 조인 조건 필요 (회사별 필터링) - if (config.referenceTable === "table_column_category_values") { - return `LEFT JOIN ${config.referenceTable} ${alias} ON main."${config.sourceColumn}"::TEXT = ${alias}."${config.referenceColumn}"::TEXT AND ${alias}.table_name = '${tableName}' AND ${alias}.column_name = '${config.sourceColumn}' AND ${alias}.company_code = main.company_code AND ${alias}.is_active = true`; + // category_values는 특별한 조인 조건 필요 (회사별 필터링) + // is_active 필터 제거: 비활성화된 카테고리도 라벨로 표시되어야 함 + if (config.referenceTable === "category_values") { + return `LEFT JOIN ${config.referenceTable} ${alias} ON main."${config.sourceColumn}"::TEXT = ${alias}."${config.referenceColumn}"::TEXT AND ${alias}.table_name = '${tableName}' AND ${alias}.column_name = '${config.sourceColumn}' AND ${alias}.company_code = main.company_code`; } // user_info는 전역 테이블이므로 company_code 조건 없이 조인 @@ -528,10 +529,10 @@ export class EntityJoinService { return "join"; } - // table_column_category_values는 특수 조인 조건이 필요하므로 캐시 불가 - if (config.referenceTable === "table_column_category_values") { + // category_values는 특수 조인 조건이 필요하므로 캐시 불가 + if (config.referenceTable === "category_values") { logger.info( - `🎯 table_column_category_values는 캐시 전략 불가: ${config.sourceColumn}` + `🎯 category_values는 캐시 전략 불가: ${config.sourceColumn}` ); return "join"; } @@ -723,10 +724,10 @@ export class EntityJoinService { const aliasKey = `${config.referenceTable}:${config.sourceColumn}`; const alias = aliasMap.get(aliasKey); - // table_column_category_values는 특별한 조인 조건 필요 (회사별 필터링만) - if (config.referenceTable === "table_column_category_values") { - // 멀티테넌시: 회사 데이터만 사용 (공통 데이터 제외) - return `LEFT JOIN ${config.referenceTable} ${alias} ON main."${config.sourceColumn}"::TEXT = ${alias}."${config.referenceColumn}"::TEXT AND ${alias}.table_name = '${tableName}' AND ${alias}.column_name = '${config.sourceColumn}' AND ${alias}.company_code = main.company_code AND ${alias}.is_active = true`; + // category_values는 특별한 조인 조건 필요 (회사별 필터링만) + // is_active 필터 제거: 비활성화된 카테고리도 라벨로 표시되어야 함 + if (config.referenceTable === "category_values") { + return `LEFT JOIN ${config.referenceTable} ${alias} ON main."${config.sourceColumn}"::TEXT = ${alias}."${config.referenceColumn}"::TEXT AND ${alias}.table_name = '${tableName}' AND ${alias}.column_name = '${config.sourceColumn}' AND ${alias}.company_code = main.company_code`; } return `LEFT JOIN ${config.referenceTable} ${alias} ON main."${config.sourceColumn}"::TEXT = ${alias}."${config.referenceColumn}"::TEXT`; diff --git a/backend-node/src/services/menuCopyService.ts b/backend-node/src/services/menuCopyService.ts index f67e09a3..af755316 100644 --- a/backend-node/src/services/menuCopyService.ts +++ b/backend-node/src/services/menuCopyService.ts @@ -3098,7 +3098,7 @@ export class MenuCopyService { } const allValuesResult = await client.query( - `SELECT * FROM table_column_category_values + `SELECT * FROM category_values WHERE company_code = $1 AND (${columnConditions.join(" OR ")}) ORDER BY depth NULLS FIRST, parent_value_id NULLS FIRST, value_order`, @@ -3115,7 +3115,7 @@ export class MenuCopyService { // 5. 대상 회사에 이미 존재하는 값 한 번에 조회 const existingValuesResult = await client.query( `SELECT value_id, table_name, column_name, value_code - FROM table_column_category_values WHERE company_code = $1`, + FROM category_values WHERE company_code = $1`, [targetCompanyCode] ); const existingValueKeys = new Map( @@ -3194,7 +3194,7 @@ export class MenuCopyService { }); const insertResult = await client.query( - `INSERT INTO table_column_category_values ( + `INSERT INTO category_values ( table_name, column_name, value_code, value_label, value_order, parent_value_id, depth, description, color, icon, is_active, is_default, created_at, created_by, company_code, menu_objid diff --git a/backend-node/src/services/tableCategoryValueService.ts b/backend-node/src/services/tableCategoryValueService.ts index dd2f73a9..96efdfbb 100644 --- a/backend-node/src/services/tableCategoryValueService.ts +++ b/backend-node/src/services/tableCategoryValueService.ts @@ -31,7 +31,7 @@ class TableCategoryValueService { tc.column_name AS "columnLabel", COUNT(cv.value_id) AS "valueCount" FROM table_type_columns tc - LEFT JOIN table_column_category_values cv + LEFT JOIN category_values cv ON tc.table_name = cv.table_name AND tc.column_name = cv.column_name AND cv.is_active = true @@ -50,7 +50,7 @@ class TableCategoryValueService { tc.column_name AS "columnLabel", COUNT(cv.value_id) AS "valueCount" FROM table_type_columns tc - LEFT JOIN table_column_category_values cv + LEFT JOIN category_values cv ON tc.table_name = cv.table_name AND tc.column_name = cv.column_name AND cv.is_active = true @@ -110,7 +110,7 @@ class TableCategoryValueService { ) tc LEFT JOIN ( SELECT table_name, column_name, COUNT(*) as cnt - FROM table_column_category_values + FROM category_values WHERE is_active = true GROUP BY table_name, column_name ) cv_count ON tc.table_name = cv_count.table_name AND tc.column_name = cv_count.column_name @@ -133,7 +133,7 @@ class TableCategoryValueService { ) tc LEFT JOIN ( SELECT table_name, column_name, COUNT(*) as cnt - FROM table_column_category_values + FROM category_values WHERE is_active = true AND company_code = $1 GROUP BY table_name, column_name ) cv_count ON tc.table_name = cv_count.table_name AND tc.column_name = cv_count.column_name @@ -207,7 +207,7 @@ class TableCategoryValueService { is_active AS "isActive", is_default AS "isDefault", company_code AS "companyCode", - NULL::numeric AS "menuObjid", + menu_objid AS "menuObjid", created_at AS "createdAt", updated_at AS "updatedAt", created_by AS "createdBy", @@ -289,7 +289,7 @@ class TableCategoryValueService { // 최고 관리자: 모든 회사에서 중복 체크 duplicateQuery = ` SELECT value_id - FROM table_column_category_values + FROM category_values WHERE table_name = $1 AND column_name = $2 AND value_code = $3 @@ -300,7 +300,7 @@ class TableCategoryValueService { // 일반 회사: 자신의 회사에서만 중복 체크 duplicateQuery = ` SELECT value_id - FROM table_column_category_values + FROM category_values WHERE table_name = $1 AND column_name = $2 AND value_code = $3 @@ -316,8 +316,41 @@ class TableCategoryValueService { throw new Error("이미 존재하는 코드입니다"); } + // 라벨 중복 체크 (같은 테이블+컬럼+회사에서 동일한 라벨명 방지) + let labelDupQuery: string; + let labelDupParams: any[]; + + if (companyCode === "*") { + labelDupQuery = ` + SELECT value_id + FROM category_values + WHERE table_name = $1 + AND column_name = $2 + AND value_label = $3 + AND is_active = true + `; + labelDupParams = [value.tableName, value.columnName, value.valueLabel]; + } else { + labelDupQuery = ` + SELECT value_id + FROM category_values + WHERE table_name = $1 + AND column_name = $2 + AND value_label = $3 + AND company_code = $4 + AND is_active = true + `; + labelDupParams = [value.tableName, value.columnName, value.valueLabel, companyCode]; + } + + const labelDupResult = await pool.query(labelDupQuery, labelDupParams); + + if (labelDupResult.rows.length > 0) { + throw new Error(`이미 동일한 이름의 카테고리 값이 존재합니다: "${value.valueLabel}"`); + } + const insertQuery = ` - INSERT INTO table_column_category_values ( + INSERT INTO category_values ( table_name, column_name, value_code, value_label, value_order, parent_value_id, depth, description, color, icon, is_active, is_default, company_code, menu_objid, created_by @@ -425,6 +458,32 @@ class TableCategoryValueService { values.push(updates.isDefault); } + // 라벨 수정 시 중복 체크 (자기 자신 제외) + if (updates.valueLabel !== undefined) { + const currentRow = await pool.query( + `SELECT table_name, column_name, company_code FROM category_values WHERE value_id = $1`, + [valueId] + ); + + if (currentRow.rows.length > 0) { + const { table_name, column_name, company_code } = currentRow.rows[0]; + const labelDupResult = await pool.query( + `SELECT value_id FROM category_values + WHERE table_name = $1 + AND column_name = $2 + AND value_label = $3 + AND company_code = $4 + AND is_active = true + AND value_id != $5`, + [table_name, column_name, updates.valueLabel, company_code, valueId] + ); + + if (labelDupResult.rows.length > 0) { + throw new Error(`이미 동일한 이름의 카테고리 값이 존재합니다: "${updates.valueLabel}"`); + } + } + } + setClauses.push(`updated_at = NOW()`); setClauses.push(`updated_by = $${paramIndex++}`); values.push(userId); @@ -436,7 +495,7 @@ class TableCategoryValueService { // 최고 관리자: 모든 카테고리 값 수정 가능 values.push(valueId); updateQuery = ` - UPDATE table_column_category_values + UPDATE category_values SET ${setClauses.join(", ")} WHERE value_id = $${paramIndex++} RETURNING @@ -459,7 +518,7 @@ class TableCategoryValueService { // 일반 회사: 자신의 카테고리 값만 수정 가능 values.push(valueId, companyCode); updateQuery = ` - UPDATE table_column_category_values + UPDATE category_values SET ${setClauses.join(", ")} WHERE value_id = $${paramIndex++} AND company_code = $${paramIndex++} @@ -516,14 +575,14 @@ class TableCategoryValueService { if (companyCode === "*") { valueQuery = ` SELECT table_name, column_name, value_code - FROM table_column_category_values + FROM category_values WHERE value_id = $1 `; valueParams = [valueId]; } else { valueQuery = ` SELECT table_name, column_name, value_code - FROM table_column_category_values + FROM category_values WHERE value_id = $1 AND company_code = $2 `; @@ -635,10 +694,10 @@ class TableCategoryValueService { if (companyCode === "*") { query = ` WITH RECURSIVE category_tree AS ( - SELECT value_id FROM table_column_category_values WHERE parent_value_id = $1 + SELECT value_id FROM category_values WHERE parent_value_id = $1 UNION ALL SELECT cv.value_id - FROM table_column_category_values cv + FROM category_values cv INNER JOIN category_tree ct ON cv.parent_value_id = ct.value_id ) SELECT value_id FROM category_tree @@ -647,11 +706,11 @@ class TableCategoryValueService { } else { query = ` WITH RECURSIVE category_tree AS ( - SELECT value_id FROM table_column_category_values + SELECT value_id FROM category_values WHERE parent_value_id = $1 AND company_code = $2 UNION ALL SELECT cv.value_id - FROM table_column_category_values cv + FROM category_values cv INNER JOIN category_tree ct ON cv.parent_value_id = ct.value_id WHERE cv.company_code = $2 ) @@ -697,10 +756,10 @@ class TableCategoryValueService { let labelParams: any[]; if (companyCode === "*") { - labelQuery = `SELECT value_label FROM table_column_category_values WHERE value_id = $1`; + labelQuery = `SELECT value_label FROM category_values WHERE value_id = $1`; labelParams = [id]; } else { - labelQuery = `SELECT value_label FROM table_column_category_values WHERE value_id = $1 AND company_code = $2`; + labelQuery = `SELECT value_label FROM category_values WHERE value_id = $1 AND company_code = $2`; labelParams = [id, companyCode]; } @@ -730,10 +789,10 @@ class TableCategoryValueService { let deleteParams: any[]; if (companyCode === "*") { - deleteQuery = `DELETE FROM table_column_category_values WHERE value_id = $1`; + deleteQuery = `DELETE FROM category_values WHERE value_id = $1`; deleteParams = [id]; } else { - deleteQuery = `DELETE FROM table_column_category_values WHERE value_id = $1 AND company_code = $2`; + deleteQuery = `DELETE FROM category_values WHERE value_id = $1 AND company_code = $2`; deleteParams = [id, companyCode]; } @@ -770,7 +829,7 @@ class TableCategoryValueService { if (companyCode === "*") { // 최고 관리자: 모든 카테고리 값 일괄 삭제 가능 deleteQuery = ` - UPDATE table_column_category_values + UPDATE category_values SET is_active = false, updated_at = NOW(), updated_by = $2 WHERE value_id = ANY($1::int[]) `; @@ -778,7 +837,7 @@ class TableCategoryValueService { } else { // 일반 회사: 자신의 카테고리 값만 일괄 삭제 가능 deleteQuery = ` - UPDATE table_column_category_values + UPDATE category_values SET is_active = false, updated_at = NOW(), updated_by = $3 WHERE value_id = ANY($1::int[]) AND company_code = $2 @@ -819,7 +878,7 @@ class TableCategoryValueService { if (companyCode === "*") { // 최고 관리자: 모든 카테고리 값 순서 변경 가능 updateQuery = ` - UPDATE table_column_category_values + UPDATE category_values SET value_order = $1, updated_at = NOW() WHERE value_id = $2 `; @@ -827,7 +886,7 @@ class TableCategoryValueService { } else { // 일반 회사: 자신의 카테고리 값만 순서 변경 가능 updateQuery = ` - UPDATE table_column_category_values + UPDATE category_values SET value_order = $1, updated_at = NOW() WHERE value_id = $2 AND company_code = $3 @@ -1379,48 +1438,23 @@ class TableCategoryValueService { let query: string; let params: any[]; + // is_active 필터 제거: 비활성화된 카테고리도 라벨로 표시되어야 함 if (companyCode === "*") { - // 최고 관리자: 두 테이블 모두에서 조회 (UNION으로 병합) - // 두 번째 쿼리용 플레이스홀더: $n+1 ~ $2n - const placeholders2 = valueCodes.map((_, i) => `$${n + i + 1}`).join(", "); query = ` - SELECT value_code, value_label FROM ( - SELECT value_code, value_label - FROM table_column_category_values - WHERE value_code IN (${placeholders1}) - AND is_active = true - UNION ALL - SELECT value_code, value_label - FROM category_values - WHERE value_code IN (${placeholders2}) - AND is_active = true - ) combined + SELECT DISTINCT value_code, value_label + FROM category_values + WHERE value_code IN (${placeholders1}) `; - params = [...valueCodes, ...valueCodes]; + params = [...valueCodes]; } else { - // 일반 회사: 두 테이블에서 자신의 카테고리 값 + 공통 카테고리 값 조회 - // 첫 번째: $1~$n (valueCodes), $n+1 (companyCode) - // 두 번째: $n+2~$2n+1 (valueCodes), $2n+2 (companyCode) - const companyIdx1 = n + 1; - const placeholders2 = valueCodes.map((_, i) => `$${n + 1 + i + 1}`).join(", "); - const companyIdx2 = 2 * n + 2; - + const companyIdx = n + 1; query = ` - SELECT value_code, value_label FROM ( - SELECT value_code, value_label - FROM table_column_category_values - WHERE value_code IN (${placeholders1}) - AND is_active = true - AND (company_code = $${companyIdx1} OR company_code = '*') - UNION ALL - SELECT value_code, value_label - FROM category_values - WHERE value_code IN (${placeholders2}) - AND is_active = true - AND (company_code = $${companyIdx2} OR company_code = '*') - ) combined + SELECT DISTINCT value_code, value_label + FROM category_values + WHERE value_code IN (${placeholders1}) + AND (company_code = $${companyIdx} OR company_code = '*') `; - params = [...valueCodes, companyCode, ...valueCodes, companyCode]; + params = [...valueCodes, companyCode]; } const result = await pool.query(query, params); @@ -1488,7 +1522,7 @@ class TableCategoryValueService { // 최고 관리자: 모든 카테고리 값 조회 query = ` SELECT value_code, value_label - FROM table_column_category_values + FROM category_values WHERE table_name = $1 AND column_name = $2 AND is_active = true @@ -1498,7 +1532,7 @@ class TableCategoryValueService { // 일반 회사: 자신의 카테고리 값 + 공통 카테고리 값 조회 query = ` SELECT value_code, value_label - FROM table_column_category_values + FROM category_values WHERE table_name = $1 AND column_name = $2 AND is_active = true diff --git a/backend-node/src/services/tableManagementService.ts b/backend-node/src/services/tableManagementService.ts index ed7ad460..0cff4f6b 100644 --- a/backend-node/src/services/tableManagementService.ts +++ b/backend-node/src/services/tableManagementService.ts @@ -3505,7 +3505,7 @@ export class TableManagementService { const referenceTableColumns = new Map(); const uniqueRefTables = new Set( joinConfigs - .filter((c) => c.referenceTable !== "table_column_category_values") // 카테고리는 제외 + .filter((c) => c.referenceTable !== "category_values") // 카테고리는 제외 .map((c) => `${c.referenceTable}:${c.sourceColumn}`) ); @@ -4310,8 +4310,8 @@ export class TableManagementService { ]; for (const config of joinConfigs) { - // table_column_category_values는 특수 조인 조건이 필요하므로 항상 DB 조인 - if (config.referenceTable === "table_column_category_values") { + // category_values는 특수 조인 조건이 필요하므로 항상 DB 조인 + if (config.referenceTable === "category_values") { dbJoins.push(config); console.log(`🔗 DB 조인 (특수 조건): ${config.referenceTable}`); continue; diff --git a/frontend/components/screen/InteractiveDataTable.tsx b/frontend/components/screen/InteractiveDataTable.tsx index 8efde578..2de4d0df 100644 --- a/frontend/components/screen/InteractiveDataTable.tsx +++ b/frontend/components/screen/InteractiveDataTable.tsx @@ -378,7 +378,7 @@ export const InteractiveDataTable: React.FC = ({ for (const col of categoryColumns) { try { // menuObjid가 있으면 쿼리 파라미터로 전달 (메뉴별 카테고리 색상 적용) - const queryParams = menuObjid ? `?menuObjid=${menuObjid}` : ""; + const queryParams = menuObjid ? `?menuObjid=${menuObjid}&includeInactive=true` : "?includeInactive=true"; const response = await apiClient.get( `/table-categories/${component.tableName}/${col.columnName}/values${queryParams}`, ); diff --git a/frontend/components/webtypes/RepeaterInput.tsx b/frontend/components/webtypes/RepeaterInput.tsx index 050b386b..0349c6c3 100644 --- a/frontend/components/webtypes/RepeaterInput.tsx +++ b/frontend/components/webtypes/RepeaterInput.tsx @@ -796,7 +796,7 @@ export const RepeaterInput: React.FC = ({ console.log(`📡 [RepeaterInput] 카테고리 매핑 로드: ${tableName}/${columnName}`); - const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values`); + const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values?includeInactive=true`); if (response.data.success && response.data.data && Array.isArray(response.data.data)) { const mapping: Record = {}; @@ -838,7 +838,7 @@ export const RepeaterInput: React.FC = ({ try { console.log(`📡 [RepeaterInput] 조인 테이블 카테고리 매핑 로드: ${joinedTableName}/${columnName}`); - const response = await apiClient.get(`/table-categories/${joinedTableName}/${columnName}/values`); + const response = await apiClient.get(`/table-categories/${joinedTableName}/${columnName}/values?includeInactive=true`); if (response.data.success && response.data.data && Array.isArray(response.data.data)) { const mapping: Record = {}; diff --git a/frontend/lib/registry/components/card-display/CardDisplayComponent.tsx b/frontend/lib/registry/components/card-display/CardDisplayComponent.tsx index e8afb3b3..1ff0088f 100644 --- a/frontend/lib/registry/components/card-display/CardDisplayComponent.tsx +++ b/frontend/lib/registry/components/card-display/CardDisplayComponent.tsx @@ -367,7 +367,7 @@ export const CardDisplayComponent: React.FC = ({ for (const columnName of categoryColumns) { try { - const response = await apiClient.get(`/table-categories/${tableNameToUse}/${columnName}/values`); + const response = await apiClient.get(`/table-categories/${tableNameToUse}/${columnName}/values?includeInactive=true`); if (response.data.success && response.data.data) { diff --git a/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx b/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx index 532881b7..3fbfdf71 100644 --- a/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx +++ b/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx @@ -133,7 +133,7 @@ export function RepeaterTable({ continue; } - const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values`); + const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values?includeInactive=true`); if (response.data?.success && response.data.data) { const options = response.data.data.map((item: any) => ({ diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index bc4ba2ba..a12e4bce 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -1588,7 +1588,7 @@ export const SplitPanelLayoutComponent: React.FC for (const col of categoryColumns) { const columnName = col.columnName || col.column_name; try { - const response = await apiClient.get(`/table-categories/${leftTableName}/${columnName}/values`); + const response = await apiClient.get(`/table-categories/${leftTableName}/${columnName}/values?includeInactive=true`); if (response.data.success && response.data.data) { const valueMap: Record = {}; @@ -1650,7 +1650,7 @@ export const SplitPanelLayoutComponent: React.FC for (const col of categoryColumns) { const columnName = col.columnName || col.column_name; try { - const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values`); + const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values?includeInactive=true`); if (response.data.success && response.data.data) { const valueMap: Record = {}; diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index c14f28f6..a01e88ed 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -1298,7 +1298,8 @@ export const TableListComponent: React.FC = ({ targetColumn = parts[1]; } - const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values`); + // 비활성화된 카테고리도 라벨로 표시하기 위해 includeInactive=true + const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values?includeInactive=true`); if (response.data.success && response.data.data && Array.isArray(response.data.data)) { const mapping: Record = {}; @@ -1381,7 +1382,7 @@ export const TableListComponent: React.FC = ({ // inputType이 category인 경우 카테고리 매핑 로드 if (inputTypeInfo?.inputType === "category" && !mappings[col.columnName]) { try { - const response = await apiClient.get(`/table-categories/${joinedTable}/${col.actualColumn}/values`); + const response = await apiClient.get(`/table-categories/${joinedTable}/${col.actualColumn}/values?includeInactive=true`); if (response.data.success && response.data.data && Array.isArray(response.data.data)) { const mapping: Record = {}; diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx index 94149e4f..d0961021 100644 --- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx +++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx @@ -1362,7 +1362,7 @@ export function UniversalFormModalComponent({ label: String(row[optionConfig.labelColumn || "name"]), })); } else if (optionConfig.type === "code" && optionConfig.categoryKey) { - // 공통코드(카테고리 컬럼): table_column_category_values 테이블에서 조회 + // 공통코드(카테고리 컬럼): category_values 테이블에서 조회 // categoryKey 형식: "tableName.columnName" const [categoryTable, categoryColumn] = optionConfig.categoryKey.split("."); if (categoryTable && categoryColumn) { diff --git a/frontend/lib/registry/components/universal-form-modal/modals/FieldDetailSettingsModal.tsx b/frontend/lib/registry/components/universal-form-modal/modals/FieldDetailSettingsModal.tsx index afec5b6c..954d0192 100644 --- a/frontend/lib/registry/components/universal-form-modal/modals/FieldDetailSettingsModal.tsx +++ b/frontend/lib/registry/components/universal-form-modal/modals/FieldDetailSettingsModal.tsx @@ -31,7 +31,7 @@ import { import { apiClient } from "@/lib/api/client"; import { getCascadingRelations, getCascadingRelationByCode, CascadingRelation } from "@/lib/api/cascadingRelation"; -// 카테고리 컬럼 타입 (table_column_category_values 용) +// 카테고리 컬럼 타입 (category_values 용) interface CategoryColumnOption { tableName: string; columnName: string; diff --git a/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx b/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx index 7bda67b2..3fc81fa3 100644 --- a/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx +++ b/frontend/lib/registry/components/universal-form-modal/modals/TableSectionSettingsModal.tsx @@ -2526,7 +2526,7 @@ interface TableSectionSettingsModalProps { tables: { table_name: string; comment?: string }[]; tableColumns: Record; onLoadTableColumns: (tableName: string) => void; - // 카테고리 목록 (table_column_category_values에서 가져옴) + // 카테고리 목록 (category_values에서 가져옴) categoryList?: { tableName: string; columnName: string; displayName?: string }[]; onLoadCategoryList?: () => void; // 전체 섹션 목록 (다른 섹션 필드 참조용) diff --git a/frontend/lib/registry/components/universal-form-modal/types.ts b/frontend/lib/registry/components/universal-form-modal/types.ts index c6673d8d..8047d4f1 100644 --- a/frontend/lib/registry/components/universal-form-modal/types.ts +++ b/frontend/lib/registry/components/universal-form-modal/types.ts @@ -16,7 +16,7 @@ export interface SelectOptionConfig { labelColumn?: string; // 표시할 컬럼 (화면에 보여줄 텍스트) saveColumn?: string; // 저장할 컬럼 (실제로 DB에 저장할 값, 미지정 시 valueColumn 사용) filterCondition?: string; - // 카테고리 컬럼 기반 옵션 (table_column_category_values 테이블) + // 카테고리 컬럼 기반 옵션 (category_values 테이블) // 형식: "tableName.columnName" (예: "sales_order_mng.incoterms") categoryKey?: string; diff --git a/frontend/lib/registry/components/v2-bom-item-editor/BomItemEditorComponent.tsx b/frontend/lib/registry/components/v2-bom-item-editor/BomItemEditorComponent.tsx index bd5f3d92..097c42c4 100644 --- a/frontend/lib/registry/components/v2-bom-item-editor/BomItemEditorComponent.tsx +++ b/frontend/lib/registry/components/v2-bom-item-editor/BomItemEditorComponent.tsx @@ -540,7 +540,7 @@ export function BomItemEditorComponent({ if (alreadyLoaded) continue; try { - const response = await apiClient.get(`/table-categories/${mainTableName}/${col.key}/values`); + const response = await apiClient.get(`/table-categories/${mainTableName}/${col.key}/values?includeInactive=true`); if (response.data?.success && response.data.data) { const options = response.data.data.map((item: any) => ({ value: item.valueCode || item.value_code, diff --git a/frontend/lib/registry/components/v2-bom-tree/BomTreeComponent.tsx b/frontend/lib/registry/components/v2-bom-tree/BomTreeComponent.tsx index e98dbf88..8d70f5c1 100644 --- a/frontend/lib/registry/components/v2-bom-tree/BomTreeComponent.tsx +++ b/frontend/lib/registry/components/v2-bom-tree/BomTreeComponent.tsx @@ -146,7 +146,7 @@ export function BomTreeComponent({ useEffect(() => { const loadLabels = async () => { try { - const res = await apiClient.get(`/table-categories/${detailTable}/process_type/values`); + const res = await apiClient.get(`/table-categories/${detailTable}/process_type/values?includeInactive=true`); const vals = res.data?.data || []; if (vals.length > 0) { const map: Record = {}; diff --git a/frontend/lib/registry/components/v2-card-display/CardDisplayComponent.tsx b/frontend/lib/registry/components/v2-card-display/CardDisplayComponent.tsx index ad6c88db..12eff3ab 100644 --- a/frontend/lib/registry/components/v2-card-display/CardDisplayComponent.tsx +++ b/frontend/lib/registry/components/v2-card-display/CardDisplayComponent.tsx @@ -367,7 +367,7 @@ export const CardDisplayComponent: React.FC = ({ for (const columnName of categoryColumns) { try { - const response = await apiClient.get(`/table-categories/${tableNameToUse}/${columnName}/values`); + const response = await apiClient.get(`/table-categories/${tableNameToUse}/${columnName}/values?includeInactive=true`); if (response.data.success && response.data.data) { diff --git a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx index 865e6c44..2bfc7b19 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx @@ -1894,7 +1894,7 @@ export const SplitPanelLayoutComponent: React.FC for (const col of categoryColumns) { const columnName = col.columnName || col.column_name; try { - const response = await apiClient.get(`/table-categories/${leftTableName}/${columnName}/values`); + const response = await apiClient.get(`/table-categories/${leftTableName}/${columnName}/values?includeInactive=true`); if (response.data.success && response.data.data) { const valueMap: Record = {}; @@ -1972,7 +1972,7 @@ export const SplitPanelLayoutComponent: React.FC for (const col of categoryColumns) { const columnName = col.columnName || col.column_name; try { - const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values`); + const response = await apiClient.get(`/table-categories/${tableName}/${columnName}/values?includeInactive=true`); if (response.data.success && response.data.data) { const valueMap: Record = {}; diff --git a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx index eae50795..4087be04 100644 --- a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx @@ -1441,7 +1441,8 @@ export const TableListComponent: React.FC = ({ targetColumn = parts[1]; } - const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values`); + // 비활성화된 카테고리도 라벨로 표시하기 위해 includeInactive=true + const response = await apiClient.get(`/table-categories/${targetTable}/${targetColumn}/values?includeInactive=true`); if (response.data.success && response.data.data && Array.isArray(response.data.data)) { const mapping: Record = {}; @@ -1524,7 +1525,7 @@ export const TableListComponent: React.FC = ({ // inputType이 category인 경우 카테고리 매핑 로드 if (inputTypeInfo?.inputType === "category" && !mappings[col.columnName]) { try { - const response = await apiClient.get(`/table-categories/${joinedTable}/${col.actualColumn}/values`); + const response = await apiClient.get(`/table-categories/${joinedTable}/${col.actualColumn}/values?includeInactive=true`); if (response.data.success && response.data.data && Array.isArray(response.data.data)) { const mapping: Record = {};