생성된 관계도 확인
This commit is contained in:
@@ -17,7 +17,7 @@ import "@xyflow/react/dist/style.css";
|
||||
import { TableNode } from "./TableNode";
|
||||
import { TableSelector } from "./TableSelector";
|
||||
import { ConnectionSetupModal } from "./ConnectionSetupModal";
|
||||
import { TableDefinition, TableRelationship, DataFlowAPI } from "@/lib/api/dataflow";
|
||||
import { TableDefinition, TableRelationship, DataFlowAPI, DataFlowDiagram } from "@/lib/api/dataflow";
|
||||
|
||||
// 고유 ID 생성 함수
|
||||
const generateUniqueId = (prefix: string, relationshipId?: number): string => {
|
||||
@@ -52,11 +52,18 @@ const edgeTypes = {};
|
||||
interface DataFlowDesignerProps {
|
||||
companyCode: string;
|
||||
onSave?: (relationships: TableRelationship[]) => void;
|
||||
selectedDiagram?: DataFlowDiagram | null;
|
||||
onBackToList?: () => void;
|
||||
}
|
||||
|
||||
// TableRelationship 타입은 dataflow.ts에서 import
|
||||
|
||||
export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({ companyCode, onSave }) => {
|
||||
export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
||||
companyCode,
|
||||
onSave,
|
||||
selectedDiagram,
|
||||
onBackToList,
|
||||
}) => {
|
||||
const [nodes, setNodes, onNodesChange] = useNodesState<Node<TableNodeData>>([]);
|
||||
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>([]);
|
||||
const [selectedColumns, setSelectedColumns] = useState<{
|
||||
@@ -113,56 +120,6 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({ companyCode,
|
||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
||||
}, [selectedNodes, setNodes]);
|
||||
|
||||
// 기존 관계 로드
|
||||
const loadExistingRelationships = useCallback(async () => {
|
||||
try {
|
||||
const existingRelationships = await DataFlowAPI.getRelationshipsByCompany(companyCode);
|
||||
setRelationships(existingRelationships);
|
||||
|
||||
// 기존 관계를 엣지로 변환하여 표시
|
||||
const existingEdges = existingRelationships.map((rel) => ({
|
||||
id: generateUniqueId("edge", rel.relationshipId),
|
||||
source: `table-${rel.fromTableName}`,
|
||||
target: `table-${rel.toTableName}`,
|
||||
sourceHandle: "right",
|
||||
targetHandle: "left",
|
||||
type: "default",
|
||||
data: {
|
||||
relationshipId: rel.relationshipId,
|
||||
relationshipType: rel.relationshipType,
|
||||
connectionType: rel.connectionType,
|
||||
label: rel.relationshipName,
|
||||
fromColumn: rel.fromColumnName,
|
||||
toColumn: rel.toColumnName,
|
||||
},
|
||||
}));
|
||||
|
||||
setEdges(existingEdges);
|
||||
} catch (error) {
|
||||
console.error("기존 관계 로드 실패:", error);
|
||||
toast.error("기존 관계를 불러오는데 실패했습니다.");
|
||||
}
|
||||
}, [companyCode, setEdges]);
|
||||
|
||||
// 컴포넌트 마운트 시 기존 관계 로드
|
||||
useEffect(() => {
|
||||
if (companyCode) {
|
||||
loadExistingRelationships();
|
||||
}
|
||||
}, [companyCode, loadExistingRelationships]);
|
||||
|
||||
// 노드 선택 변경 핸들러
|
||||
const onSelectionChange = useCallback(({ nodes }: { nodes: Node<TableNodeData>[] }) => {
|
||||
const selectedNodeIds = nodes.map((node) => node.id);
|
||||
setSelectedNodes(selectedNodeIds);
|
||||
}, []);
|
||||
|
||||
// 빈 onConnect 함수 (드래그 연결 비활성화)
|
||||
const onConnect = useCallback(() => {
|
||||
// 드래그로 연결하는 것을 방지
|
||||
return;
|
||||
}, []);
|
||||
|
||||
// 컬럼 클릭 처리 (토글 방식, 최대 2개 테이블만 허용)
|
||||
const handleColumnClick = useCallback((tableName: string, columnName: string) => {
|
||||
setSelectedColumns((prev) => {
|
||||
@@ -184,39 +141,191 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({ companyCode,
|
||||
}
|
||||
return { ...prev, [tableName]: newColumns };
|
||||
} else {
|
||||
// 선택 추가 - 새로운 테이블이고 이미 2개 테이블이 선택되어 있으면 거부
|
||||
if (!prev[tableName] && selectedTables.length >= 2) {
|
||||
// 토스트 중복 방지를 위한 ref 사용
|
||||
if (!toastShownRef.current) {
|
||||
toastShownRef.current = true;
|
||||
setTimeout(() => {
|
||||
toast.error("최대 2개의 테이블에서만 컬럼을 선택할 수 있습니다.", {
|
||||
duration: 3000,
|
||||
position: "top-center",
|
||||
});
|
||||
// 3초 후 플래그 리셋
|
||||
setTimeout(() => {
|
||||
toastShownRef.current = false;
|
||||
}, 3000);
|
||||
}, 0);
|
||||
}
|
||||
// 새 선택
|
||||
if (selectedTables.length >= 2 && !selectedTables.includes(tableName)) {
|
||||
toast.error("최대 2개 테이블까지만 선택할 수 있습니다.");
|
||||
return prev;
|
||||
}
|
||||
|
||||
// 새로운 테이블이면 선택 순서에 추가, 기존 테이블이면 맨 뒤로 이동 (다음 렌더링에서)
|
||||
const newColumns = [...currentColumns, columnName];
|
||||
const newSelection = { ...prev, [tableName]: newColumns };
|
||||
|
||||
// 선택 순서 업데이트 (다음 렌더링에서)
|
||||
setTimeout(() => {
|
||||
setSelectionOrder((order) => {
|
||||
// 기존에 있던 테이블이면 제거 후 맨 뒤에 추가 (순서 갱신)
|
||||
const filteredOrder = order.filter((name) => name !== tableName);
|
||||
return [...filteredOrder, tableName];
|
||||
if (!order.includes(tableName)) {
|
||||
return [...order, tableName];
|
||||
}
|
||||
return order;
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return { ...prev, [tableName]: [...currentColumns, columnName] };
|
||||
return newSelection;
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 선택된 관계도의 관계 로드
|
||||
const loadSelectedDiagramRelationships = useCallback(async () => {
|
||||
if (!selectedDiagram) return;
|
||||
|
||||
try {
|
||||
console.log("🔍 관계도 로드 시작:", selectedDiagram.diagramName);
|
||||
toast.loading("관계도를 불러오는 중...", { id: "load-diagram" });
|
||||
const diagramRelationships = await DataFlowAPI.getDiagramRelationships(selectedDiagram.diagramName);
|
||||
console.log("📋 관계도 관계 데이터:", diagramRelationships);
|
||||
console.log("📋 첫 번째 관계 상세:", diagramRelationships[0]);
|
||||
console.log(
|
||||
"📋 관계 객체 키들:",
|
||||
diagramRelationships[0] ? Object.keys(diagramRelationships[0]) : "배열이 비어있음",
|
||||
);
|
||||
setRelationships(diagramRelationships);
|
||||
|
||||
// 관계도의 모든 테이블 추출
|
||||
const tableNames = new Set<string>();
|
||||
diagramRelationships.forEach((rel) => {
|
||||
tableNames.add(rel.from_table_name);
|
||||
tableNames.add(rel.to_table_name);
|
||||
});
|
||||
console.log("📊 추출된 테이블 이름들:", Array.from(tableNames));
|
||||
|
||||
// 테이블 정보 로드
|
||||
const allTables = await DataFlowAPI.getTables();
|
||||
console.log("🏢 전체 테이블 수:", allTables.length);
|
||||
const tableDefinitions: TableDefinition[] = [];
|
||||
|
||||
for (const tableName of tableNames) {
|
||||
const foundTable = allTables.find((t) => t.tableName === tableName);
|
||||
console.log(`🔍 테이블 ${tableName} 검색 결과:`, foundTable);
|
||||
if (foundTable) {
|
||||
// 각 테이블의 컬럼 정보를 별도로 가져옴
|
||||
const columns = await DataFlowAPI.getTableColumns(tableName);
|
||||
console.log(`📋 테이블 ${tableName}의 컬럼 수:`, columns.length);
|
||||
tableDefinitions.push({
|
||||
tableName: foundTable.tableName,
|
||||
displayName: foundTable.displayName,
|
||||
description: foundTable.description,
|
||||
columns: columns,
|
||||
});
|
||||
} else {
|
||||
console.warn(`⚠️ 테이블 ${tableName}을 찾을 수 없습니다`);
|
||||
}
|
||||
}
|
||||
|
||||
// 테이블을 노드로 변환 (자동 레이아웃)
|
||||
const tableNodes = tableDefinitions.map((table, index) => {
|
||||
const x = (index % 3) * 400 + 100; // 3열 배치
|
||||
const y = Math.floor(index / 3) * 300 + 100;
|
||||
|
||||
return {
|
||||
id: `table-${table.tableName}`,
|
||||
type: "tableNode",
|
||||
position: { x, y },
|
||||
data: {
|
||||
table: {
|
||||
tableName: table.tableName,
|
||||
displayName: table.displayName,
|
||||
description: table.description || "",
|
||||
columns: table.columns.map((col) => ({
|
||||
name: col.columnName,
|
||||
type: col.dataType || "varchar",
|
||||
description: col.description || "",
|
||||
})),
|
||||
},
|
||||
onColumnClick: handleColumnClick,
|
||||
selectedColumns: selectedColumns[table.tableName] || [],
|
||||
} as TableNodeData,
|
||||
};
|
||||
});
|
||||
|
||||
console.log("🎨 생성된 테이블 노드 수:", tableNodes.length);
|
||||
console.log("📍 테이블 노드 상세:", tableNodes);
|
||||
setNodes(tableNodes);
|
||||
|
||||
// 관계를 엣지로 변환하여 표시
|
||||
const relationshipEdges = diagramRelationships.map((rel) => ({
|
||||
id: generateUniqueId("edge", rel.relationship_id),
|
||||
source: `table-${rel.from_table_name}`,
|
||||
target: `table-${rel.to_table_name}`,
|
||||
sourceHandle: "right",
|
||||
targetHandle: "left",
|
||||
type: "default",
|
||||
data: {
|
||||
relationshipId: rel.relationship_id,
|
||||
relationshipType: rel.relationship_type,
|
||||
connectionType: rel.connection_type,
|
||||
label: rel.relationship_name,
|
||||
fromColumn: rel.from_column_name,
|
||||
toColumn: rel.to_column_name,
|
||||
},
|
||||
}));
|
||||
|
||||
console.log("🔗 생성된 관계 에지 수:", relationshipEdges.length);
|
||||
console.log("📍 관계 에지 상세:", relationshipEdges);
|
||||
setEdges(relationshipEdges);
|
||||
toast.success(`"${selectedDiagram.diagramName}" 관계도를 불러왔습니다.`, { id: "load-diagram" });
|
||||
} catch (error) {
|
||||
console.error("선택된 관계도 로드 실패:", error);
|
||||
toast.error("관계도를 불러오는데 실패했습니다.", { id: "load-diagram" });
|
||||
}
|
||||
}, [selectedDiagram, companyCode, setNodes, setEdges, selectedColumns, handleColumnClick]);
|
||||
|
||||
// 기존 관계 로드 (새 관계도 생성 시)
|
||||
const loadExistingRelationships = useCallback(async () => {
|
||||
if (selectedDiagram) return; // 선택된 관계도가 있으면 실행하지 않음
|
||||
|
||||
try {
|
||||
const existingRelationships = await DataFlowAPI.getRelationshipsByCompany(companyCode);
|
||||
setRelationships(existingRelationships);
|
||||
|
||||
// 기존 관계를 엣지로 변환하여 표시
|
||||
const existingEdges = existingRelationships.map((rel) => ({
|
||||
id: generateUniqueId("edge", rel.relationship_id),
|
||||
source: `table-${rel.from_table_name}`,
|
||||
target: `table-${rel.to_table_name}`,
|
||||
sourceHandle: "right",
|
||||
targetHandle: "left",
|
||||
type: "default",
|
||||
data: {
|
||||
relationshipId: rel.relationship_id,
|
||||
relationshipType: rel.relationship_type,
|
||||
connectionType: rel.connection_type,
|
||||
label: rel.relationship_name,
|
||||
fromColumn: rel.from_column_name,
|
||||
toColumn: rel.to_column_name,
|
||||
},
|
||||
}));
|
||||
|
||||
setEdges(existingEdges);
|
||||
} catch (error) {
|
||||
console.error("기존 관계 로드 실패:", error);
|
||||
toast.error("기존 관계를 불러오는데 실패했습니다.");
|
||||
}
|
||||
}, [companyCode, setEdges, selectedDiagram]);
|
||||
|
||||
// 컴포넌트 마운트 시 관계 로드
|
||||
useEffect(() => {
|
||||
if (companyCode) {
|
||||
if (selectedDiagram) {
|
||||
loadSelectedDiagramRelationships();
|
||||
} else {
|
||||
loadExistingRelationships();
|
||||
}
|
||||
}
|
||||
}, [companyCode, selectedDiagram, loadExistingRelationships, loadSelectedDiagramRelationships]);
|
||||
|
||||
// 노드 선택 변경 핸들러
|
||||
const onSelectionChange = useCallback(({ nodes }: { nodes: Node<TableNodeData>[] }) => {
|
||||
const selectedNodeIds = nodes.map((node) => node.id);
|
||||
setSelectedNodes(selectedNodeIds);
|
||||
}, []);
|
||||
|
||||
// 빈 onConnect 함수 (드래그 연결 비활성화)
|
||||
const onConnect = useCallback(() => {
|
||||
// 드래그로 연결하는 것을 방지
|
||||
return;
|
||||
}, []);
|
||||
|
||||
// 선택된 컬럼이 변경될 때마다 기존 노드들 업데이트 및 selectionOrder 정리
|
||||
useEffect(() => {
|
||||
setNodes((prevNodes) =>
|
||||
@@ -380,19 +489,19 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({ companyCode,
|
||||
if (!pendingConnection) return;
|
||||
|
||||
const newEdge = {
|
||||
id: generateUniqueId("edge", relationship.relationshipId),
|
||||
id: generateUniqueId("edge", relationship.relationship_id),
|
||||
source: pendingConnection.fromNode.id,
|
||||
target: pendingConnection.toNode.id,
|
||||
sourceHandle: "right",
|
||||
targetHandle: "left",
|
||||
type: "default",
|
||||
data: {
|
||||
relationshipId: relationship.relationshipId,
|
||||
relationshipType: relationship.relationshipType,
|
||||
connectionType: relationship.connectionType,
|
||||
label: relationship.relationshipName,
|
||||
fromColumn: relationship.fromColumnName,
|
||||
toColumn: relationship.toColumnName,
|
||||
relationshipId: relationship.relationship_id,
|
||||
relationshipType: relationship.relationship_type,
|
||||
connectionType: relationship.connection_type,
|
||||
label: relationship.relationship_name,
|
||||
fromColumn: relationship.from_column_name,
|
||||
toColumn: relationship.to_column_name,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user