diff --git a/backend-node/src/services/entityJoinService.ts b/backend-node/src/services/entityJoinService.ts index d0b01846..b88c0c8b 100644 --- a/backend-node/src/services/entityJoinService.ts +++ b/backend-node/src/services/entityJoinService.ts @@ -42,9 +42,23 @@ export class EntityJoinService { }, }); + logger.info(`๐Ÿ” Entity ์ปฌ๋Ÿผ ์กฐํšŒ ๊ฒฐ๊ณผ: ${entityColumns.length}๊ฐœ ๋ฐœ๊ฒฌ`); + entityColumns.forEach((col, index) => { + logger.info( + ` ${index + 1}. ${col.column_name} -> ${col.reference_table}.${col.reference_column} (display: ${col.display_column})` + ); + }); + const joinConfigs: EntityJoinConfig[] = []; for (const column of entityColumns) { + logger.info(`๐Ÿ” Entity ์ปฌ๋Ÿผ ์ƒ์„ธ ์ •๋ณด:`, { + column_name: column.column_name, + reference_table: column.reference_table, + reference_column: column.reference_column, + display_column: column.display_column, + }); + if ( !column.column_name || !column.reference_table || @@ -58,6 +72,12 @@ export class EntityJoinService { let displayColumns: string[] = []; let separator = " - "; + logger.info(`๐Ÿ” ์กฐ๊ฑด ํ™•์ธ - ์ปฌ๋Ÿผ: ${column.column_name}`, { + hasScreenConfig: !!screenConfig, + hasDisplayColumns: screenConfig?.displayColumns, + displayColumn: column.display_column, + }); + if (screenConfig && screenConfig.displayColumns) { // ํ™”๋ฉด์—์„œ ์„ค์ •๋œ ํ‘œ์‹œ ์ปฌ๋Ÿผ๋“ค ์‚ฌ์šฉ (๊ธฐ๋ณธ ํ…Œ์ด๋ธ” + ์กฐ์ธ ํ…Œ์ด๋ธ” ์กฐํ•ฉ ์ง€์›) displayColumns = screenConfig.displayColumns; @@ -70,9 +90,12 @@ export class EntityJoinService { } else if (column.display_column && column.display_column !== "none") { // ๊ธฐ์กด ์„ค์ •๋œ ๋‹จ์ผ ํ‘œ์‹œ ์ปฌ๋Ÿผ ์‚ฌ์šฉ (none์ด ์•„๋‹Œ ๊ฒฝ์šฐ๋งŒ) displayColumns = [column.display_column]; + logger.info( + `๐Ÿ”ง ๊ธฐ์กด display_column ์‚ฌ์šฉ: ${column.column_name} โ†’ ${column.display_column}` + ); } else { - // ์กฐ์ธ ํƒญ์—์„œ ๋ณด์—ฌ์ค„ ๊ธฐ๋ณธ ํ‘œ์‹œ ์ปฌ๋Ÿผ ์„ค์ • - // dept_info ํ…Œ์ด๋ธ”์˜ ๊ฒฝ์šฐ dept_name์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉ + // display_column์ด "none"์ด๊ฑฐ๋‚˜ ์—†๋Š” ๊ฒฝ์šฐ ๊ธฐ๋ณธ ํ‘œ์‹œ ์ปฌ๋Ÿผ ์„ค์ • + // ๐Ÿšจ display_column์ด ํ•ญ์ƒ "none"์ด๋ฏ€๋กœ ์ด ๋กœ์ง์„ ๊ธฐ๋ณธ์œผ๋กœ ์‚ฌ์šฉ let defaultDisplayColumn = column.reference_column; if (column.reference_table === "dept_info") { defaultDisplayColumn = "dept_name"; @@ -83,9 +106,10 @@ export class EntityJoinService { } displayColumns = [defaultDisplayColumn]; - console.log( - `๐Ÿ”ง ์กฐ์ธ ํƒญ์šฉ ๊ธฐ๋ณธ ํ‘œ์‹œ ์ปฌ๋Ÿผ ์„ค์ •: ${column.column_name} โ†’ ${defaultDisplayColumn} (${column.reference_table})` + logger.info( + `๐Ÿ”ง Entity ์กฐ์ธ ๊ธฐ๋ณธ ํ‘œ์‹œ ์ปฌ๋Ÿผ ์„ค์ •: ${column.column_name} โ†’ ${defaultDisplayColumn} (${column.reference_table})` ); + logger.info(`๐Ÿ” ์ƒ์„ฑ๋œ displayColumns ๋ฐฐ์—ด:`, displayColumns); } // ๋ณ„์นญ ์ปฌ๋Ÿผ๋ช… ์ƒ์„ฑ (writer -> writer_name) @@ -102,13 +126,32 @@ export class EntityJoinService { separator: separator, }; + logger.info(`๐Ÿ”ง ๊ธฐ๋ณธ ์กฐ์ธ ์„ค์ • ์ƒ์„ฑ:`, { + sourceTable: joinConfig.sourceTable, + sourceColumn: joinConfig.sourceColumn, + referenceTable: joinConfig.referenceTable, + aliasColumn: joinConfig.aliasColumn, + displayColumns: joinConfig.displayColumns, + }); + // ์กฐ์ธ ์„ค์ • ์œ ํšจ์„ฑ ๊ฒ€์ฆ + logger.info( + `๐Ÿ” ์กฐ์ธ ์„ค์ • ๊ฒ€์ฆ ์ค‘: ${joinConfig.sourceColumn} -> ${joinConfig.referenceTable}` + ); if (await this.validateJoinConfig(joinConfig)) { joinConfigs.push(joinConfig); + logger.info(`โœ… ์กฐ์ธ ์„ค์ • ์ถ”๊ฐ€๋จ: ${joinConfig.aliasColumn}`); + } else { + logger.warn(`โŒ ์กฐ์ธ ์„ค์ • ๊ฒ€์ฆ ์‹คํŒจ: ${joinConfig.sourceColumn}`); } } - logger.info(`Entity ์กฐ์ธ ์„ค์ • ์ƒ์„ฑ ์™„๋ฃŒ: ${joinConfigs.length}๊ฐœ`); + logger.info(`๐ŸŽฏ Entity ์กฐ์ธ ์„ค์ • ์ƒ์„ฑ ์™„๋ฃŒ: ${joinConfigs.length}๊ฐœ`); + joinConfigs.forEach((config, index) => { + logger.info( + ` ${index + 1}. ${config.sourceColumn} -> ${config.referenceTable}.${config.referenceColumn} AS ${config.aliasColumn}` + ); + }); return joinConfigs; } catch (error) { logger.error(`Entity ์กฐ์ธ ๊ฐ์ง€ ์‹คํŒจ: ${tableName}`, error); @@ -190,6 +233,9 @@ export class EntityJoinService { "master_sabun", "location", "data_type", + "company_name", + "sales_yn", + "status", ].includes(col); if (isJoinTableColumn) { @@ -213,6 +259,9 @@ export class EntityJoinService { "master_sabun", "location", "data_type", + "company_name", + "sales_yn", + "status", ].includes(col); if (isJoinTableColumn) { @@ -273,7 +322,7 @@ export class EntityJoinService { .filter(Boolean) .join("\n"); - logger.debug(`์ƒ์„ฑ๋œ Entity ์กฐ์ธ ์ฟผ๋ฆฌ:`, query); + logger.info(`๐Ÿ” ์ƒ์„ฑ๋œ Entity ์กฐ์ธ ์ฟผ๋ฆฌ:`, query); return { query: query, aliasMap: aliasMap, @@ -303,10 +352,18 @@ export class EntityJoinService { } // ์ฐธ์กฐ ํ…Œ์ด๋ธ”์˜ ์บ์‹œ ๊ฐ€๋Šฅ์„ฑ ํ™•์ธ + const displayCol = + config.displayColumn || + config.displayColumns?.[0] || + config.referenceColumn; + logger.info( + `๐Ÿ” ์บ์‹œ ํ™•์ธ์šฉ ํ‘œ์‹œ ์ปฌ๋Ÿผ: ${config.referenceTable} - ${displayCol}` + ); + const cachedData = await referenceCacheService.getCachedReference( config.referenceTable, config.referenceColumn, - config.displayColumn || config.displayColumns[0] + displayCol ); return cachedData ? "cache" : "join"; @@ -336,6 +393,14 @@ export class EntityJoinService { */ private async validateJoinConfig(config: EntityJoinConfig): Promise { try { + logger.info("๐Ÿ” ์กฐ์ธ ์„ค์ • ๊ฒ€์ฆ ์ƒ์„ธ:", { + sourceColumn: config.sourceColumn, + referenceTable: config.referenceTable, + displayColumns: config.displayColumns, + displayColumn: config.displayColumn, + aliasColumn: config.aliasColumn, + }); + // ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์กด์žฌ ํ™•์ธ const tableExists = await prisma.$queryRaw` SELECT 1 FROM information_schema.tables @@ -350,23 +415,32 @@ export class EntityJoinService { // ์ฐธ์กฐ ์ปฌ๋Ÿผ ์กด์žฌ ํ™•์ธ (displayColumns[0] ์‚ฌ์šฉ) const displayColumn = config.displayColumns?.[0] || config.displayColumn; - if (!displayColumn) { - logger.warn(`ํ‘œ์‹œ ์ปฌ๋Ÿผ์ด ์„ค์ •๋˜์ง€ ์•Š์Œ: ${config.sourceColumn}`); - return false; - } + logger.info( + `๐Ÿ” ํ‘œ์‹œ ์ปฌ๋Ÿผ ํ™•์ธ: ${displayColumn} (from displayColumns: ${config.displayColumns}, displayColumn: ${config.displayColumn})` + ); - const columnExists = await prisma.$queryRaw` - SELECT 1 FROM information_schema.columns - WHERE table_name = ${config.referenceTable} - AND column_name = ${displayColumn} - LIMIT 1 - `; + // ๐Ÿšจ display_column์ด ํ•ญ์ƒ "none"์ด๋ฏ€๋กœ, ํ‘œ์‹œ ์ปฌ๋Ÿผ์ด ์—†์–ด๋„ ์กฐ์ธ ํ—ˆ์šฉ + if (displayColumn && displayColumn !== "none") { + const columnExists = await prisma.$queryRaw` + SELECT 1 FROM information_schema.columns + WHERE table_name = ${config.referenceTable} + AND column_name = ${displayColumn} + LIMIT 1 + `; - if (!Array.isArray(columnExists) || columnExists.length === 0) { - logger.warn( - `ํ‘œ์‹œ ์ปฌ๋Ÿผ์ด ์กด์žฌํ•˜์ง€ ์•Š์Œ: ${config.referenceTable}.${displayColumn}` + if (!Array.isArray(columnExists) || columnExists.length === 0) { + logger.warn( + `ํ‘œ์‹œ ์ปฌ๋Ÿผ์ด ์กด์žฌํ•˜์ง€ ์•Š์Œ: ${config.referenceTable}.${displayColumn}` + ); + return false; + } + logger.info( + `โœ… ํ‘œ์‹œ ์ปฌ๋Ÿผ ํ™•์ธ ์™„๋ฃŒ: ${config.referenceTable}.${displayColumn}` + ); + } else { + logger.info( + `๐Ÿ”ง ํ‘œ์‹œ ์ปฌ๋Ÿผ ๊ฒ€์ฆ ์ƒ๋žต: display_column์ด none์ด๊ฑฐ๋‚˜ ์„ค์ •๋˜์ง€ ์•Š์Œ` ); - return false; } return true; diff --git a/backend-node/src/services/tableManagementService.ts b/backend-node/src/services/tableManagementService.ts index 5cb2853d..4ca5369d 100644 --- a/backend-node/src/services/tableManagementService.ts +++ b/backend-node/src/services/tableManagementService.ts @@ -2049,6 +2049,17 @@ export class TableManagementService { options.screenEntityConfigs ); + logger.info( + `๐Ÿ” detectEntityJoins ๊ฒฐ๊ณผ: ${joinConfigs.length}๊ฐœ ์กฐ์ธ ์„ค์ •` + ); + if (joinConfigs.length > 0) { + joinConfigs.forEach((config, index) => { + logger.info( + ` ์กฐ์ธ ${index + 1}: ${config.sourceColumn} -> ${config.referenceTable} AS ${config.aliasColumn}` + ); + }); + } + // ์ถ”๊ฐ€ ์กฐ์ธ ์ปฌ๋Ÿผ ์ •๋ณด๊ฐ€ ์žˆ์œผ๋ฉด ์กฐ์ธ ์„ค์ •์— ์ถ”๊ฐ€ if ( options.additionalJoinColumns && @@ -2057,40 +2068,84 @@ export class TableManagementService { logger.info( `์ถ”๊ฐ€ ์กฐ์ธ ์ปฌ๋Ÿผ ์ฒ˜๋ฆฌ: ${options.additionalJoinColumns.length}๊ฐœ` ); + logger.info( + "๐Ÿ“‹ ์ „๋‹ฌ๋ฐ›์€ additionalJoinColumns:", + options.additionalJoinColumns + ); for (const additionalColumn of options.additionalJoinColumns) { - // ๊ธฐ์กด ์กฐ์ธ ์„ค์ •์—์„œ ๊ฐ™์€ ์ฐธ์กฐ ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•˜๋Š” ์„ค์ • ์ฐพ๊ธฐ + // ๐Ÿ” sourceColumn์„ ๊ธฐ์ค€์œผ๋กœ ๊ธฐ์กด ์กฐ์ธ ์„ค์ • ์ฐพ๊ธฐ (dept_code๋กœ ์ฐพ๊ธฐ) const baseJoinConfig = joinConfigs.find( - (config) => config.referenceTable === additionalColumn.sourceTable + (config) => config.sourceColumn === additionalColumn.sourceColumn ); if (baseJoinConfig) { // joinAlias์—์„œ ์‹ค์ œ ์ปฌ๋Ÿผ๋ช… ์ถ”์ถœ (์˜ˆ: dept_code_location_name -> location_name) // sourceColumn์„ ์ œ๊ฑฐํ•œ ๋‚˜๋จธ์ง€ ๋ถ€๋ถ„์ด ์‹ค์ œ ์ปฌ๋Ÿผ๋ช… const sourceColumn = baseJoinConfig.sourceColumn; // dept_code - const joinAlias = additionalColumn.joinAlias; // dept_code_location_name - const actualColumnName = joinAlias.replace(`${sourceColumn}_`, ""); // location_name + const joinAlias = additionalColumn.joinAlias; // dept_code_company_name + const actualColumnName = joinAlias.replace(`${sourceColumn}_`, ""); // company_name + + logger.info(`๐Ÿ” ์กฐ์ธ ์ปฌ๋Ÿผ ์ƒ์„ธ ๋ถ„์„:`, { + sourceColumn, + joinAlias, + actualColumnName, + referenceTable: additionalColumn.sourceTable, + }); + + // ๐Ÿšจ ๊ธฐ๋ณธ Entity ์กฐ์ธ๊ณผ ์ค‘๋ณต๋˜์ง€ ์•Š๋„๋ก ์ฒดํฌ + const isBasicEntityJoin = + additionalColumn.joinAlias === + `${baseJoinConfig.sourceColumn}_name`; + + if (isBasicEntityJoin) { + logger.info( + `โš ๏ธ ๊ธฐ๋ณธ Entity ์กฐ์ธ๊ณผ ์ค‘๋ณต: ${additionalColumn.joinAlias} - ๊ฑด๋„ˆ๋œ€` + ); + continue; // ๊ธฐ๋ณธ Entity ์กฐ์ธ๊ณผ ์ค‘๋ณต๋˜๋ฉด ์ถ”๊ฐ€ํ•˜์ง€ ์•Š์Œ + } // ์ถ”๊ฐ€ ์กฐ์ธ ์ปฌ๋Ÿผ ์„ค์ • ์ƒ์„ฑ const additionalJoinConfig: EntityJoinConfig = { sourceTable: tableName, sourceColumn: baseJoinConfig.sourceColumn, // ์›๋ณธ ์ปฌ๋Ÿผ (dept_code) - referenceTable: additionalColumn.sourceTable, // ์ฐธ์กฐ ํ…Œ์ด๋ธ” (dept_info) + referenceTable: + (additionalColumn as any).referenceTable || + baseJoinConfig.referenceTable, // ์ฐธ์กฐ ํ…Œ์ด๋ธ” (dept_info) referenceColumn: baseJoinConfig.referenceColumn, // ์ฐธ์กฐ ํ‚ค (dept_code) - displayColumns: [actualColumnName], // ํ‘œ์‹œํ•  ์ปฌ๋Ÿผ๋“ค (location_name) + displayColumns: [actualColumnName], // ํ‘œ์‹œํ•  ์ปฌ๋Ÿผ๋“ค (company_name) displayColumn: actualColumnName, // ํ•˜์œ„ ํ˜ธํ™˜์„ฑ - aliasColumn: additionalColumn.joinAlias, // ๋ณ„์นญ (dept_code_location_name) + aliasColumn: additionalColumn.joinAlias, // ๋ณ„์นญ (dept_code_company_name) separator: " - ", // ๊ธฐ๋ณธ ๊ตฌ๋ถ„์ž }; joinConfigs.push(additionalJoinConfig); logger.info( - `์ถ”๊ฐ€ ์กฐ์ธ ์ปฌ๋Ÿผ ์„ค์ • ์ถ”๊ฐ€: ${additionalJoinConfig.aliasColumn} -> ${actualColumnName}` + `โœ… ์ถ”๊ฐ€ ์กฐ์ธ ์ปฌ๋Ÿผ ์„ค์ • ์ถ”๊ฐ€: ${additionalJoinConfig.aliasColumn} -> ${actualColumnName}` ); + logger.info(`๐Ÿ” ์ถ”๊ฐ€๋œ ์กฐ์ธ ์„ค์ • ์ƒ์„ธ:`, { + sourceTable: additionalJoinConfig.sourceTable, + sourceColumn: additionalJoinConfig.sourceColumn, + referenceTable: additionalJoinConfig.referenceTable, + displayColumns: additionalJoinConfig.displayColumns, + aliasColumn: additionalJoinConfig.aliasColumn, + }); } } } + // ์ตœ์ข… ์กฐ์ธ ์„ค์ • ๋ฐฐ์—ด ๋กœ๊น… + logger.info(`๐ŸŽฏ ์ตœ์ข… joinConfigs ๋ฐฐ์—ด (${joinConfigs.length}๊ฐœ):`); + joinConfigs.forEach((config, index) => { + logger.info( + ` ${index + 1}. ${config.sourceColumn} -> ${config.referenceTable} AS ${config.aliasColumn}`, + { + displayColumns: config.displayColumns, + displayColumn: config.displayColumn, + } + ); + }); + if (joinConfigs.length === 0) { logger.info(`Entity ์กฐ์ธ ์„ค์ •์ด ์—†์Œ: ${tableName}`); const basicResult = await this.getTableData(tableName, options); @@ -2104,8 +2159,21 @@ export class TableManagementService { } // ์กฐ์ธ ์ „๋žต ๊ฒฐ์ • (ํ…Œ์ด๋ธ” ํฌ๊ธฐ ๊ธฐ๋ฐ˜) - const strategy = - await entityJoinService.determineJoinStrategy(joinConfigs); + // ๐Ÿšจ additionalJoinColumns๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ๊ฐ•์ œ๋กœ full_join ์‚ฌ์šฉ (์บ์‹œ ์•ˆ์ •์„ฑ ๋ณด์žฅ) + let strategy: "full_join" | "cache_lookup" | "hybrid"; + + if ( + options.additionalJoinColumns && + options.additionalJoinColumns.length > 0 + ) { + strategy = "full_join"; + console.log( + `๐Ÿ”ง additionalJoinColumns ๊ฐ์ง€: ๊ฐ•์ œ๋กœ full_join ์ „๋žต ์‚ฌ์šฉ (${options.additionalJoinColumns.length}๊ฐœ ์ถ”๊ฐ€ ์กฐ์ธ)` + ); + } else { + strategy = await entityJoinService.determineJoinStrategy(joinConfigs); + } + console.log( `๐ŸŽฏ ์„ ํƒ๋œ ์กฐ์ธ ์ „๋žต: ${strategy} (${joinConfigs.length}๊ฐœ Entity ์กฐ์ธ)` ); @@ -2251,10 +2319,18 @@ export class TableManagementService { try { // ์บ์‹œ ๋ฐ์ดํ„ฐ ๋ฏธ๋ฆฌ ๋กœ๋“œ for (const config of joinConfigs) { + const displayCol = + config.displayColumn || + config.displayColumns?.[0] || + config.referenceColumn; + logger.info( + `๐Ÿ” ์บ์‹œ ๋กœ๋“œ - ${config.referenceTable}: keyCol=${config.referenceColumn}, displayCol=${displayCol}` + ); + await referenceCacheService.getCachedReference( config.referenceTable, config.referenceColumn, - config.displayColumn || config.displayColumns[0] + displayCol ); } diff --git a/frontend/app/(main)/admin/tableMng/page.tsx b/frontend/app/(main)/admin/tableMng/page.tsx index 9a83277a..3c95f4df 100644 --- a/frontend/app/(main)/admin/tableMng/page.tsx +++ b/frontend/app/(main)/admin/tableMng/page.tsx @@ -866,7 +866,6 @@ export default function TableManagementPage() { )} - {/* ์„ค์ • ์™„๋ฃŒ ํ‘œ์‹œ - ๊ฐ„์†Œํ™” */} diff --git a/frontend/lib/registry/components/table-list/SingleTableWithSticky.tsx b/frontend/lib/registry/components/table-list/SingleTableWithSticky.tsx index 51cfdb6b..5687bdba 100644 --- a/frontend/lib/registry/components/table-list/SingleTableWithSticky.tsx +++ b/frontend/lib/registry/components/table-list/SingleTableWithSticky.tsx @@ -22,6 +22,7 @@ interface SingleTableWithStickyProps { renderCheckboxCell: (row: any, index: number) => React.ReactNode; formatCellValue: (value: any, format?: string, columnName?: string) => string; getColumnWidth: (column: ColumnConfig) => number; + joinColumnMapping: Record; // ์กฐ์ธ ์ปฌ๋Ÿผ ๋งคํ•‘ ์ถ”๊ฐ€ } export const SingleTableWithSticky: React.FC = ({ @@ -39,6 +40,7 @@ export const SingleTableWithSticky: React.FC = ({ renderCheckboxCell, formatCellValue, getColumnWidth, + joinColumnMapping, }) => { const checkboxConfig = tableConfig.checkbox || {}; @@ -64,7 +66,7 @@ export const SingleTableWithSticky: React.FC = ({ return ( = ({ ) : ( data.map((row, index) => ( = ({ return ( = ({ > {column.columnName === "__checkbox__" ? renderCheckboxCell(row, index) - : formatCellValue(row[column.columnName], column.format, column.columnName) || "\u00A0"} + : (() => { + // ๐ŸŽฏ ๋งคํ•‘๋œ ์ปฌ๋Ÿผ๋ช…์œผ๋กœ ๋ฐ์ดํ„ฐ ์ฐพ๊ธฐ (๊ธฐ๋ณธ ํ…Œ์ด๋ธ”๊ณผ ๋™์ผํ•œ ๋กœ์ง) + const mappedColumnName = joinColumnMapping[column.columnName] || column.columnName; + + // ์กฐ์ธ ์ปฌ๋Ÿผ ๋งคํ•‘ ์ •๋ณด ๋กœ๊น… + if (column.columnName !== mappedColumnName && index === 0) { + console.log(`๐Ÿ”— Sticky ์กฐ์ธ ์ปฌ๋Ÿผ ๋งคํ•‘: ${column.columnName} โ†’ ${mappedColumnName}`); + } + + const cellValue = row[mappedColumnName]; + if (index === 0) { + // ์ฒซ ๋ฒˆ์งธ ํ–‰๋งŒ ๋กœ๊ทธ ์ถœ๋ ฅ (๋””๋ฒ„๊น…์šฉ) + console.log( + `๐Ÿ” Sticky ์…€ ๋ฐ์ดํ„ฐ [${column.columnName} โ†’ ${mappedColumnName}]:`, + cellValue, + ); + } + return formatCellValue(cellValue, column.format, column.columnName) || "\u00A0"; + })()} ); })} diff --git a/frontend/lib/registry/components/table-list/TableListComponent.tsx b/frontend/lib/registry/components/table-list/TableListComponent.tsx index 19bd3670..6bd974ed 100644 --- a/frontend/lib/registry/components/table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/table-list/TableListComponent.tsx @@ -97,7 +97,7 @@ export const TableListComponent: React.FC = ({ const [currentPage, setCurrentPage] = useState(1); const [totalPages, setTotalPages] = useState(0); const [totalItems, setTotalItems] = useState(0); - const [searchTerm, setSearchTerm] = useState(""); + const [searchTerm] = useState(""); const [sortColumn, setSortColumn] = useState(null); const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc"); const [columnLabels, setColumnLabels] = useState>({}); @@ -290,30 +290,42 @@ export const TableListComponent: React.FC = ({ // ๐ŸŽฏ ์กฐ์ธ ํƒญ์—์„œ ์ถ”๊ฐ€ํ•œ ์ปฌ๋Ÿผ๋“ค๋„ ์ถ”๊ฐ€ (์‹ค์ œ๋กœ ์กด์žฌํ•˜๋Š” ์ปฌ๋Ÿผ๋งŒ) ...joinTabColumns .filter((col) => { - // ์‹ค์ œ API ์‘๋‹ต์— ์กด์žฌํ•˜๋Š” ์ปฌ๋Ÿผ๋งŒ ํ•„ํ„ฐ๋ง - const validJoinColumns = ["dept_code_name", "dept_name"]; - const isValid = validJoinColumns.includes(col.columnName); - if (!isValid) { - console.log(`๐Ÿ” ์กฐ์ธ ํƒญ ์ปฌ๋Ÿผ ์ œ์™ธ: ${col.columnName} (์œ ํšจํ•˜์ง€ ์•Š์Œ)`); + // ์กฐ์ธ ์ปฌ๋Ÿผ์ธ์ง€ ํ™•์ธ (์–ธ๋”์Šค์ฝ”์–ด๊ฐ€ ํฌํ•จ๋œ ์ปฌ๋Ÿผ) + const isJoinColumn = col.columnName.includes("_") && col.columnName !== "__checkbox__"; + if (!isJoinColumn) { + console.log(`๐Ÿ” ์กฐ์ธ ํƒญ ์ปฌ๋Ÿผ ์ œ์™ธ: ${col.columnName} (์กฐ์ธ ์ปฌ๋Ÿผ์ด ์•„๋‹˜)`); } - return isValid; + return isJoinColumn; }) .map((col) => { - // ์‹ค์ œ ์กด์žฌํ•˜๋Š” ์กฐ์ธ ์ปฌ๋Ÿผ๋งŒ ์ฒ˜๋ฆฌ - let sourceTable = tableConfig.selectedTable; - let sourceColumn = col.columnName; + // ๋™์ ์œผ๋กœ ์กฐ์ธ ์ปฌ๋Ÿผ ์ •๋ณด ์ถ”์ถœ + console.log(`๐Ÿ” ์กฐ์ธ ์ปฌ๋Ÿผ ๋ถ„์„: ${col.columnName}`); - if (col.columnName === "dept_code_name" || col.columnName === "dept_name") { - sourceTable = "dept_info"; + // ์ปฌ๋Ÿผ๋ช…์—์„œ ๊ธฐ๋ณธ ์ปฌ๋Ÿผ๊ณผ ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์ถ”์ถœ + // ์˜ˆ: dept_code_company_name -> dept_code (๊ธฐ๋ณธ), company_name (์ฐธ์กฐ) + const parts = col.columnName.split("_"); + let sourceColumn = ""; + let referenceTable = ""; + + // dept_code๋กœ ์‹œ์ž‘ํ•˜๋Š” ๊ฒฝ์šฐ + if (col.columnName.startsWith("dept_code_")) { sourceColumn = "dept_code"; + referenceTable = "dept_info"; + } + // ๋‹ค๋ฅธ ํŒจํ„ด๋“ค๋„ ์ถ”๊ฐ€ ๊ฐ€๋Šฅ + else { + // ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฒซ ๋ฒˆ์งธ ๋ถ€๋ถ„์„ ์†Œ์Šค ์ปฌ๋Ÿผ์œผ๋กœ ์‚ฌ์šฉ + sourceColumn = parts[0]; + referenceTable = tableConfig.selectedTable || "unknown"; } - console.log(`๐Ÿ” ์กฐ์ธ ํƒญ ์ปฌ๋Ÿผ ์ฒ˜๋ฆฌ: ${col.columnName} -> ${sourceTable}.${sourceColumn}`); + console.log(`๐Ÿ”— ์กฐ์ธ ์„ค์ •: ${col.columnName} -> ${sourceColumn} (${referenceTable})`); return { - sourceTable: sourceTable, + sourceTable: tableConfig.selectedTable || "unknown", // ๊ธฐ๋ณธ ํ…Œ์ด๋ธ” (user_info) sourceColumn: sourceColumn, joinAlias: col.columnName, + referenceTable: referenceTable, // ์ฐธ์กฐ ํ…Œ์ด๋ธ” ์ •๋ณด๋„ ์ถ”๊ฐ€ }; }), ]; @@ -410,6 +422,14 @@ export const TableListComponent: React.FC = ({ console.log("๐ŸŽฏ ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜:", result.data?.length || 0); console.log("๐ŸŽฏ ์ „์ฒด ํŽ˜์ด์ง€:", result.totalPages); console.log("๐ŸŽฏ ์ด ์•„์ดํ…œ:", result.total); + + // ๐Ÿšจ ๋ฐ์ดํ„ฐ ์ƒ˜ํ”Œ ํ™•์ธ (์ฒซ ๋ฒˆ์งธ ํ–‰์˜ ๋ชจ๋“  ์ปฌ๋Ÿผ๊ณผ ๊ฐ’) + if (result.data && result.data.length > 0) { + console.log("๐Ÿ” ์ฒซ ๋ฒˆ์งธ ํ–‰ ๋ฐ์ดํ„ฐ ์ƒ˜ํ”Œ:", result.data[0]); + Object.entries(result.data[0]).forEach(([key, value]) => { + console.log(` ๐Ÿ“Š ${key}: "${value}" (ํƒ€์ž…: ${typeof value})`); + }); + } setData(result.data || []); setTotalPages(result.totalPages || 1); setTotalItems(result.total || 0); @@ -427,7 +447,7 @@ export const TableListComponent: React.FC = ({ // ๐ŸŽฏ ์ฝ”๋“œ ์ปฌ๋Ÿผ๋“ค์˜ ์บ์‹œ ๋ฏธ๋ฆฌ ๋กœ๋“œ (์ „์—ญ ์บ์‹œ ์‚ฌ์šฉ) const codeColumns = Object.entries(columnMeta).filter( - ([_, meta]) => meta.webType === "code" && meta.codeCategory, + ([, meta]) => meta.webType === "code" && meta.codeCategory, ); if (codeColumns.length > 0) { @@ -462,16 +482,119 @@ export const TableListComponent: React.FC = ({ const actualApiColumns = Object.keys(result.data[0]); console.log("๐Ÿ” API ์‘๋‹ต์˜ ์‹ค์ œ ์ปฌ๋Ÿผ๋“ค:", actualApiColumns); - // ๐ŸŽฏ ์กฐ์ธ ์ปฌ๋Ÿผ ๋งคํ•‘ ํ…Œ์ด๋ธ” (์‚ฌ์šฉ์ž ์„ค์ • โ†’ API ์‘๋‹ต) - // ์‹ค์ œ API ์‘๋‹ต์— ์กด์žฌํ•˜๋Š” ์ปฌ๋Ÿผ๋งŒ ๋งคํ•‘ - const newJoinColumnMapping: Record = { - dept_code_dept_code: "dept_code", // user_info.dept_code - dept_code_status: "status", // user_info.status (dept_info.status๊ฐ€ ์กฐ์ธ๋˜์ง€ ์•Š์Œ) - dept_code_company_name: "dept_name", // dept_info.dept_name (company_name์ด ์กฐ์ธ๋˜์ง€ ์•Š์Œ) - dept_code_name: "dept_code_name", // dept_info.dept_name - dept_name: "dept_name", // dept_info.dept_name - status: "status", // user_info.status - }; + // ๐ŸŽฏ ์กฐ์ธ ์ปฌ๋Ÿผ ๋งคํ•‘ ํ…Œ์ด๋ธ” ๋™์  ์ƒ์„ฑ (์‚ฌ์šฉ์ž ์„ค์ • โ†’ API ์‘๋‹ต) + const newJoinColumnMapping: Record = {}; + + // ์‚ฌ์šฉ์ž๊ฐ€ ์„ค์ •ํ•œ ์ปฌ๋Ÿผ๋“ค๊ณผ ์‹ค์ œ API ์‘๋‹ต ์ปฌ๋Ÿผ๋“ค์„ ๋™์ ์œผ๋กœ ๋งคํ•‘ + processedColumns.forEach((userColumn) => { + // ์ฒดํฌ๋ฐ•์Šค๋Š” ์ œ์™ธ + if (userColumn.columnName === "__checkbox__") return; + + console.log(`๐Ÿ” ์ปฌ๋Ÿผ ๋งคํ•‘ ๋ถ„์„: "${userColumn.columnName}"`, { + displayName: userColumn.displayName, + isEntityJoin: userColumn.isEntityJoin, + entityJoinInfo: userColumn.entityJoinInfo, + available: actualApiColumns, + }); + + // ์‚ฌ์šฉ์ž ์„ค์ • ์ปฌ๋Ÿผ๋ช…์ด API ์‘๋‹ต์— ์ •ํ™•ํžˆ ์žˆ๋Š”์ง€ ํ™•์ธ + if (actualApiColumns.includes(userColumn.columnName)) { + // ์ง์ ‘ ๋งค์นญ๋˜๋Š” ๊ฒฝ์šฐ + newJoinColumnMapping[userColumn.columnName] = userColumn.columnName; + console.log(`โœ… ์ •ํ™• ๋งคํ•‘: ${userColumn.columnName} โ†’ ${userColumn.columnName}`); + } else { + // Entity ์กฐ์ธ๋œ ์ปฌ๋Ÿผ์ด๊ฑฐ๋‚˜ ์กฐ์ธ ํƒญ์—์„œ ์ถ”๊ฐ€ํ•œ ์ปฌ๋Ÿผ์ธ ๊ฒฝ์šฐ + let foundMatch = false; + + // 1. Entity ์กฐ์ธ ์ •๋ณด๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ aliasColumn ์šฐ์„  ํ™•์ธ + if (userColumn.entityJoinInfo?.joinAlias) { + const aliasColumn = userColumn.entityJoinInfo.joinAlias; + if (actualApiColumns.includes(aliasColumn)) { + newJoinColumnMapping[userColumn.columnName] = aliasColumn; + console.log(`๐Ÿ”— Entity ๋ณ„์นญ ๋งคํ•‘: ${userColumn.columnName} โ†’ ${aliasColumn}`); + foundMatch = true; + } + } + + // 2. ์ •ํ™•ํ•œ ์ด๋ฆ„ ๋งค์นญ (์˜ˆ: dept_code_company_name) + if (!foundMatch) { + const exactMatches = actualApiColumns.filter((apiCol) => apiCol === userColumn.columnName); + + if (exactMatches.length > 0) { + newJoinColumnMapping[userColumn.columnName] = exactMatches[0]; + console.log(`๐ŸŽฏ ์ •ํ™• ์ด๋ฆ„ ๋งคํ•‘: ${userColumn.columnName} โ†’ ${exactMatches[0]}`); + foundMatch = true; + } + } + + // 3. ์กฐ์ธ ์ปฌ๋Ÿผ ๊ฒ€์ฆ ๋ฐ ์ฒ˜๋ฆฌ + if (!foundMatch) { + // ๐Ÿšจ ์กฐ์ธ ์ปฌ๋Ÿผ์ธ์ง€ ํ™•์ธ (๋” ์ •ํ™•ํ•œ ๊ฐ์ง€ ๋กœ์ง) + const hasUnderscore = userColumn.columnName.includes("_"); + let isJoinColumn = false; + let baseColumnName = ""; + + if (hasUnderscore) { + // ๊ฐ€๋Šฅํ•œ ๋ชจ๋“  ๊ธฐ๋ณธ ์ปฌ๋Ÿผ๋ช…์„ ํ™•์ธ (dept_code_company_name -> dept_code, dept ์ˆœ์œผ๋กœ) + const parts = userColumn.columnName.split("_"); + for (let i = parts.length - 1; i >= 1; i--) { + const possibleBase = parts.slice(0, i).join("_"); + if (actualApiColumns.includes(possibleBase)) { + baseColumnName = possibleBase; + isJoinColumn = true; + break; + } + } + } + + console.log(`๐Ÿ” ์กฐ์ธ ์ปฌ๋Ÿผ ๊ฒ€์‚ฌ: "${userColumn.columnName}"`, { + hasUnderscore, + baseColumnName, + isJoinColumn, + }); + + if (isJoinColumn) { + console.log(`๐Ÿ” ์กฐ์ธ ์ปฌ๋Ÿผ ๊ธฐ๋ณธ ์ปฌ๋Ÿผ ํ™•์ธ: "${baseColumnName}"`, { + existsInApi: actualApiColumns.includes(baseColumnName), + actualApiColumns: actualApiColumns.slice(0, 10), // ์ฒ˜์Œ 10๊ฐœ๋งŒ ํ‘œ์‹œ + }); + + console.warn( + `โš ๏ธ ์กฐ์ธ ์‹คํŒจ: "${userColumn.columnName}" - ๋ฐฑ์—”๋“œ์—์„œ Entity ์กฐ์ธ์ด ์‹คํ–‰๋˜์ง€ ์•Š์Œ. ๊ธฐ๋ณธ ์ปฌ๋Ÿผ๊ฐ’ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.`, + ); + // ์กฐ์ธ ์‹คํŒจ ์‹œ ๊ธฐ๋ณธ ์ปฌ๋Ÿผ๊ฐ’์„ ํ‘œ์‹œํ•˜๋„๋ก ๋งคํ•‘ + newJoinColumnMapping[userColumn.columnName] = baseColumnName; + foundMatch = true; + } else { + // ์ผ๋ฐ˜ ์ปฌ๋Ÿผ์ธ ๊ฒฝ์šฐ ๋ถ€๋ถ„ ๋งค์นญ ์‹œ๋„ + const partialMatches = actualApiColumns.filter( + (apiCol) => apiCol.includes(userColumn.columnName) || userColumn.columnName.includes(apiCol), + ); + + if (partialMatches.length > 0) { + const bestMatch = partialMatches.reduce((best, current) => + Math.abs(current.length - userColumn.columnName.length) < + Math.abs(best.length - userColumn.columnName.length) + ? current + : best, + ); + + newJoinColumnMapping[userColumn.columnName] = bestMatch; + console.log(`๐Ÿ” ๋ถ€๋ถ„ ๋งคํ•‘: ${userColumn.columnName} โ†’ ${bestMatch}`); + foundMatch = true; + } + } + } + + // 4. ๋งค์นญ ์‹คํŒจํ•œ ๊ฒฝ์šฐ ์›๋ณธ ์œ ์ง€ (ํ•˜์ง€๋งŒ ๊ฒฝ๊ณ  ํ‘œ์‹œ) + if (!foundMatch) { + newJoinColumnMapping[userColumn.columnName] = userColumn.columnName; + console.warn( + `โš ๏ธ ๋งคํ•‘ ์‹คํŒจ: "${userColumn.columnName}" - ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปฌ๋Ÿผ: [${actualApiColumns.join(", ")}]`, + ); + } + } + }); // ๐ŸŽฏ ์กฐ์ธ ์ปฌ๋Ÿผ ๋งคํ•‘ ์ƒํƒœ ์—…๋ฐ์ดํŠธ setJoinColumnMapping(newJoinColumnMapping); @@ -533,11 +656,23 @@ export const TableListComponent: React.FC = ({ if (result.entityJoinInfo?.joinConfigs) { result.entityJoinInfo.joinConfigs.forEach((joinConfig) => { // ์›๋ณธ ์ปฌ๋Ÿผ์„ ์กฐ์ธ๋œ ์ปฌ๋Ÿผ์œผ๋กœ ๊ต์ฒด - const originalColumnIndex = processedColumns.findIndex((col) => col.columnName === joinConfig.sourceColumn); + let originalColumnIndex = processedColumns.findIndex((col) => col.columnName === joinConfig.sourceColumn); if (originalColumnIndex !== -1) { console.log(`๐Ÿ”„ ์ปฌ๋Ÿผ ๊ต์ฒด: ${joinConfig.sourceColumn} โ†’ ${joinConfig.aliasColumn}`); const originalColumn = processedColumns[originalColumnIndex]; + + // ๐Ÿšจ ์ค‘๋ณต ๋ฐฉ์ง€: ์ด๋ฏธ ๊ฐ™์€ aliasColumn์ด ์žˆ๋Š”์ง€ ํ™•์ธ + const existingAliasIndex = processedColumns.findIndex((col) => col.columnName === joinConfig.aliasColumn); + if (existingAliasIndex !== -1 && existingAliasIndex !== originalColumnIndex) { + console.warn(`๐Ÿšจ ์ค‘๋ณต ์ปฌ๋Ÿผ ๋ฐœ๊ฒฌ: ${joinConfig.aliasColumn}์ด ์ด๋ฏธ ์กด์žฌํ•ฉ๋‹ˆ๋‹ค. ์ค‘๋ณต ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.`); + processedColumns.splice(existingAliasIndex, 1); + // ์ธ๋ฑ์Šค ์žฌ์กฐ์ • + if (existingAliasIndex < originalColumnIndex) { + originalColumnIndex--; + } + } + processedColumns[originalColumnIndex] = { ...originalColumn, columnName: joinConfig.aliasColumn, // dept_code โ†’ dept_code_name @@ -583,9 +718,26 @@ export const TableListComponent: React.FC = ({ processedColumns = autoColumns; } + // ๐Ÿšจ processedColumns์—์„œ ์ค‘๋ณต ์ œ๊ฑฐ + const uniqueProcessedColumns = processedColumns.filter( + (column, index, self) => self.findIndex((c) => c.columnName === column.columnName) === index, + ); + + if (uniqueProcessedColumns.length !== processedColumns.length) { + console.error("๐Ÿšจ processedColumns์—์„œ ์ค‘๋ณต ๋ฐœ๊ฒฌ:"); + console.error( + "์›๋ณธ:", + processedColumns.map((c) => c.columnName), + ); + console.error( + "์ค‘๋ณต ์ œ๊ฑฐ ํ›„:", + uniqueProcessedColumns.map((c) => c.columnName), + ); + } + // ๐ŸŽฏ ํ‘œ์‹œํ•  ์ปฌ๋Ÿผ ์ƒํƒœ ์—…๋ฐ์ดํŠธ - setDisplayColumns(processedColumns); - console.log("๐ŸŽฏ displayColumns ์—…๋ฐ์ดํŠธ๋จ:", processedColumns); + setDisplayColumns(uniqueProcessedColumns); + console.log("๐ŸŽฏ displayColumns ์—…๋ฐ์ดํŠธ๋จ:", uniqueProcessedColumns); console.log("๐ŸŽฏ ๋ฐ์ดํ„ฐ ๊ฐœ์ˆ˜:", result.data?.length || 0); console.log("๐ŸŽฏ ์ „์ฒด ๋ฐ์ดํ„ฐ:", result.data); } @@ -836,6 +988,25 @@ export const TableListComponent: React.FC = ({ "๐ŸŽฏ visibleColumns ์ปฌ๋Ÿผ๋ช…๋“ค:", columns.map((c) => c.columnName), ); + + // ๐Ÿšจ ์ค‘๋ณต ํ‚ค ๊ฒ€์‚ฌ + const columnNames = columns.map((c) => c.columnName); + const duplicates = columnNames.filter((name, index) => columnNames.indexOf(name) !== index); + if (duplicates.length > 0) { + console.error("๐Ÿšจ ์ค‘๋ณต๋œ ์ปฌ๋Ÿผ๋ช… ๋ฐœ๊ฒฌ:", duplicates); + console.error("๐Ÿšจ ์ „์ฒด ์ปฌ๋Ÿผ๋ช… ๋ชฉ๋ก:", columnNames); + + // ์ค‘๋ณต ์ œ๊ฑฐ + const uniqueColumns = columns.filter( + (column, index, self) => self.findIndex((c) => c.columnName === column.columnName) === index, + ); + console.log( + "๐Ÿ”ง ์ค‘๋ณต ์ œ๊ฑฐ ํ›„ ์ปฌ๋Ÿผ๋“ค:", + uniqueColumns.map((c) => c.columnName), + ); + return uniqueColumns; + } + return columns; }, [displayColumns, tableConfig.columns, tableConfig.checkbox, isDesignMode]); @@ -1084,7 +1255,7 @@ export const TableListComponent: React.FC = ({ onClearFilters={handleClearAdvancedFilters} tableColumns={visibleColumns.map((col) => ({ columnName: col.columnName, - webType: columnMeta[col.columnName]?.webType || "text", + webType: (columnMeta[col.columnName]?.webType as any) || "text", displayName: columnLabels[col.columnName] || col.displayName || col.columnName, codeCategory: columnMeta[col.columnName]?.codeCategory, isVisible: col.visible, @@ -1132,15 +1303,16 @@ export const TableListComponent: React.FC = ({ renderCheckboxCell={renderCheckboxCell} formatCellValue={formatCellValue} getColumnWidth={getColumnWidth} + joinColumnMapping={joinColumnMapping} /> ) : ( // ๊ธฐ์กด ํ…Œ์ด๋ธ” (๊ฐ€๋กœ ์Šคํฌ๋กค์ด ํ•„์š” ์—†๋Š” ๊ฒฝ์šฐ) - {visibleColumns.map((column) => ( + {visibleColumns.map((column, colIndex) => ( = ({ ) : ( data.map((row, index) => ( = ({ style={{ minHeight: "40px", height: "40px", lineHeight: "1" }} onClick={() => handleRowClick(row)} > - {visibleColumns.map((column) => ( + {visibleColumns.map((column, colIndex) => ( @@ -1212,15 +1384,28 @@ export const TableListComponent: React.FC = ({ : (() => { // ๐ŸŽฏ ๋งคํ•‘๋œ ์ปฌ๋Ÿผ๋ช…์œผ๋กœ ๋ฐ์ดํ„ฐ ์ฐพ๊ธฐ const mappedColumnName = joinColumnMapping[column.columnName] || column.columnName; + + // ์กฐ์ธ ์ปฌ๋Ÿผ ๋งคํ•‘ ์ •๋ณด ๋กœ๊น… + if (column.columnName !== mappedColumnName && index === 0) { + console.log(`๐Ÿ”— ์กฐ์ธ ์ปฌ๋Ÿผ ๋งคํ•‘: ${column.columnName} โ†’ ${mappedColumnName}`); + } + const cellValue = row[mappedColumnName]; if (index === 0) { // ์ฒซ ๋ฒˆ์งธ ํ–‰๋งŒ ๋กœ๊ทธ ์ถœ๋ ฅ - console.log( - `๐Ÿ” ์…€ ๋ฐ์ดํ„ฐ [${column.columnName} โ†’ ${mappedColumnName}]:`, - cellValue, - "์ „์ฒด row:", - row, - ); + console.log(`๐Ÿ” ์…€ ๋ฐ์ดํ„ฐ [${column.columnName} โ†’ ${mappedColumnName}]:`, cellValue); + + // ๐Ÿšจ ์กฐ์ธ๋œ ์ปฌ๋Ÿผ์ธ ๊ฒฝ์šฐ ์ถ”๊ฐ€ ๋””๋ฒ„๊น… + if (column.columnName !== mappedColumnName) { + console.log(" ๐Ÿ”— ์กฐ์ธ ์ปฌ๋Ÿผ ๋ถ„์„:"); + console.log(` ๐Ÿ‘ค ์‚ฌ์šฉ์ž ์„ค์ • ์ปฌ๋Ÿผ: "${column.columnName}"`); + console.log(` ๐Ÿ“ก ๋งคํ•‘๋œ API ์ปฌ๋Ÿผ: "${mappedColumnName}"`); + console.log(` ๐Ÿ“‹ ์ปฌ๋Ÿผ ๋ผ๋ฒจ: "${column.displayName}"`); + console.log(` ๐Ÿ’พ ์‹ค์ œ ๋ฐ์ดํ„ฐ: "${cellValue}"`); + console.log( + ` ๐Ÿ”„ ์›๋ณธ ์ปฌ๋Ÿผ ๋ฐ์ดํ„ฐ (${column.columnName}): "${row[column.columnName]}"`, + ); + } } return formatCellValue(cellValue, column.format, column.columnName) || "\u00A0"; })()}