조인컬럼 오류
This commit is contained in:
parent
d52d6c129b
commit
86dc961968
|
|
@ -866,7 +866,6 @@ export default function TableManagementPage() {
|
|||
</Select>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
{/* 설정 완료 표시 - 간소화 */}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||
|
||||
return (
|
||||
<TableHead
|
||||
key={column.columnName}
|
||||
key={`sticky-header-${colIndex}-${column.columnName}`}
|
||||
className={cn(
|
||||
column.columnName === "__checkbox__"
|
||||
? "h-10 border-b px-4 py-2 text-center align-middle"
|
||||
|
|
@ -129,7 +129,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||
) : (
|
||||
data.map((row, index) => (
|
||||
<TableRow
|
||||
key={`row-${index}`}
|
||||
key={`sticky-row-${index}`}
|
||||
className={cn(
|
||||
"h-10 cursor-pointer border-b leading-none",
|
||||
tableConfig.tableStyle?.hoverEffect && "hover:bg-gray-50",
|
||||
|
|
@ -155,7 +155,7 @@ export const SingleTableWithSticky: React.FC<SingleTableWithStickyProps> = ({
|
|||
|
||||
return (
|
||||
<TableCell
|
||||
key={`cell-${column.columnName}`}
|
||||
key={`sticky-cell-${index}-${colIndex}-${column.columnName}`}
|
||||
className={cn(
|
||||
"h-10 px-4 py-2 align-middle text-sm whitespace-nowrap",
|
||||
`text-${column.align}`,
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
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<string | null>(null);
|
||||
const [sortDirection, setSortDirection] = useState<"asc" | "desc">("asc");
|
||||
const [columnLabels, setColumnLabels] = useState<Record<string, string>>({});
|
||||
|
|
@ -311,7 +311,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
console.log(`🔍 조인 탭 컬럼 처리: ${col.columnName} -> ${sourceTable}.${sourceColumn}`);
|
||||
|
||||
return {
|
||||
sourceTable: sourceTable,
|
||||
sourceTable: sourceTable || tableConfig.selectedTable || "unknown",
|
||||
sourceColumn: sourceColumn,
|
||||
joinAlias: col.columnName,
|
||||
};
|
||||
|
|
@ -427,7 +427,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
|
||||
// 🎯 코드 컬럼들의 캐시 미리 로드 (전역 캐시 사용)
|
||||
const codeColumns = Object.entries(columnMeta).filter(
|
||||
([_, meta]) => meta.webType === "code" && meta.codeCategory,
|
||||
([, meta]) => meta.webType === "code" && meta.codeCategory,
|
||||
);
|
||||
|
||||
if (codeColumns.length > 0) {
|
||||
|
|
@ -462,16 +462,81 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
const actualApiColumns = Object.keys(result.data[0]);
|
||||
console.log("🔍 API 응답의 실제 컬럼들:", actualApiColumns);
|
||||
|
||||
// 🎯 조인 컬럼 매핑 테이블 (사용자 설정 → API 응답)
|
||||
// 실제 API 응답에 존재하는 컬럼만 매핑
|
||||
const newJoinColumnMapping: Record<string, string> = {
|
||||
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<string, string> = {};
|
||||
|
||||
// 사용자가 설정한 컬럼들과 실제 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 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 +598,23 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
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 +660,26 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
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 +930,25 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
"🎯 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 +1197,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
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,
|
||||
|
|
@ -1138,9 +1251,9 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
<Table>
|
||||
<TableHeader className={tableConfig.stickyHeader ? "sticky top-0 z-10 bg-white" : ""}>
|
||||
<TableRow style={{ minHeight: "40px !important", height: "40px !important", lineHeight: "1" }}>
|
||||
{visibleColumns.map((column) => (
|
||||
{visibleColumns.map((column, colIndex) => (
|
||||
<TableHead
|
||||
key={column.columnName}
|
||||
key={`header-${colIndex}-${column.columnName}`}
|
||||
style={{
|
||||
width: column.width ? `${column.width}px` : undefined,
|
||||
minHeight: "40px !important",
|
||||
|
|
@ -1192,7 +1305,7 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
) : (
|
||||
data.map((row, index) => (
|
||||
<TableRow
|
||||
key={index}
|
||||
key={`row-${index}-${getRowKey(row, index)}`}
|
||||
className={cn(
|
||||
"h-10 cursor-pointer leading-none",
|
||||
tableConfig.tableStyle?.hoverEffect && "hover:bg-gray-50",
|
||||
|
|
@ -1201,9 +1314,9 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
style={{ minHeight: "40px", height: "40px", lineHeight: "1" }}
|
||||
onClick={() => handleRowClick(row)}
|
||||
>
|
||||
{visibleColumns.map((column) => (
|
||||
{visibleColumns.map((column, colIndex) => (
|
||||
<TableCell
|
||||
key={column.columnName}
|
||||
key={`cell-${index}-${colIndex}-${column.columnName}`}
|
||||
className={cn("h-10 align-middle whitespace-nowrap", `text-${column.align}`)}
|
||||
style={{ minHeight: "40px", height: "40px", verticalAlign: "middle" }}
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in New Issue