From eb61506acd4cc3304d54717113b35f5e90cb76d2 Mon Sep 17 00:00:00 2001 From: kjs Date: Tue, 6 Jan 2026 13:43:47 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B6=84=ED=95=A0=ED=8C=A8=EB=84=90=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EB=8F=99=EC=9E=91=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/tableManagementController.ts | 10 +- .../TableSectionRenderer.tsx | 19 +- .../UniversalFormModalComponent.tsx | 219 ++++++++++++++++++ 3 files changed, 242 insertions(+), 6 deletions(-) diff --git a/backend-node/src/controllers/tableManagementController.ts b/backend-node/src/controllers/tableManagementController.ts index 7c84898b..83384be6 100644 --- a/backend-node/src/controllers/tableManagementController.ts +++ b/backend-node/src/controllers/tableManagementController.ts @@ -775,7 +775,8 @@ export async function getTableData( const userField = autoFilter?.userField || "companyCode"; const userValue = (req.user as any)[userField]; - if (userValue) { + // ๐Ÿ†• ์ตœ๊ณ  ๊ด€๋ฆฌ์ž(company_code = '*')๋Š” ๋ชจ๋“  ํšŒ์‚ฌ ๋ฐ์ดํ„ฐ ์กฐํšŒ ๊ฐ€๋Šฅ + if (userValue && userValue !== "*") { enhancedSearch[filterColumn] = userValue; logger.info("๐Ÿ” ํ˜„์žฌ ์‚ฌ์šฉ์ž ํ•„ํ„ฐ ์ ์šฉ:", { @@ -784,6 +785,10 @@ export async function getTableData( userValue, tableName, }); + } else if (userValue === "*") { + logger.info("๐Ÿ”“ ์ตœ๊ณ  ๊ด€๋ฆฌ์ž - ํšŒ์‚ฌ ํ•„ํ„ฐ ๋ฏธ์ ์šฉ (๋ชจ๋“  ํšŒ์‚ฌ ๋ฐ์ดํ„ฐ ์กฐํšŒ)", { + tableName, + }); } else { logger.warn("โš ๏ธ ์‚ฌ์šฉ์ž ์ •๋ณด ํ•„๋“œ ๊ฐ’ ์—†์Œ:", { userField, @@ -792,6 +797,9 @@ export async function getTableData( } } + // ๐Ÿ†• ์ตœ์ข… ๊ฒ€์ƒ‰ ์กฐ๊ฑด ๋กœ๊ทธ + logger.info(`๐Ÿ” ์ตœ์ข… ๊ฒ€์ƒ‰ ์กฐ๊ฑด (enhancedSearch):`, JSON.stringify(enhancedSearch)); + // ๋ฐ์ดํ„ฐ ์กฐํšŒ const result = await tableManagementService.getTableData(tableName, { page: parseInt(page), diff --git a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx index b4c6d2d8..9c238b47 100644 --- a/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx +++ b/frontend/lib/registry/components/universal-form-modal/TableSectionRenderer.tsx @@ -816,14 +816,23 @@ export function TableSectionRenderer({ // ์ด๋ฏธ ์ดˆ๊ธฐํ™”๋˜์—ˆ์œผ๋ฉด ์Šคํ‚ต if (initialDataLoadedRef.current) return; - const tableSectionKey = `_tableSection_${sectionId}`; + const tableSectionKey = `__tableSection_${sectionId}`; const initialData = formData[tableSectionKey]; + console.log("[TableSectionRenderer] ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ํ™•์ธ:", { + sectionId, + tableSectionKey, + hasInitialData: !!initialData, + initialDataLength: Array.isArray(initialData) ? initialData.length : 0, + formDataKeys: Object.keys(formData).filter(k => k.startsWith("__tableSection_")), + }); + if (Array.isArray(initialData) && initialData.length > 0) { - // console.log("[TableSectionRenderer] ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ๋กœ๋“œ:", { - // sectionId, - // itemCount: initialData.length, - // }); + console.log("[TableSectionRenderer] ์ดˆ๊ธฐ ๋ฐ์ดํ„ฐ ๋กœ๋“œ:", { + sectionId, + itemCount: initialData.length, + firstItem: initialData[0], + }); setTableData(initialData); initialDataLoadedRef.current = true; diff --git a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx index 3ba30347..a605c45e 100644 --- a/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx +++ b/frontend/lib/registry/components/universal-form-modal/UniversalFormModalComponent.tsx @@ -588,6 +588,225 @@ export function UniversalFormModalComponent({ } } + // ๐Ÿ†• ํ…Œ์ด๋ธ” ์„น์…˜(type: "table") ๋””ํ…Œ์ผ ๋ฐ์ดํ„ฐ ๋กœ๋“œ (๋งˆ์Šคํ„ฐ-๋””ํ…Œ์ผ ๊ตฌ์กฐ) + // ์ˆ˜์ • ๋ชจ๋“œ์ผ ๋•Œ ๋””ํ…Œ์ผ ํ…Œ์ด๋ธ”์—์„œ ๋ฐ์ดํ„ฐ ๊ฐ€์ ธ์˜ค๊ธฐ + if (effectiveInitialData) { + console.log("[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ๋””ํ…Œ์ผ ๋กœ๋“œ ์‹œ์ž‘", { + sectionsCount: config.sections.length, + effectiveInitialDataKeys: Object.keys(effectiveInitialData), + }); + + for (const section of config.sections) { + if (section.type !== "table" || !section.tableConfig) { + continue; + } + + const tableConfig = section.tableConfig; + const editConfig = tableConfig.editConfig; + const saveConfig = tableConfig.saveConfig; + + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id} ๊ฒ€์‚ฌ:`, { + hasEditConfig: !!editConfig, + loadOnEdit: editConfig?.loadOnEdit, + hasSaveConfig: !!saveConfig, + targetTable: saveConfig?.targetTable, + linkColumn: editConfig?.linkColumn, + }); + + // ์ˆ˜์ • ๋ชจ๋“œ ๋กœ๋“œ ์„ค์ • ํ™•์ธ (๊ธฐ๋ณธ๊ฐ’: true) + if (editConfig?.loadOnEdit === false) { + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: loadOnEdit=false, ์Šคํ‚ต`); + continue; + } + + // ๋””ํ…Œ์ผ ํ…Œ์ด๋ธ”๊ณผ ์—ฐ๊ฒฐ ์ •๋ณด ํ™•์ธ + const detailTable = saveConfig?.targetTable; + let linkColumn = editConfig?.linkColumn; + + if (!detailTable) { + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: saveConfig.targetTable ๋ฏธ์„ค์ •, ์Šคํ‚ต`); + continue; + } + + // linkColumn์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์œผ๋ฉด, ๋””ํ…Œ์ผ ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ ์ •๋ณด ์กฐํšŒํ•˜์—ฌ ์ž๋™ ๊ฐ์ง€ + if (!linkColumn?.masterField || !linkColumn?.detailField) { + try { + // ๋งˆ์Šคํ„ฐ ํ…Œ์ด๋ธ”๋ช… ํ™•์ธ (saveConfig์—์„œ) + // 1. customApiSave.multiTable.mainTable.tableName (๋‹ค์ค‘ ํ…Œ์ด๋ธ” ์ €์žฅ) + // 2. saveConfig.tableName (๋‹จ์ผ ํ…Œ์ด๋ธ” ์ €์žฅ) + const masterTable = config.saveConfig?.customApiSave?.multiTable?.mainTable?.tableName + || config.saveConfig?.tableName; + + // ๋””ํ…Œ์ผ ํ…Œ์ด๋ธ”์˜ ์ปฌ๋Ÿผ ๋ชฉ๋ก ์กฐํšŒ + const columnsResponse = await apiClient.get(`/table-management/tables/${detailTable}/columns`); + + if (columnsResponse.data?.success && columnsResponse.data?.data) { + // API ์‘๋‹ต ๊ตฌ์กฐ: { success, data: { columns: [...], total, page, ... } } + const columnsArray = columnsResponse.data.data.columns || columnsResponse.data.data || []; + const detailColumnsData = Array.isArray(columnsArray) ? columnsArray : []; + const detailColumns = detailColumnsData.map((col: any) => col.column_name || col.columnName); + const masterKeys = Object.keys(effectiveInitialData); + + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: ์—ฐ๊ฒฐ ํ•„๋“œ ์ž๋™ ๊ฐ์ง€`, { + masterTable, + detailTable, + detailColumnsCount: detailColumnsData.length, + }); + + // ๋ฐฉ๋ฒ• 1: ์—”ํ‹ฐํ‹ฐ ๊ด€๊ณ„ ๊ธฐ๋ฐ˜ ๊ฐ์ง€ (์ •ํ™•) + // ๋””ํ…Œ์ผ ํ…Œ์ด๋ธ”์—์„œ ๋งˆ์Šคํ„ฐ ํ…Œ์ด๋ธ”์„ ์ฐธ์กฐํ•˜๋Š” ์—”ํ‹ฐํ‹ฐ ์ปฌ๋Ÿผ ์ฐพ๊ธฐ + if (masterTable) { + for (const col of detailColumnsData) { + const colName = col.column_name || col.columnName; + const inputType = col.input_type || col.inputType; + + // ์—”ํ‹ฐํ‹ฐ ํƒ€์ž… ์ปฌ๋Ÿผ ํ™•์ธ + if (inputType === "entity") { + // reference_table ๋˜๋Š” detail_settings์—์„œ ์ฐธ์กฐ ํ…Œ์ด๋ธ” ํ™•์ธ + let refTable = col.reference_table || col.referenceTable; + + // detail_settings์—์„œ referenceTable ํ™•์ธ + if (!refTable && col.detail_settings) { + try { + const settings = typeof col.detail_settings === "string" + ? JSON.parse(col.detail_settings) + : col.detail_settings; + refTable = settings.referenceTable; + } catch { + // JSON ํŒŒ์‹ฑ ์‹คํŒจ ๋ฌด์‹œ + } + } + + // ๋งˆ์Šคํ„ฐ ํ…Œ์ด๋ธ”์„ ์ฐธ์กฐํ•˜๋Š” ์ปฌ๋Ÿผ ๋ฐœ๊ฒฌ + if (refTable === masterTable) { + // ์ฐธ์กฐ ์ปฌ๋Ÿผ ํ™•์ธ (๋งˆ์Šคํ„ฐ ํ…Œ์ด๋ธ”์˜ ์–ด๋–ค ์ปฌ๋Ÿผ์„ ์ฐธ์กฐํ•˜๋Š”์ง€) + let refColumn = col.reference_column || col.referenceColumn; + if (!refColumn && col.detail_settings) { + try { + const settings = typeof col.detail_settings === "string" + ? JSON.parse(col.detail_settings) + : col.detail_settings; + refColumn = settings.referenceColumn; + } catch { + // JSON ํŒŒ์‹ฑ ์‹คํŒจ ๋ฌด์‹œ + } + } + + // ๋งˆ์Šคํ„ฐ ๋ฐ์ดํ„ฐ์— ํ•ด๋‹น ์ปฌ๋Ÿผ ๊ฐ’์ด ์žˆ๋Š”์ง€ ํ™•์ธ + if (refColumn && effectiveInitialData[refColumn]) { + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: ์—”ํ‹ฐํ‹ฐ ๊ด€๊ณ„ ๊ฐ์ง€ - ${colName} โ†’ ${masterTable}.${refColumn}`); + linkColumn = { masterField: refColumn, detailField: colName }; + break; + } + } + } + } + } + + // ๋ฐฉ๋ฒ• 2: ๊ณตํ†ต ์ปฌ๋Ÿผ ํŒจํ„ด ๊ธฐ๋ฐ˜ ๊ฐ์ง€ (ํด๋ฐฑ) + // ์—”ํ‹ฐํ‹ฐ ๊ด€๊ณ„๊ฐ€ ์—†์œผ๋ฉด ๊ณตํ†ต ์ปฌ๋Ÿผ๋ช… ํŒจํ„ด์œผ๋กœ ์ฐพ๊ธฐ + if (!linkColumn) { + const priorityPatterns = ["_no", "_number", "_code", "_id"]; + + for (const pattern of priorityPatterns) { + for (const masterKey of masterKeys) { + if (masterKey.endsWith(pattern) && + detailColumns.includes(masterKey) && + effectiveInitialData[masterKey] && + masterKey !== "id" && masterKey !== "company_code") { + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: ๊ณตํ†ต ์ปฌ๋Ÿผ ํŒจํ„ด ๊ฐ์ง€ - ${masterKey}`); + linkColumn = { masterField: masterKey, detailField: masterKey }; + break; + } + } + if (linkColumn) break; + } + } + + // ๋ฐฉ๋ฒ• 3: ์ผ๋ฐ˜ ๊ณตํ†ต ์ปฌ๋Ÿผ (๋งˆ์ง€๋ง‰ ํด๋ฐฑ) + if (!linkColumn) { + for (const masterKey of masterKeys) { + if (detailColumns.includes(masterKey) && + effectiveInitialData[masterKey] && + masterKey !== "id" && masterKey !== "company_code" && + !masterKey.startsWith("__")) { + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: ๊ณตํ†ต ์ปฌ๋Ÿผ ๊ฐ์ง€ - ${masterKey}`); + linkColumn = { masterField: masterKey, detailField: masterKey }; + break; + } + } + } + } + } catch (error) { + console.warn(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: ์ปฌ๋Ÿผ ์ •๋ณด ์กฐํšŒ ์‹คํŒจ`, error); + } + } + + if (!linkColumn?.masterField || !linkColumn?.detailField) { + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: linkColumn ๋ฏธ์„ค์ • ๋ฐ ์ž๋™ ๊ฐ์ง€ ์‹คํŒจ, ์Šคํ‚ต`); + continue; + } + + // ๋งˆ์Šคํ„ฐ ํ…Œ์ด๋ธ”์˜ ์—ฐ๊ฒฐ ํ•„๋“œ ๊ฐ’ ๊ฐ€์ ธ์˜ค๊ธฐ + const masterValue = effectiveInitialData[linkColumn.masterField]; + if (!masterValue) { + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: masterField(${linkColumn.masterField}) ๊ฐ’ ์—†์Œ, ์Šคํ‚ต`); + continue; + } + + try { + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: ๋””ํ…Œ์ผ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹œ์ž‘`, { + detailTable, + linkColumn, + masterValue, + }); + + // ๋””ํ…Œ์ผ ํ…Œ์ด๋ธ”์—์„œ ๋ฐ์ดํ„ฐ ์กฐํšŒ + // operator: "equals"๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” ๊ฐ’๋งŒ ๊ฒ€์ƒ‰ (์—”ํ‹ฐํ‹ฐ ํƒ€์ž… ์ปฌ๋Ÿผ์—์„œ ์ค‘์š”) + const searchCondition: Record = { + [linkColumn.detailField]: { value: masterValue, operator: "equals" }, + }; + + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: API ์š”์ฒญ - URL: /table-management/tables/${detailTable}/data`); + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: API ์š”์ฒญ - search:`, JSON.stringify(searchCondition)); + + const response = await apiClient.post(`/table-management/tables/${detailTable}/data`, { + search: searchCondition, // filters๊ฐ€ ์•„๋‹Œ search๋กœ ์ „๋‹ฌ + page: 1, + size: 1000, // pageSize๊ฐ€ ์•„๋‹Œ size๋กœ ์ „๋‹ฌ + autoFilter: { enabled: true }, // ๋ฉ€ํ‹ฐํ…Œ๋„Œ์‹œ ํ•„ํ„ฐ ์ ์šฉ + }); + + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: API ์‘๋‹ต - success: ${response.data?.success}, total: ${response.data?.data?.total}, dataLength: ${response.data?.data?.data?.length}`); + + if (response.data?.success) { + // ๋‹ค์–‘ํ•œ ์‘๋‹ต ๊ตฌ์กฐ ์ฒ˜๋ฆฌ + let items: any[] = []; + const data = response.data.data; + + if (Array.isArray(data)) { + items = data; + } else if (data?.items && Array.isArray(data.items)) { + items = data.items; + } else if (data?.rows && Array.isArray(data.rows)) { + items = data.rows; + } else if (data?.data && Array.isArray(data.data)) { + items = data.data; + } + + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: ${items.length}๊ฑด ๋กœ๋“œ๋จ`, items); + + // ํ…Œ์ด๋ธ” ์„น์…˜ ๋ฐ์ดํ„ฐ๋ฅผ formData์— ์ €์žฅ (TableSectionRenderer์—์„œ ์‚ฌ์šฉ) + const tableSectionKey = `__tableSection_${section.id}`; + newFormData[tableSectionKey] = items; + console.log(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: formData[${tableSectionKey}]์— ์ €์žฅ๋จ`); + } + } catch (error) { + console.error(`[initializeForm] ํ…Œ์ด๋ธ” ์„น์…˜ ${section.id}: ๋””ํ…Œ์ผ ๋ฐ์ดํ„ฐ ๋กœ๋“œ ์‹คํŒจ`, error); + } + } + } + setFormData(newFormData); setRepeatSections(newRepeatSections); setCollapsedSections(newCollapsed);