엔티티 표시기능 개선
This commit is contained in:
parent
a90ddac512
commit
2cddb42255
|
|
@ -1183,13 +1183,20 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
referenceTable: col.additionalJoinInfo!.referenceTable,
|
||||
}));
|
||||
|
||||
// console.log("🔍 [TableList] API 호출 시작", {
|
||||
// tableName: tableConfig.selectedTable,
|
||||
// page,
|
||||
// pageSize,
|
||||
// sortBy,
|
||||
// sortOrder,
|
||||
// });
|
||||
// 🎯 화면별 엔티티 표시 설정 수집
|
||||
const screenEntityConfigs: Record<string, any> = {};
|
||||
(tableConfig.columns || [])
|
||||
.filter((col) => col.entityDisplayConfig && col.entityDisplayConfig.displayColumns?.length > 0)
|
||||
.forEach((col) => {
|
||||
screenEntityConfigs[col.columnName] = {
|
||||
displayColumns: col.entityDisplayConfig!.displayColumns,
|
||||
separator: col.entityDisplayConfig!.separator || " - ",
|
||||
sourceTable: col.entityDisplayConfig!.sourceTable || tableConfig.selectedTable,
|
||||
joinTable: col.entityDisplayConfig!.joinTable,
|
||||
};
|
||||
});
|
||||
|
||||
console.log("🎯 [TableList] 화면별 엔티티 설정:", screenEntityConfigs);
|
||||
|
||||
// 🎯 항상 entityJoinApi 사용 (writer 컬럼 자동 조인 지원)
|
||||
response = await entityJoinApi.getTableDataWithJoins(tableConfig.selectedTable, {
|
||||
|
|
@ -1200,6 +1207,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
search: hasFilters ? filters : undefined,
|
||||
enableEntityJoin: true,
|
||||
additionalJoinColumns: entityJoinColumns.length > 0 ? entityJoinColumns : undefined,
|
||||
screenEntityConfigs: Object.keys(screenEntityConfigs).length > 0 ? screenEntityConfigs : undefined, // 🎯 화면별 엔티티 설정 전달
|
||||
dataFilter: tableConfig.dataFilter, // 🆕 데이터 필터 전달
|
||||
});
|
||||
|
||||
|
|
@ -1756,33 +1764,46 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
|
||||
const formatCellValue = useCallback(
|
||||
(value: any, column: ColumnConfig, rowData?: Record<string, any>) => {
|
||||
if (value === null || value === undefined) return "-";
|
||||
|
||||
// 🎯 writer 컬럼 자동 변환: user_id -> user_name
|
||||
if (column.columnName === "writer" && rowData && rowData.writer_name) {
|
||||
return rowData.writer_name;
|
||||
}
|
||||
|
||||
// 🎯 엔티티 컬럼 표시 설정이 있는 경우
|
||||
// 🎯 엔티티 컬럼 표시 설정이 있는 경우 - value가 null이어도 rowData에서 조합 가능
|
||||
// 이 체크를 가장 먼저 수행 (null 체크보다 앞에)
|
||||
if (column.entityDisplayConfig && rowData) {
|
||||
// displayColumns 또는 selectedColumns 둘 다 체크
|
||||
const displayColumns = column.entityDisplayConfig.displayColumns || column.entityDisplayConfig.selectedColumns;
|
||||
const displayColumns = column.entityDisplayConfig.displayColumns || (column.entityDisplayConfig as any).selectedColumns;
|
||||
const separator = column.entityDisplayConfig.separator;
|
||||
|
||||
if (displayColumns && displayColumns.length > 0) {
|
||||
// 선택된 컬럼들의 값을 구분자로 조합
|
||||
const values = displayColumns
|
||||
.map((colName) => {
|
||||
const cellValue = rowData[colName];
|
||||
.map((colName: string) => {
|
||||
// 1. 먼저 직접 컬럼명으로 시도 (기본 테이블 컬럼인 경우)
|
||||
let cellValue = rowData[colName];
|
||||
|
||||
// 2. 없으면 ${sourceColumn}_${colName} 형식으로 시도 (조인 테이블 컬럼인 경우)
|
||||
if (cellValue === null || cellValue === undefined) {
|
||||
const joinedKey = `${column.columnName}_${colName}`;
|
||||
cellValue = rowData[joinedKey];
|
||||
}
|
||||
|
||||
if (cellValue === null || cellValue === undefined) return "";
|
||||
return String(cellValue);
|
||||
})
|
||||
.filter((v) => v !== ""); // 빈 값 제외
|
||||
.filter((v: string) => v !== ""); // 빈 값 제외
|
||||
|
||||
return values.join(separator || " - ");
|
||||
const result = values.join(separator || " - ");
|
||||
if (result) {
|
||||
return result; // 결과가 있으면 반환
|
||||
}
|
||||
// 결과가 비어있으면 아래로 계속 진행 (원래 값 사용)
|
||||
}
|
||||
}
|
||||
|
||||
// value가 null/undefined면 "-" 반환
|
||||
if (value === null || value === undefined) return "-";
|
||||
|
||||
// 🎯 writer 컬럼 자동 변환: user_id -> user_name
|
||||
if (column.columnName === "writer" && rowData && rowData.writer_name) {
|
||||
return rowData.writer_name;
|
||||
}
|
||||
|
||||
const meta = columnMeta[column.columnName];
|
||||
|
||||
// inputType 기반 포맷팅 (columnMeta에서 가져온 inputType 우선)
|
||||
|
|
|
|||
|
|
@ -467,42 +467,22 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
|
||||
// 🎯 엔티티 컬럼의 표시 컬럼 정보 로드
|
||||
const loadEntityDisplayConfig = async (column: ColumnConfig) => {
|
||||
if (!column.isEntityJoin || !column.entityJoinInfo) {
|
||||
return;
|
||||
}
|
||||
const configKey = `${column.columnName}`;
|
||||
|
||||
// 이미 로드된 경우 스킵
|
||||
if (entityDisplayConfigs[configKey]) return;
|
||||
|
||||
// entityDisplayConfig가 없으면 초기화
|
||||
if (!column.entityDisplayConfig) {
|
||||
// sourceTable을 결정: entityJoinInfo -> config.selectedTable -> screenTableName 순서
|
||||
const initialSourceTable = column.entityJoinInfo?.sourceTable || config.selectedTable || screenTableName;
|
||||
|
||||
if (!initialSourceTable) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedColumns = config.columns?.map((col) => {
|
||||
if (col.columnName === column.columnName) {
|
||||
return {
|
||||
...col,
|
||||
entityDisplayConfig: {
|
||||
displayColumns: [],
|
||||
separator: " - ",
|
||||
sourceTable: initialSourceTable,
|
||||
joinTable: "",
|
||||
},
|
||||
};
|
||||
}
|
||||
return col;
|
||||
});
|
||||
|
||||
if (updatedColumns) {
|
||||
handleChange("columns", updatedColumns);
|
||||
// 업데이트된 컬럼으로 다시 시도
|
||||
const updatedColumn = updatedColumns.find((col) => col.columnName === column.columnName);
|
||||
if (updatedColumn) {
|
||||
return loadEntityDisplayConfig(updatedColumn);
|
||||
}
|
||||
}
|
||||
if (!column.isEntityJoin) {
|
||||
// 엔티티 컬럼이 아니면 빈 상태로 설정하여 로딩 상태 해제
|
||||
setEntityDisplayConfigs((prev) => ({
|
||||
...prev,
|
||||
[configKey]: {
|
||||
sourceColumns: [],
|
||||
joinColumns: [],
|
||||
selectedColumns: [],
|
||||
separator: " - ",
|
||||
},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -512,32 +492,56 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
// 3. config.selectedTable
|
||||
// 4. screenTableName
|
||||
const sourceTable =
|
||||
column.entityDisplayConfig.sourceTable ||
|
||||
column.entityDisplayConfig?.sourceTable ||
|
||||
column.entityJoinInfo?.sourceTable ||
|
||||
config.selectedTable ||
|
||||
screenTableName;
|
||||
|
||||
let joinTable = column.entityDisplayConfig.joinTable;
|
||||
|
||||
// sourceTable이 여전히 비어있으면 에러
|
||||
// sourceTable이 비어있으면 빈 상태로 설정
|
||||
if (!sourceTable) {
|
||||
console.warn("⚠️ sourceTable을 찾을 수 없음:", column.columnName);
|
||||
setEntityDisplayConfigs((prev) => ({
|
||||
...prev,
|
||||
[configKey]: {
|
||||
sourceColumns: [],
|
||||
joinColumns: [],
|
||||
selectedColumns: column.entityDisplayConfig?.displayColumns || [],
|
||||
separator: column.entityDisplayConfig?.separator || " - ",
|
||||
},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!joinTable && sourceTable) {
|
||||
// joinTable이 없으면 tableTypeApi로 조회해서 설정
|
||||
let joinTable = column.entityDisplayConfig?.joinTable;
|
||||
|
||||
// joinTable이 없으면 tableTypeApi로 조회해서 설정
|
||||
if (!joinTable) {
|
||||
try {
|
||||
console.log("🔍 tableTypeApi로 컬럼 정보 조회:", {
|
||||
tableName: sourceTable,
|
||||
columnName: column.columnName,
|
||||
});
|
||||
|
||||
const columnList = await tableTypeApi.getColumns(sourceTable);
|
||||
const columnInfo = columnList.find((col: any) => (col.column_name || col.columnName) === column.columnName);
|
||||
|
||||
console.log("🔍 컬럼 정보 조회 결과:", {
|
||||
columnInfo: columnInfo,
|
||||
referenceTable: columnInfo?.reference_table || columnInfo?.referenceTable,
|
||||
referenceColumn: columnInfo?.reference_column || columnInfo?.referenceColumn,
|
||||
});
|
||||
|
||||
if (columnInfo?.reference_table || columnInfo?.referenceTable) {
|
||||
joinTable = columnInfo.reference_table || columnInfo.referenceTable;
|
||||
console.log("✅ tableTypeApi에서 조인 테이블 정보 찾음:", joinTable);
|
||||
|
||||
// entityDisplayConfig 업데이트
|
||||
const updatedConfig = {
|
||||
...column.entityDisplayConfig,
|
||||
sourceTable: sourceTable,
|
||||
joinTable: joinTable,
|
||||
displayColumns: column.entityDisplayConfig?.displayColumns || [],
|
||||
separator: column.entityDisplayConfig?.separator || " - ",
|
||||
};
|
||||
|
||||
// 컬럼 설정 업데이트
|
||||
|
|
@ -553,74 +557,27 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
}
|
||||
} catch (error) {
|
||||
console.error("tableTypeApi 컬럼 정보 조회 실패:", error);
|
||||
console.log("❌ 조회 실패 상세:", { sourceTable, columnName: column.columnName });
|
||||
}
|
||||
} else if (!joinTable) {
|
||||
console.warn("⚠️ sourceTable이 없어서 joinTable 조회 불가:", column.columnName);
|
||||
}
|
||||
|
||||
console.log("🔍 최종 추출한 값:", { sourceTable, joinTable });
|
||||
const configKey = `${column.columnName}`;
|
||||
|
||||
// 이미 로드된 경우 스킵
|
||||
if (entityDisplayConfigs[configKey]) return;
|
||||
|
||||
// joinTable이 비어있으면 tableTypeApi로 컬럼 정보를 다시 가져와서 referenceTable 정보를 찾기
|
||||
let actualJoinTable = joinTable;
|
||||
if (!actualJoinTable && sourceTable) {
|
||||
try {
|
||||
console.log("🔍 tableTypeApi로 컬럼 정보 다시 조회:", {
|
||||
tableName: sourceTable,
|
||||
columnName: column.columnName,
|
||||
});
|
||||
|
||||
const columnList = await tableTypeApi.getColumns(sourceTable);
|
||||
const columnInfo = columnList.find((col: any) => (col.column_name || col.columnName) === column.columnName);
|
||||
|
||||
console.log("🔍 컬럼 정보 조회 결과:", {
|
||||
columnInfo: columnInfo,
|
||||
referenceTable: columnInfo?.reference_table || columnInfo?.referenceTable,
|
||||
referenceColumn: columnInfo?.reference_column || columnInfo?.referenceColumn,
|
||||
});
|
||||
|
||||
if (columnInfo?.reference_table || columnInfo?.referenceTable) {
|
||||
actualJoinTable = columnInfo.reference_table || columnInfo.referenceTable;
|
||||
console.log("✅ tableTypeApi에서 조인 테이블 정보 찾음:", actualJoinTable);
|
||||
|
||||
// entityDisplayConfig 업데이트
|
||||
const updatedConfig = {
|
||||
...column.entityDisplayConfig,
|
||||
joinTable: actualJoinTable,
|
||||
};
|
||||
|
||||
// 컬럼 설정 업데이트
|
||||
const updatedColumns = config.columns?.map((col) =>
|
||||
col.columnName === column.columnName ? { ...col, entityDisplayConfig: updatedConfig } : col,
|
||||
);
|
||||
|
||||
if (updatedColumns) {
|
||||
handleChange("columns", updatedColumns);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("tableTypeApi 컬럼 정보 조회 실패:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// sourceTable과 joinTable이 모두 있어야 로드
|
||||
if (!sourceTable || !actualJoinTable) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 기본 테이블과 조인 테이블의 컬럼 정보를 병렬로 로드
|
||||
const [sourceResult, joinResult] = await Promise.all([
|
||||
entityJoinApi.getReferenceTableColumns(sourceTable),
|
||||
entityJoinApi.getReferenceTableColumns(actualJoinTable),
|
||||
]);
|
||||
|
||||
// 기본 테이블 컬럼 정보는 항상 로드
|
||||
const sourceResult = await entityJoinApi.getReferenceTableColumns(sourceTable);
|
||||
const sourceColumns = sourceResult.columns || [];
|
||||
const joinColumns = joinResult.columns || [];
|
||||
|
||||
// joinTable이 있으면 조인 테이블 컬럼도 로드
|
||||
let joinColumns: Array<{ columnName: string; displayName: string; dataType: string }> = [];
|
||||
if (joinTable) {
|
||||
try {
|
||||
const joinResult = await entityJoinApi.getReferenceTableColumns(joinTable);
|
||||
joinColumns = joinResult.columns || [];
|
||||
} catch (joinError) {
|
||||
console.warn("⚠️ 조인 테이블 컬럼 로드 실패:", joinTable, joinError);
|
||||
// 조인 테이블 로드 실패해도 소스 테이블 컬럼은 표시
|
||||
}
|
||||
}
|
||||
|
||||
setEntityDisplayConfigs((prev) => ({
|
||||
...prev,
|
||||
|
|
@ -633,6 +590,16 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
}));
|
||||
} catch (error) {
|
||||
console.error("엔티티 표시 컬럼 정보 로드 실패:", error);
|
||||
// 에러 발생 시에도 빈 상태로 설정하여 로딩 상태 해제
|
||||
setEntityDisplayConfigs((prev) => ({
|
||||
...prev,
|
||||
[configKey]: {
|
||||
sourceColumns: [],
|
||||
joinColumns: [],
|
||||
selectedColumns: column.entityDisplayConfig?.displayColumns || [],
|
||||
separator: column.entityDisplayConfig?.separator || " - ",
|
||||
},
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -873,76 +840,95 @@ export const TableListConfigPanel: React.FC<TableListConfigPanelProps> = ({
|
|||
{/* 표시 컬럼 선택 (다중 선택) */}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-xs">표시할 컬럼 선택</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-6 w-full justify-between text-xs"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{entityDisplayConfigs[column.columnName].selectedColumns.length > 0
|
||||
? `${entityDisplayConfigs[column.columnName].selectedColumns.length}개 선택됨`
|
||||
: "컬럼 선택"}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="컬럼 검색..." className="text-xs" />
|
||||
<CommandList>
|
||||
<CommandEmpty className="text-xs">컬럼을 찾을 수 없습니다.</CommandEmpty>
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.length > 0 && (
|
||||
<CommandGroup heading={`기본: ${column.entityDisplayConfig?.sourceTable}`}>
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`source-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)}
|
||||
{entityDisplayConfigs[column.columnName].joinColumns.length > 0 && (
|
||||
<CommandGroup heading={`조인: ${column.entityDisplayConfig?.joinTable}`}>
|
||||
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`join-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.length === 0 &&
|
||||
entityDisplayConfigs[column.columnName].joinColumns.length === 0 ? (
|
||||
<div className="py-2 text-center text-xs text-gray-400">
|
||||
표시 가능한 컬럼이 없습니다.
|
||||
{!column.entityDisplayConfig?.joinTable && (
|
||||
<p className="mt-1 text-[10px]">
|
||||
테이블 타입 관리에서 참조 테이블을 설정하면 더 많은 컬럼을 선택할 수 있습니다.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-6 w-full justify-between text-xs"
|
||||
style={{ fontSize: "12px" }}
|
||||
>
|
||||
{entityDisplayConfigs[column.columnName].selectedColumns.length > 0
|
||||
? `${entityDisplayConfigs[column.columnName].selectedColumns.length}개 선택됨`
|
||||
: "컬럼 선택"}
|
||||
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-full p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput placeholder="컬럼 검색..." className="text-xs" />
|
||||
<CommandList>
|
||||
<CommandEmpty className="text-xs">컬럼을 찾을 수 없습니다.</CommandEmpty>
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.length > 0 && (
|
||||
<CommandGroup heading={`기본 테이블: ${column.entityDisplayConfig?.sourceTable || config.selectedTable || screenTableName}`}>
|
||||
{entityDisplayConfigs[column.columnName].sourceColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`source-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)}
|
||||
{entityDisplayConfigs[column.columnName].joinColumns.length > 0 && (
|
||||
<CommandGroup heading={`참조 테이블: ${column.entityDisplayConfig?.joinTable}`}>
|
||||
{entityDisplayConfigs[column.columnName].joinColumns.map((col) => (
|
||||
<CommandItem
|
||||
key={`join-${col.columnName}`}
|
||||
onSelect={() => toggleEntityDisplayColumn(column.columnName, col.columnName)}
|
||||
className="text-xs"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-2 h-4 w-4",
|
||||
entityDisplayConfigs[column.columnName].selectedColumns.includes(
|
||||
col.columnName,
|
||||
)
|
||||
? "opacity-100"
|
||||
: "opacity-0",
|
||||
)}
|
||||
/>
|
||||
{col.displayName}
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
)}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 참조 테이블 미설정 안내 */}
|
||||
{!column.entityDisplayConfig?.joinTable && entityDisplayConfigs[column.columnName].sourceColumns.length > 0 && (
|
||||
<div className="rounded bg-blue-50 p-2 text-[10px] text-blue-600">
|
||||
현재 기본 테이블 컬럼만 표시됩니다. 테이블 타입 관리에서 참조 테이블을 설정하면 조인된 테이블의 컬럼도 선택할 수 있습니다.
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 선택된 컬럼 미리보기 */}
|
||||
{entityDisplayConfigs[column.columnName].selectedColumns.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
|
|
|
|||
Loading…
Reference in New Issue