Merge branch 'dev' of http://39.117.244.52:3000/kjs/ERP-node into feature/screen-management
This commit is contained in:
commit
eb6fa71cf4
|
|
@ -282,15 +282,13 @@ export async function getTableLabels(
|
||||||
const tableLabels = await tableManagementService.getTableLabels(tableName);
|
const tableLabels = await tableManagementService.getTableLabels(tableName);
|
||||||
|
|
||||||
if (!tableLabels) {
|
if (!tableLabels) {
|
||||||
const response: ApiResponse<null> = {
|
// 라벨이 없으면 빈 객체를 성공으로 반환 (404 에러 대신)
|
||||||
success: false,
|
const response: ApiResponse<{}> = {
|
||||||
message: "테이블 라벨 정보를 찾을 수 없습니다.",
|
success: true,
|
||||||
error: {
|
message: "테이블 라벨 정보를 조회했습니다.",
|
||||||
code: "TABLE_LABELS_NOT_FOUND",
|
data: {},
|
||||||
details: `테이블 ${tableName}의 라벨 정보가 존재하지 않습니다.`,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
res.status(404).json(response);
|
res.status(200).json(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,15 +348,13 @@ export async function getColumnLabels(
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!columnLabels) {
|
if (!columnLabels) {
|
||||||
const response: ApiResponse<null> = {
|
// 라벨이 없으면 빈 객체를 성공으로 반환 (404 에러 대신)
|
||||||
success: false,
|
const response: ApiResponse<{}> = {
|
||||||
message: "컬럼 라벨 정보를 찾을 수 없습니다.",
|
success: true,
|
||||||
error: {
|
message: "컬럼 라벨 정보를 조회했습니다.",
|
||||||
code: "COLUMN_LABELS_NOT_FOUND",
|
data: {},
|
||||||
details: `컬럼 ${tableName}.${columnName}의 라벨 정보가 존재하지 않습니다.`,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
res.status(404).json(response);
|
res.status(200).json(response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -185,6 +185,9 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||||
// 모달이 열릴 때 기본값 설정
|
// 모달이 열릴 때 기본값 설정
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen && connection) {
|
if (isOpen && connection) {
|
||||||
|
// 모달이 열릴 때마다 캐시 초기화 (라벨 업데이트 반영)
|
||||||
|
setTableColumnsCache({});
|
||||||
|
|
||||||
const fromTableName = connection.fromNode.tableName;
|
const fromTableName = connection.fromNode.tableName;
|
||||||
const toTableName = connection.toNode.tableName;
|
const toTableName = connection.toNode.tableName;
|
||||||
const fromDisplayName = connection.fromNode.displayName;
|
const fromDisplayName = connection.fromNode.displayName;
|
||||||
|
|
@ -283,8 +286,8 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||||
}, [selectedFromColumns, selectedToColumns]);
|
}, [selectedFromColumns, selectedToColumns]);
|
||||||
|
|
||||||
// 테이블 컬럼 로드 함수 (캐시 활용)
|
// 테이블 컬럼 로드 함수 (캐시 활용)
|
||||||
const loadTableColumns = async (tableName: string): Promise<ColumnInfo[]> => {
|
const loadTableColumns = async (tableName: string, forceReload = false): Promise<ColumnInfo[]> => {
|
||||||
if (tableColumnsCache[tableName]) {
|
if (tableColumnsCache[tableName] && !forceReload) {
|
||||||
return tableColumnsCache[tableName];
|
return tableColumnsCache[tableName];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -553,26 +556,9 @@ export const ConnectionSetupModal: React.FC<ConnectionSetupModalProps> = ({
|
||||||
return true; // DELETE는 필드 매핑 검증 생략
|
return true; // DELETE는 필드 매핑 검증 생략
|
||||||
}
|
}
|
||||||
|
|
||||||
// INSERT 액션의 경우 모든 TO 테이블 컬럼이 매핑되거나 기본값이 있어야 함
|
// INSERT 액션의 경우 최소 하나의 매핑이 있으면 됨 (모든 컬럼 매핑 필수 조건 제거)
|
||||||
if (action.actionType === "insert") {
|
if (action.actionType === "insert") {
|
||||||
// TO 테이블의 모든 컬럼을 찾기
|
return true; // 필드 매핑이 있으면 충분함
|
||||||
const toTableName = action.fieldMappings[0]?.targetTable;
|
|
||||||
if (!toTableName) return false;
|
|
||||||
|
|
||||||
const toTableColumns = tableColumnsCache[toTableName] || [];
|
|
||||||
if (toTableColumns.length === 0) return false;
|
|
||||||
|
|
||||||
// 모든 TO 컬럼이 매핑되거나 기본값이 있는지 확인
|
|
||||||
return toTableColumns.every((column) => {
|
|
||||||
const mapping = action.fieldMappings.find((m) => m.targetField === column.columnName);
|
|
||||||
if (!mapping) return false;
|
|
||||||
|
|
||||||
// 소스 매핑 또는 기본값 중 하나는 있어야 함
|
|
||||||
const hasSource = mapping.sourceTable && mapping.sourceField;
|
|
||||||
const hasDefault = mapping.defaultValue && mapping.defaultValue.trim();
|
|
||||||
|
|
||||||
return hasSource || hasDefault;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return action.fieldMappings.every((mapping) => {
|
return action.fieldMappings.every((mapping) => {
|
||||||
|
|
|
||||||
|
|
@ -284,8 +284,12 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
||||||
description: "", // 새로 추가된 노드는 description 없이 통일
|
description: "", // 새로 추가된 노드는 description 없이 통일
|
||||||
columns: Array.isArray(table.columns)
|
columns: Array.isArray(table.columns)
|
||||||
? table.columns.map((col) => ({
|
? table.columns.map((col) => ({
|
||||||
name: col.columnName || "unknown",
|
columnName: col.columnName || "unknown",
|
||||||
type: col.dataType || "varchar", // 기존과 동일한 기본값 사용
|
name: col.columnName || "unknown", // 호환성을 위해 유지
|
||||||
|
displayName: col.displayName, // 한국어 라벨
|
||||||
|
columnLabel: col.columnLabel, // 한국어 라벨
|
||||||
|
type: col.dataType || "varchar",
|
||||||
|
dataType: col.dataType || "varchar",
|
||||||
description: col.description || "",
|
description: col.description || "",
|
||||||
}))
|
}))
|
||||||
: [],
|
: [],
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,7 @@ const SaveDiagramModal: React.FC<SaveDiagramModalProps> = ({
|
||||||
<div className="flex flex-wrap gap-2">
|
<div className="flex flex-wrap gap-2">
|
||||||
{connectedTables.map((table) => (
|
{connectedTables.map((table) => (
|
||||||
<Badge key={table} variant="outline" className="text-xs">
|
<Badge key={table} variant="outline" className="text-xs">
|
||||||
📋 {table}
|
{table}
|
||||||
</Badge>
|
</Badge>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -162,7 +162,7 @@ const SaveDiagramModal: React.FC<SaveDiagramModalProps> = ({
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-1 text-xs text-gray-600">
|
<div className="mt-1 text-xs text-gray-600">
|
||||||
{relationship.fromColumns.join(", ")} → {relationship.toColumns.join(", ")}
|
{relationship.fromTable} → {relationship.toTable}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="outline" className="text-xs">
|
<Badge variant="outline" className="text-xs">
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,13 @@ import React from "react";
|
||||||
import { Handle, Position } from "@xyflow/react";
|
import { Handle, Position } from "@xyflow/react";
|
||||||
|
|
||||||
interface TableColumn {
|
interface TableColumn {
|
||||||
name: string;
|
columnName: string;
|
||||||
type: string;
|
name?: string; // 호환성을 위해 유지
|
||||||
description: string;
|
columnLabel?: string;
|
||||||
|
displayName?: string;
|
||||||
|
dataType?: string;
|
||||||
|
type?: string; // 호환성을 위해 유지
|
||||||
|
description?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Table {
|
interface Table {
|
||||||
|
|
@ -43,21 +47,24 @@ export const TableNode: React.FC<{ data: TableNodeData }> = ({ data }) => {
|
||||||
<div className="flex-1 overflow-hidden p-2" onMouseEnter={onScrollAreaEnter} onMouseLeave={onScrollAreaLeave}>
|
<div className="flex-1 overflow-hidden p-2" onMouseEnter={onScrollAreaEnter} onMouseLeave={onScrollAreaLeave}>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
{table.columns.map((column) => {
|
{table.columns.map((column) => {
|
||||||
const isSelected = selectedColumns.includes(column.name);
|
const columnKey = column.columnName || column.name || "";
|
||||||
|
const columnDisplayName = column.displayName || column.columnLabel || column.name || column.columnName;
|
||||||
|
const columnType = column.dataType || column.type || "";
|
||||||
|
const isSelected = selectedColumns.includes(columnKey);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={column.name}
|
key={columnKey}
|
||||||
className={`relative cursor-pointer rounded px-2 py-1 text-xs transition-colors ${
|
className={`relative cursor-pointer rounded px-2 py-1 text-xs transition-colors ${
|
||||||
isSelected ? "bg-blue-100 text-blue-800 ring-2 ring-blue-500" : "text-gray-700 hover:bg-gray-100"
|
isSelected ? "bg-blue-100 text-blue-800 ring-2 ring-blue-500" : "text-gray-700 hover:bg-gray-100"
|
||||||
}`}
|
}`}
|
||||||
onClick={() => onColumnClick(table.tableName, column.name)}
|
onClick={() => onColumnClick(table.tableName, columnKey)}
|
||||||
>
|
>
|
||||||
{/* 핸들 제거됨 - 컬럼 클릭으로만 연결 생성 */}
|
{/* 핸들 제거됨 - 컬럼 클릭으로만 연결 생성 */}
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="font-mono font-medium">{column.name}</span>
|
<span className="font-mono font-medium">{columnDisplayName}</span>
|
||||||
<span className="text-gray-500">{column.type}</span>
|
<span className="text-gray-500">{columnType}</span>
|
||||||
</div>
|
</div>
|
||||||
{column.description && <div className="mt-0.5 text-gray-500">{column.description}</div>}
|
{column.description && <div className="mt-0.5 text-gray-500">{column.description}</div>}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ export const ConditionRenderer: React.FC<ConditionRendererProps> = ({
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{fromTableColumns.map((column) => (
|
{fromTableColumns.map((column) => (
|
||||||
<SelectItem key={column.columnName} value={column.columnName}>
|
<SelectItem key={column.columnName} value={column.columnName}>
|
||||||
{column.columnName}
|
{column.displayName || column.columnLabel || column.columnName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|
|
||||||
|
|
@ -214,13 +214,13 @@ export const ActionConditionRenderer: React.FC<ActionConditionRendererProps> = (
|
||||||
{condition.tableType === "from" &&
|
{condition.tableType === "from" &&
|
||||||
fromTableColumns.map((column) => (
|
fromTableColumns.map((column) => (
|
||||||
<SelectItem key={column.columnName} value={column.columnName}>
|
<SelectItem key={column.columnName} value={column.columnName}>
|
||||||
{column.columnName}
|
{column.displayName || column.columnLabel || column.columnName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
{condition.tableType === "to" &&
|
{condition.tableType === "to" &&
|
||||||
toTableColumns.map((column) => (
|
toTableColumns.map((column) => (
|
||||||
<SelectItem key={column.columnName} value={column.columnName}>
|
<SelectItem key={column.columnName} value={column.columnName}>
|
||||||
{column.columnName}
|
{column.displayName || column.columnLabel || column.columnName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,7 @@ export const ActionFieldMappings: React.FC<ActionFieldMappingsProps> = ({
|
||||||
tableColumnsCache[mapping.sourceTable]?.map((column) => (
|
tableColumnsCache[mapping.sourceTable]?.map((column) => (
|
||||||
<SelectItem key={column.columnName} value={column.columnName}>
|
<SelectItem key={column.columnName} value={column.columnName}>
|
||||||
<div className="truncate" title={column.columnName}>
|
<div className="truncate" title={column.columnName}>
|
||||||
{column.columnName}
|
{column.displayName || column.columnLabel || column.columnName}
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
@ -200,7 +200,7 @@ export const ActionFieldMappings: React.FC<ActionFieldMappingsProps> = ({
|
||||||
tableColumnsCache[mapping.targetTable]?.map((column) => (
|
tableColumnsCache[mapping.targetTable]?.map((column) => (
|
||||||
<SelectItem key={column.columnName} value={column.columnName}>
|
<SelectItem key={column.columnName} value={column.columnName}>
|
||||||
<div className="truncate" title={column.columnName}>
|
<div className="truncate" title={column.columnName}>
|
||||||
{column.columnName}
|
{column.displayName || column.columnLabel || column.columnName}
|
||||||
</div>
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ export const ActionSplitConfig: React.FC<ActionSplitConfigProps> = ({
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{fromTableColumns.map((column) => (
|
{fromTableColumns.map((column) => (
|
||||||
<SelectItem key={column.columnName} value={column.columnName}>
|
<SelectItem key={column.columnName} value={column.columnName}>
|
||||||
{column.columnName}
|
{column.displayName || column.columnLabel || column.columnName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|
@ -117,7 +117,7 @@ export const ActionSplitConfig: React.FC<ActionSplitConfigProps> = ({
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{toTableColumns.map((column) => (
|
{toTableColumns.map((column) => (
|
||||||
<SelectItem key={column.columnName} value={column.columnName}>
|
<SelectItem key={column.columnName} value={column.columnName}>
|
||||||
{column.columnName}
|
{column.displayName || column.columnLabel || column.columnName}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|
|
||||||
|
|
@ -198,7 +198,9 @@ export const ColumnTableSection: React.FC<ColumnTableSectionProps> = ({
|
||||||
<div className="flex items-start justify-between gap-2">
|
<div className="flex items-start justify-between gap-2">
|
||||||
<div className="min-w-0 flex-1">
|
<div className="min-w-0 flex-1">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="truncate font-medium">{column.columnName}</span>
|
<span className="truncate font-medium">
|
||||||
|
{column.displayName || column.columnLabel || column.columnName}
|
||||||
|
</span>
|
||||||
{isSelected && <span className="flex-shrink-0 text-blue-500">●</span>}
|
{isSelected && <span className="flex-shrink-0 text-blue-500">●</span>}
|
||||||
{isMapped && <span className="flex-shrink-0 text-green-500">✓</span>}
|
{isMapped && <span className="flex-shrink-0 text-green-500">✓</span>}
|
||||||
{oppositeSelectedColumn && !isTypeCompatible && (
|
{oppositeSelectedColumn && !isTypeCompatible && (
|
||||||
|
|
@ -264,7 +266,9 @@ export const ColumnTableSection: React.FC<ColumnTableSectionProps> = ({
|
||||||
>
|
>
|
||||||
<div className="flex min-w-0 flex-col justify-between">
|
<div className="flex min-w-0 flex-col justify-between">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="truncate font-medium">{column.columnName}</span>
|
<span className="truncate font-medium">
|
||||||
|
{column.displayName || column.columnLabel || column.columnName}
|
||||||
|
</span>
|
||||||
{isSelected && <span className="flex-shrink-0 text-green-500">●</span>}
|
{isSelected && <span className="flex-shrink-0 text-green-500">●</span>}
|
||||||
{oppositeSelectedColumn && !isTypeCompatible && (
|
{oppositeSelectedColumn && !isTypeCompatible && (
|
||||||
<span className="flex-shrink-0 text-red-500" title="데이터 타입이 호환되지 않음">
|
<span className="flex-shrink-0 text-red-500" title="데이터 타입이 호환되지 않음">
|
||||||
|
|
|
||||||
|
|
@ -236,14 +236,10 @@ export const InsertFieldMappingPanel: React.FC<InsertFieldMappingPanelProps> = (
|
||||||
return (
|
return (
|
||||||
<div className="mt-4">
|
<div className="mt-4">
|
||||||
{/* 헤더 섹션 */}
|
{/* 헤더 섹션 */}
|
||||||
<Card className="mb-6 from-blue-50 to-green-50 py-2">
|
<p className="mb-4 text-sm leading-relaxed text-gray-700">
|
||||||
<CardContent className="pt-0">
|
양쪽 테이블의 컬럼을 클릭하여 매핑하거나, 대상 컬럼에 기본값을 입력하세요. 같은 데이터 타입의 컬럼만 매핑
|
||||||
<p className="text-sm leading-relaxed text-gray-700">
|
가능합니다. 하나의 FROM 컬럼은 하나의 TO 컬럼에만 매핑 가능합니다.
|
||||||
양쪽 테이블의 컬럼을 클릭하여 매핑하거나, 대상 컬럼에 기본값을 입력하세요. 같은 데이터 타입의 컬럼만 매핑
|
</p>
|
||||||
가능합니다. 하나의 FROM 컬럼은 하나의 TO 컬럼에만 매핑 가능합니다.
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-2 gap-6">
|
<div className="grid grid-cols-2 gap-6">
|
||||||
<ColumnTableSection
|
<ColumnTableSection
|
||||||
|
|
@ -290,7 +286,7 @@ export const InsertFieldMappingPanel: React.FC<InsertFieldMappingPanelProps> = (
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 빠른 필터 액션 */}
|
{/* 빠른 필터 액션 */}
|
||||||
<Card className="mt-6 py-2">
|
<Card className="mt-6 border-none py-2 shadow-none">
|
||||||
<CardContent className="flex flex-wrap gap-2 p-3">
|
<CardContent className="flex flex-wrap gap-2 p-3">
|
||||||
<span className="self-center text-sm font-medium text-gray-700">빠른 필터:</span>
|
<span className="self-center text-sm font-medium text-gray-700">빠른 필터:</span>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ export const SimpleKeySettings: React.FC<SimpleKeySettingsProps> = ({
|
||||||
}}
|
}}
|
||||||
className="rounded"
|
className="rounded"
|
||||||
/>
|
/>
|
||||||
<span>{column.columnName}</span>
|
<span>{column.displayName || column.columnLabel || column.columnName}</span>
|
||||||
<span className="text-xs text-gray-500">({column.dataType})</span>
|
<span className="text-xs text-gray-500">({column.dataType})</span>
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
|
|
@ -112,7 +112,7 @@ export const SimpleKeySettings: React.FC<SimpleKeySettingsProps> = ({
|
||||||
}}
|
}}
|
||||||
className="rounded"
|
className="rounded"
|
||||||
/>
|
/>
|
||||||
<span>{column.columnName}</span>
|
<span>{column.displayName || column.columnLabel || column.columnName}</span>
|
||||||
<span className="text-xs text-gray-500">({column.dataType})</span>
|
<span className="text-xs text-gray-500">({column.dataType})</span>
|
||||||
</label>
|
</label>
|
||||||
))}
|
))}
|
||||||
|
|
@ -132,11 +132,15 @@ export const SimpleKeySettings: React.FC<SimpleKeySettingsProps> = ({
|
||||||
<Label className="text-xs font-medium text-gray-600">선택된 From 컬럼</Label>
|
<Label className="text-xs font-medium text-gray-600">선택된 From 컬럼</Label>
|
||||||
<div className="mt-1 flex flex-wrap gap-1">
|
<div className="mt-1 flex flex-wrap gap-1">
|
||||||
{selectedFromColumns.length > 0 ? (
|
{selectedFromColumns.length > 0 ? (
|
||||||
selectedFromColumns.map((column) => (
|
selectedFromColumns.map((column) => {
|
||||||
<Badge key={column} variant="outline" className="text-xs">
|
const columnInfo = fromTableColumns.find((col) => col.columnName === column);
|
||||||
{column}
|
const displayName = columnInfo?.displayName || columnInfo?.columnLabel || column;
|
||||||
</Badge>
|
return (
|
||||||
))
|
<Badge key={column} variant="outline" className="text-xs">
|
||||||
|
{displayName}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
})
|
||||||
) : (
|
) : (
|
||||||
<span className="text-xs text-gray-400">선택된 컬럼 없음</span>
|
<span className="text-xs text-gray-400">선택된 컬럼 없음</span>
|
||||||
)}
|
)}
|
||||||
|
|
@ -147,11 +151,15 @@ export const SimpleKeySettings: React.FC<SimpleKeySettingsProps> = ({
|
||||||
<Label className="text-xs font-medium text-gray-600">선택된 To 컬럼</Label>
|
<Label className="text-xs font-medium text-gray-600">선택된 To 컬럼</Label>
|
||||||
<div className="mt-1 flex flex-wrap gap-1">
|
<div className="mt-1 flex flex-wrap gap-1">
|
||||||
{selectedToColumns.length > 0 ? (
|
{selectedToColumns.length > 0 ? (
|
||||||
selectedToColumns.map((column) => (
|
selectedToColumns.map((column) => {
|
||||||
<Badge key={column} variant="secondary" className="text-xs">
|
const columnInfo = toTableColumns.find((col) => col.columnName === column);
|
||||||
{column}
|
const displayName = columnInfo?.displayName || columnInfo?.columnLabel || column;
|
||||||
</Badge>
|
return (
|
||||||
))
|
<Badge key={column} variant="secondary" className="text-xs">
|
||||||
|
{displayName}
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
})
|
||||||
) : (
|
) : (
|
||||||
<span className="text-xs text-gray-400">선택된 컬럼 없음</span>
|
<span className="text-xs text-gray-400">선택된 컬럼 없음</span>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,13 @@ export class DataFlowAPI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 페이지네이션된 응답에서 columns 배열만 추출
|
// 페이지네이션된 응답에서 columns 배열만 추출
|
||||||
return response.data.data?.columns || [];
|
const columns = response.data.data?.columns || [];
|
||||||
|
|
||||||
|
// 이미 displayName에 라벨이 포함되어 있으므로 추가 처리 불필요
|
||||||
|
return columns.map((column) => ({
|
||||||
|
...column,
|
||||||
|
columnLabel: column.displayName || column.columnName, // displayName을 columnLabel로도 설정
|
||||||
|
}));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("컬럼 정보 조회 오류:", error);
|
console.error("컬럼 정보 조회 오류:", error);
|
||||||
throw error;
|
throw error;
|
||||||
|
|
@ -390,11 +396,40 @@ export class DataFlowAPI {
|
||||||
try {
|
try {
|
||||||
const columns = await this.getTableColumns(tableName);
|
const columns = await this.getTableColumns(tableName);
|
||||||
|
|
||||||
|
// 테이블 라벨 정보 조회
|
||||||
|
let tableLabel = tableName;
|
||||||
|
let tableDescription = `${tableName} 테이블`;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await apiClient.get(`/table-management/tables/${tableName}/labels`);
|
||||||
|
if (response.data.success && response.data.data) {
|
||||||
|
tableLabel = response.data.data.tableLabel || tableName;
|
||||||
|
tableDescription = response.data.data.description || `${tableName} 테이블`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// 라벨 정보가 없으면 기본값 사용 (404 등의 에러는 무시)
|
||||||
|
const axiosError = error as { response?: { status?: number } };
|
||||||
|
if (axiosError?.response?.status !== 404) {
|
||||||
|
console.warn(`테이블 라벨 조회 중 예상치 못한 오류: ${tableName}`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TableNode가 기대하는 컬럼 구조로 변환
|
||||||
|
const formattedColumns = columns.map((column) => ({
|
||||||
|
columnName: column.columnName,
|
||||||
|
name: column.columnName, // TableNode에서 사용하는 필드
|
||||||
|
displayName: column.displayName, // 한국어 라벨
|
||||||
|
columnLabel: column.displayName, // 동일한 값으로 설정
|
||||||
|
type: column.dataType, // TableNode에서 사용하는 필드
|
||||||
|
dataType: column.dataType,
|
||||||
|
description: column.description || "",
|
||||||
|
}));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tableName,
|
tableName,
|
||||||
displayName: tableName,
|
displayName: tableLabel,
|
||||||
description: `${tableName} 테이블`,
|
description: tableDescription,
|
||||||
columns,
|
columns: formattedColumns,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("테이블 및 컬럼 정보 조회 오류:", error);
|
console.error("테이블 및 컬럼 정보 조회 오류:", error);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue