diff --git a/backend-node/src/services/entityJoinService.ts b/backend-node/src/services/entityJoinService.ts index 280051d0..5557d8b5 100644 --- a/backend-node/src/services/entityJoinService.ts +++ b/backend-node/src/services/entityJoinService.ts @@ -134,8 +134,8 @@ export class EntityJoinService { `๐Ÿ”ง ๊ธฐ์กด display_column ์‚ฌ์šฉ: ${column.column_name} โ†’ ${displayColumn}` ); } else { - // display_column์ด "none"์ด๊ฑฐ๋‚˜ ์—†๋Š” ๊ฒฝ์šฐ ์ฐธ์กฐ ํ…Œ์ด๋ธ”์˜ ๋ชจ๋“  ์ปฌ๋Ÿผ ๊ฐ€์ ธ์˜ค๊ธฐ - logger.info(`๐Ÿ” ${referenceTable}์˜ ๋ชจ๋“  ์ปฌ๋Ÿผ ์กฐํšŒ ์ค‘...`); + // display_column์ด "none"์ด๊ฑฐ๋‚˜ ์—†๋Š” ๊ฒฝ์šฐ ์ฐธ์กฐ ํ…Œ์ด๋ธ”์˜ ํ‘œ์‹œ์šฉ ์ปฌ๋Ÿผ ์ž๋™ ๊ฐ์ง€ + logger.info(`๐Ÿ” ${referenceTable}์˜ ํ‘œ์‹œ ์ปฌ๋Ÿผ ์ž๋™ ๊ฐ์ง€ ์ค‘...`); // ์ฐธ์กฐ ํ…Œ์ด๋ธ”์˜ ๋ชจ๋“  ์ปฌ๋Ÿผ ์ด๋ฆ„ ๊ฐ€์ ธ์˜ค๊ธฐ const tableColumnsResult = await query<{ column_name: string }>( @@ -148,10 +148,34 @@ export class EntityJoinService { ); if (tableColumnsResult.length > 0) { - displayColumns = tableColumnsResult.map((col) => col.column_name); + const allColumns = tableColumnsResult.map((col) => col.column_name); + + // ๐Ÿ†• ํ‘œ์‹œ์šฉ ์ปฌ๋Ÿผ ์ž๋™ ๊ฐ์ง€ (์šฐ์„ ์ˆœ์œ„ ์ˆœ์„œ) + // 1. *_name ์ปฌ๋Ÿผ (item_name, customer_name ๋“ฑ) + // 2. name ์ปฌ๋Ÿผ + // 3. label ์ปฌ๋Ÿผ + // 4. title ์ปฌ๋Ÿผ + // 5. ์ฐธ์กฐ ์ปฌ๋Ÿผ (referenceColumn) + const nameColumn = allColumns.find( + (col) => col.endsWith("_name") && col !== "company_name" + ); + const simpleNameColumn = allColumns.find((col) => col === "name"); + const labelColumn = allColumns.find( + (col) => col === "label" || col.endsWith("_label") + ); + const titleColumn = allColumns.find((col) => col === "title"); + + // ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ ํ‘œ์‹œ ์ปฌ๋Ÿผ ์„ ํƒ + const displayColumn = + nameColumn || + simpleNameColumn || + labelColumn || + titleColumn || + referenceColumn; + displayColumns = [displayColumn]; + logger.info( - `โœ… ${referenceTable}์˜ ๋ชจ๋“  ์ปฌ๋Ÿผ ์ž๋™ ํฌํ•จ (${displayColumns.length}๊ฐœ):`, - displayColumns.join(", ") + `โœ… ${referenceTable}์˜ ํ‘œ์‹œ ์ปฌ๋Ÿผ ์ž๋™ ๊ฐ์ง€: ${displayColumn} (์ „์ฒด ${allColumns.length}๊ฐœ ์ค‘)` ); } else { // ํ…Œ์ด๋ธ” ์ปฌ๋Ÿผ์„ ๋ชป ์ฐพ์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’ ์‚ฌ์šฉ diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index 7753baa3..1df2a551 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -73,6 +73,67 @@ export const SplitPanelLayoutComponent: React.FC return true; }; + // ๐Ÿ†• ์—”ํ‹ฐํ‹ฐ ์กฐ์ธ ์ปฌ๋Ÿผ๋ช… ๋ณ€ํ™˜ ํ—ฌํผ + // "ํ…Œ์ด๋ธ”๋ช….์ปฌ๋Ÿผ๋ช…" ํ˜•์‹์„ "์›๋ณธ์ปฌ๋Ÿผ_์กฐ์ธ์ปฌ๋Ÿผ๋ช…" ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ์ดํ„ฐ ์ ‘๊ทผ + const getEntityJoinValue = useCallback( + (item: any, columnName: string, entityColumnMap?: Record): any => { + // ์ง์ ‘ ๋งค์นญ ์‹œ๋„ + if (item[columnName] !== undefined) { + return item[columnName]; + } + + // "ํ…Œ์ด๋ธ”๋ช….์ปฌ๋Ÿผ๋ช…" ํ˜•์‹์ธ ๊ฒฝ์šฐ (์˜ˆ: item_info.item_name) + if (columnName.includes(".")) { + const [tableName, fieldName] = columnName.split("."); + + // ๐Ÿ” ๋””๋ฒ„๊น…: ์ฒซ ๋ฒˆ์งธ ์•„์ดํ…œ์—์„œ ํ‚ค ๋ชฉ๋ก ์ถœ๋ ฅ + if (item && typeof item === "object") { + const keys = Object.keys(item); + const matchingKeys = keys.filter((k) => k.includes(fieldName)); + console.log(`๐Ÿ” getEntityJoinValue: columnName=${columnName}, fieldName=${fieldName}`); + console.log(" ์ „์ฒด ํ‚ค ๋ชฉ๋ก:", keys); + console.log(" ๋งค์นญ ๊ฐ€๋Šฅํ•œ ํ‚ค๋“ค:", matchingKeys); + } + + // entityColumnMap์—์„œ ๋งคํ•‘ ์ฐพ๊ธฐ (์˜ˆ: item_info โ†’ item_code) + if (entityColumnMap && entityColumnMap[tableName]) { + const sourceColumn = entityColumnMap[tableName]; + const joinedColumnName = `${sourceColumn}_${fieldName}`; + if (item[joinedColumnName] !== undefined) { + return item[joinedColumnName]; + } + } + + // ๋ชจ๋“  ํ‚ค์—์„œ _fieldName์œผ๋กœ ๋๋‚˜๋Š” ๊ฒƒ ์ฐพ๊ธฐ + for (const key of Object.keys(item)) { + if (key.endsWith(`_${fieldName}`)) { + console.log(` โœ… ๋งค์นญ๋จ: ${key} โ†’ ${item[key]}`); + return item[key]; + } + } + + // ๐Ÿ†• ์—”ํ‹ฐํ‹ฐ ์กฐ์ธ ๊ธฐ๋ณธ ํŒจํ„ด: ํ…Œ์ด๋ธ”๋ช….์ปฌ๋Ÿผ๋ช… โ†’ ์†Œ์Šค์ปฌ๋Ÿผ_name + // ์˜ˆ: item_info.item_name โ†’ item_code_name + // tableName์—์„œ ์†Œ์Šค ์ปฌ๋Ÿผ ์ถ”๋ก  (item_info โ†’ item_code, customer_mng โ†’ customer_id ๋“ฑ) + const inferredSourceColumn = tableName.replace("_info", "_code").replace("_mng", "_id"); + const defaultAliasKey = `${inferredSourceColumn}_name`; + if (item[defaultAliasKey] !== undefined) { + console.log(` โœ… ๊ธฐ๋ณธ ๋ณ„์นญ ๋งค์นญ: ${defaultAliasKey} โ†’ ${item[defaultAliasKey]}`); + return item[defaultAliasKey]; + } + + // ํ…Œ์ด๋ธ”๋ช…_์ปฌ๋Ÿผ๋ช… ํ˜•์‹์œผ๋กœ ์‹œ๋„ + const underscoreKey = `${tableName}_${fieldName}`; + if (item[underscoreKey] !== undefined) { + return item[underscoreKey]; + } + } + + return undefined; + }, + [], + ); + // TableOptions Context const { registerTable, unregisterTable } = useTableOptions(); const [leftFilters, setLeftFilters] = useState([]); @@ -1737,7 +1798,7 @@ export const SplitPanelLayoutComponent: React.FC > {formatCellValue( col.name, - item[col.name], + getEntityJoinValue(item, col.name), leftCategoryMappings, col.format, )} @@ -1796,7 +1857,12 @@ export const SplitPanelLayoutComponent: React.FC className="px-3 py-2 text-sm whitespace-nowrap text-gray-900" style={{ textAlign: col.align || "left" }} > - {formatCellValue(col.name, item[col.name], leftCategoryMappings, col.format)} + {formatCellValue( + col.name, + getEntityJoinValue(item, col.name), + leftCategoryMappings, + col.format, + )} ))} @@ -2172,7 +2238,12 @@ export const SplitPanelLayoutComponent: React.FC className="px-3 py-2 text-sm whitespace-nowrap text-gray-900" style={{ textAlign: col.align || "left" }} > - {formatCellValue(col.name, item[col.name], rightCategoryMappings, col.format)} + {formatCellValue( + col.name, + getEntityJoinValue(item, col.name), + rightCategoryMappings, + col.format, + )} ))} {/* ์ˆ˜์ • ๋˜๋Š” ์‚ญ์ œ ๋ฒ„ํŠผ์ด ํ•˜๋‚˜๋ผ๋„ ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์„ ๋•Œ๋งŒ ์ž‘์—… ์…€ ํ‘œ์‹œ */} @@ -2246,78 +2317,16 @@ export const SplitPanelLayoutComponent: React.FC firstValues = rightColumns .slice(0, summaryCount) .map((col) => { - // ๐Ÿ†• ์—”ํ‹ฐํ‹ฐ ์กฐ์ธ ์ปฌ๋Ÿผ ์ฒ˜๋ฆฌ (์˜ˆ: item_info.item_number โ†’ item_number ๋˜๋Š” item_id_name) - let value = item[col.name]; - if (value === undefined && col.name.includes(".")) { - const columnName = col.name.split(".").pop(); - // 1์ฐจ: ์ปฌ๋Ÿผ๋ช… ๊ทธ๋Œ€๋กœ (์˜ˆ: item_number) - value = item[columnName || ""]; - // 2์ฐจ: item_info.item_number โ†’ item_id_name ๋˜๋Š” item_id_item_number ํ˜•์‹ ํ™•์ธ - if (value === undefined) { - const parts = col.name.split("."); - if (parts.length === 2) { - const refTable = parts[0]; // item_info - const refColumn = parts[1]; // item_number ๋˜๋Š” item_name - // FK ์ปฌ๋Ÿผ๋ช… ์ถ”๋ก : item_info โ†’ item_id - const fkColumn = refTable.replace("_info", "").replace("_mng", "") + "_id"; - - // ๋ฐฑ์—”๋“œ์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ณ„์นญ ํŒจํ„ด: - // 1) item_id_name (๊ธฐ๋ณธ referenceColumn) - // 2) item_id_item_name (์ถ”๊ฐ€ ์ปฌ๋Ÿผ) - if ( - refColumn === refTable.replace("_info", "").replace("_mng", "") + "_number" || - refColumn === refTable.replace("_info", "").replace("_mng", "") + "_code" - ) { - // ๊ธฐ๋ณธ ์ฐธ์กฐ ์ปฌ๋Ÿผ (item_number, customer_code ๋“ฑ) - const aliasKey = fkColumn + "_name"; - value = item[aliasKey]; - } else { - // ์ถ”๊ฐ€ ์ปฌ๋Ÿผ (item_name, customer_name ๋“ฑ) - const aliasKey = `${fkColumn}_${refColumn}`; - value = item[aliasKey]; - } - } - } - } + // ๐Ÿ†• ์—”ํ‹ฐํ‹ฐ ์กฐ์ธ ์ปฌ๋Ÿผ ์ฒ˜๋ฆฌ (getEntityJoinValue ์‚ฌ์šฉ) + const value = getEntityJoinValue(item, col.name); return [col.name, value, col.label] as [string, any, string]; }) .filter(([_, value]) => value !== null && value !== undefined && value !== ""); allValues = rightColumns .map((col) => { - // ๐Ÿ†• ์—”ํ‹ฐํ‹ฐ ์กฐ์ธ ์ปฌ๋Ÿผ ์ฒ˜๋ฆฌ - let value = item[col.name]; - if (value === undefined && col.name.includes(".")) { - const columnName = col.name.split(".").pop(); - // 1์ฐจ: ์ปฌ๋Ÿผ๋ช… ๊ทธ๋Œ€๋กœ - value = item[columnName || ""]; - // 2์ฐจ: {fk_column}_name ๋˜๋Š” {fk_column}_{ref_column} ํ˜•์‹ ํ™•์ธ - if (value === undefined) { - const parts = col.name.split("."); - if (parts.length === 2) { - const refTable = parts[0]; // item_info - const refColumn = parts[1]; // item_number ๋˜๋Š” item_name - // FK ์ปฌ๋Ÿผ๋ช… ์ถ”๋ก : item_info โ†’ item_id - const fkColumn = refTable.replace("_info", "").replace("_mng", "") + "_id"; - - // ๋ฐฑ์—”๋“œ์—์„œ ๋ฐ˜ํ™˜ํ•˜๋Š” ๋ณ„์นญ ํŒจํ„ด: - // 1) item_id_name (๊ธฐ๋ณธ referenceColumn) - // 2) item_id_item_name (์ถ”๊ฐ€ ์ปฌ๋Ÿผ) - if ( - refColumn === refTable.replace("_info", "").replace("_mng", "") + "_number" || - refColumn === refTable.replace("_info", "").replace("_mng", "") + "_code" - ) { - // ๊ธฐ๋ณธ ์ฐธ์กฐ ์ปฌ๋Ÿผ - const aliasKey = fkColumn + "_name"; - value = item[aliasKey]; - } else { - // ์ถ”๊ฐ€ ์ปฌ๋Ÿผ - const aliasKey = `${fkColumn}_${refColumn}`; - value = item[aliasKey]; - } - } - } - } + // ๐Ÿ†• ์—”ํ‹ฐํ‹ฐ ์กฐ์ธ ์ปฌ๋Ÿผ ์ฒ˜๋ฆฌ (getEntityJoinValue ์‚ฌ์šฉ) + const value = getEntityJoinValue(item, col.name); return [col.name, value, col.label] as [string, any, string]; }) .filter(([_, value]) => value !== null && value !== undefined && value !== "");