엣지 클릭 시 연결 목록 조회로 변경
This commit is contained in:
parent
e459025d8a
commit
6a04ae450d
|
|
@ -43,6 +43,11 @@ export default function DataFlowEditPage() {
|
|||
router.push("/admin/dataflow");
|
||||
};
|
||||
|
||||
// 관계도 이름 업데이트 핸들러
|
||||
const handleDiagramNameUpdate = (newDiagramName: string) => {
|
||||
setDiagramName(newDiagramName);
|
||||
};
|
||||
|
||||
if (!diagramId || !diagramName) {
|
||||
return (
|
||||
<div className="flex h-64 items-center justify-center">
|
||||
|
|
@ -74,7 +79,12 @@ export default function DataFlowEditPage() {
|
|||
|
||||
{/* 데이터플로우 디자이너 */}
|
||||
<div className="rounded-lg border border-gray-200 bg-white">
|
||||
<DataFlowDesigner selectedDiagram={diagramName} diagramId={diagramId} onBackToList={handleBackToList} />
|
||||
<DataFlowDesigner
|
||||
selectedDiagram={diagramName}
|
||||
diagramId={diagramId}
|
||||
onBackToList={handleBackToList}
|
||||
onDiagramNameUpdate={handleDiagramNameUpdate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ interface DataFlowDesignerProps {
|
|||
diagramId?: number;
|
||||
relationshipId?: string; // 하위 호환성 유지
|
||||
onBackToList?: () => void;
|
||||
onDiagramNameUpdate?: (diagramName: string) => void; // 관계도 이름 업데이트 콜백 추가
|
||||
}
|
||||
|
||||
// TableRelationship 타입은 dataflow.ts에서 import
|
||||
|
|
@ -82,6 +83,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
onSave, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
selectedDiagram,
|
||||
onBackToList, // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
onDiagramNameUpdate, // 관계도 이름 업데이트 콜백
|
||||
}) => {
|
||||
const { user } = useAuth();
|
||||
|
||||
|
|
@ -136,6 +138,8 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
const [showEdgeActions, setShowEdgeActions] = useState(false); // 엣지 액션 버튼 표시 상태
|
||||
const [edgeActionPosition, setEdgeActionPosition] = useState<{ x: number; y: number }>({ x: 0, y: 0 }); // 액션 버튼 위치
|
||||
const [editingRelationshipId, setEditingRelationshipId] = useState<string | null>(null); // 현재 수정 중인 관계 ID
|
||||
const [showRelationshipListModal, setShowRelationshipListModal] = useState(false); // 관계 목록 모달 표시 상태
|
||||
const [selectedTablePairRelationships, setSelectedTablePairRelationships] = useState<ExtendedJsonRelationship[]>([]); // 선택된 테이블 쌍의 관계들
|
||||
const toastShownRef = useRef(false); // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||
|
||||
// 편집 모드일 때 관계도 이름 로드
|
||||
|
|
@ -333,8 +337,9 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
console.log("📍 테이블 노드 상세:", tableNodes);
|
||||
setNodes(tableNodes);
|
||||
|
||||
// JSON 관계를 엣지로 변환하여 표시 (테이블 간 번들 연결)
|
||||
// JSON 관계를 엣지로 변환하여 표시 (각 관계마다 개별 엣지 생성)
|
||||
const relationshipEdges: Edge[] = [];
|
||||
const tableRelationshipCount: { [key: string]: number } = {}; // 테이블 쌍별 관계 개수
|
||||
|
||||
normalizedRelationships.forEach((rel: ExtendedJsonRelationship) => {
|
||||
const fromTable = rel.fromTable;
|
||||
|
|
@ -347,9 +352,16 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
return;
|
||||
}
|
||||
|
||||
// 테이블 간 하나의 번들 엣지 생성 (컬럼별 개별 엣지 대신)
|
||||
// 테이블 쌍 키 생성 (양방향 동일하게 처리)
|
||||
const tableKey = [fromTable, toTable].sort().join("-");
|
||||
tableRelationshipCount[tableKey] = (tableRelationshipCount[tableKey] || 0) + 1;
|
||||
const relationshipIndex = tableRelationshipCount[tableKey];
|
||||
|
||||
// 각 관계마다 고유한 엣지 생성 (곡선 오프셋으로 구분)
|
||||
const curveOffset = (relationshipIndex - 1) * 30; // 30px씩 오프셋
|
||||
|
||||
relationshipEdges.push({
|
||||
id: generateUniqueId("edge", currentDiagramId),
|
||||
id: `edge-${rel.id}`, // 관계 ID를 기반으로 고유 ID 생성
|
||||
source: `table-${fromTable}`,
|
||||
target: `table-${toTable}`,
|
||||
type: "smoothstep",
|
||||
|
|
@ -359,6 +371,17 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
strokeWidth: 2,
|
||||
strokeDasharray: "none",
|
||||
},
|
||||
// 여러 관계가 있을 때 곡선 오프셋 적용
|
||||
...(relationshipIndex > 1 && {
|
||||
style: {
|
||||
stroke: "#3b82f6",
|
||||
strokeWidth: 2,
|
||||
strokeDasharray: "none",
|
||||
},
|
||||
pathOptions: {
|
||||
offset: curveOffset,
|
||||
},
|
||||
}),
|
||||
data: {
|
||||
relationshipId: rel.id,
|
||||
relationshipName: rel.relationshipName,
|
||||
|
|
@ -367,6 +390,9 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
toTable: toTable,
|
||||
fromColumns: fromColumns,
|
||||
toColumns: toColumns,
|
||||
// 테이블 쌍의 모든 관계 정보 (엣지 클릭 시 사용)
|
||||
tableKey: tableKey,
|
||||
relationshipIndex: relationshipIndex,
|
||||
// 클릭 시 표시할 상세 정보
|
||||
details: {
|
||||
connectionInfo: `${fromTable}(${fromColumns.join(", ")}) → ${toTable}(${toColumns.join(", ")})`,
|
||||
|
|
@ -486,56 +512,45 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
}, []);
|
||||
|
||||
// 엣지 클릭 시 연결 정보 표시 및 관련 컬럼 하이라이트
|
||||
const onEdgeClick = useCallback((event: React.MouseEvent, edge: Edge) => {
|
||||
event.stopPropagation();
|
||||
const edgeData = edge.data as {
|
||||
relationshipId: string;
|
||||
relationshipName: string;
|
||||
fromTable: string;
|
||||
toTable: string;
|
||||
fromColumns: string[];
|
||||
toColumns: string[];
|
||||
connectionType: string;
|
||||
details?: {
|
||||
connectionInfo: string;
|
||||
const onEdgeClick = useCallback(
|
||||
(event: React.MouseEvent, edge: Edge) => {
|
||||
event.stopPropagation();
|
||||
const edgeData = edge.data as {
|
||||
relationshipId: string;
|
||||
relationshipName: string;
|
||||
fromTable: string;
|
||||
toTable: string;
|
||||
fromColumns: string[];
|
||||
toColumns: string[];
|
||||
connectionType: string;
|
||||
tableKey: string;
|
||||
relationshipIndex: number;
|
||||
details?: {
|
||||
connectionInfo: string;
|
||||
connectionType: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
if (edgeData) {
|
||||
// 엣지 정보 설정
|
||||
setSelectedEdgeInfo({
|
||||
relationshipId: edgeData.relationshipId,
|
||||
relationshipName: edgeData.relationshipName || "관계",
|
||||
fromTable: edgeData.fromTable,
|
||||
toTable: edgeData.toTable,
|
||||
fromColumns: edgeData.fromColumns || [],
|
||||
toColumns: edgeData.toColumns || [],
|
||||
connectionType: edgeData.connectionType,
|
||||
connectionInfo: edgeData.details?.connectionInfo || `${edgeData.fromTable} → ${edgeData.toTable}`,
|
||||
});
|
||||
if (edgeData) {
|
||||
// 해당 테이블 쌍의 모든 관계 찾기
|
||||
const fromTable = edgeData.fromTable;
|
||||
const toTable = edgeData.toTable;
|
||||
|
||||
// 관련 컬럼 하이라이트
|
||||
const newSelectedColumns: { [tableName: string]: string[] } = {};
|
||||
const tablePairRelationships = tempRelationships.filter(
|
||||
(rel) =>
|
||||
(rel.fromTable === fromTable && rel.toTable === toTable) ||
|
||||
(rel.fromTable === toTable && rel.toTable === fromTable),
|
||||
);
|
||||
|
||||
// fromTable의 컬럼들 선택
|
||||
if (edgeData.fromTable && edgeData.fromColumns) {
|
||||
newSelectedColumns[edgeData.fromTable] = [...edgeData.fromColumns];
|
||||
console.log(`🔗 ${fromTable} ↔ ${toTable} 간의 관계:`, tablePairRelationships);
|
||||
|
||||
// 관계가 1개든 여러 개든 항상 관계 목록 모달 표시
|
||||
setSelectedTablePairRelationships(tablePairRelationships);
|
||||
setShowRelationshipListModal(true);
|
||||
}
|
||||
|
||||
// toTable의 컬럼들 선택
|
||||
if (edgeData.toTable && edgeData.toColumns) {
|
||||
newSelectedColumns[edgeData.toTable] = [...edgeData.toColumns];
|
||||
}
|
||||
|
||||
setSelectedColumns(newSelectedColumns);
|
||||
|
||||
// 액션 버튼 표시
|
||||
setSelectedEdgeForEdit(edge);
|
||||
setEdgeActionPosition({ x: event.clientX, y: event.clientY });
|
||||
setShowEdgeActions(true);
|
||||
}
|
||||
}, []);
|
||||
},
|
||||
[tempRelationships],
|
||||
);
|
||||
|
||||
// 엣지 마우스 엔터 시 색상 변경
|
||||
const onEdgeMouseEnter = useCallback(
|
||||
|
|
@ -845,6 +860,11 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
setShowSaveModal(false);
|
||||
setCurrentDiagramId(savedDiagram.diagram_id);
|
||||
|
||||
// 관계도 이름 업데이트 (편집 모드일 때만)
|
||||
if (diagramId && diagramId > 0 && onDiagramNameUpdate) {
|
||||
onDiagramNameUpdate(diagramName);
|
||||
}
|
||||
|
||||
console.log("관계도 저장 완료:", savedDiagram);
|
||||
} catch (error) {
|
||||
console.error("관계도 저장 실패:", error);
|
||||
|
|
@ -853,7 +873,7 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
setIsSaving(false);
|
||||
}
|
||||
},
|
||||
[tempRelationships, diagramId, companyCode, user?.userId, nodes],
|
||||
[tempRelationships, diagramId, companyCode, user?.userId, nodes, onDiagramNameUpdate],
|
||||
);
|
||||
|
||||
// 저장 모달 열기
|
||||
|
|
@ -1136,6 +1156,72 @@ export const DataFlowDesigner: React.FC<DataFlowDesignerProps> = ({
|
|||
>
|
||||
<Controls />
|
||||
<Background variant={BackgroundVariant.Dots} gap={20} size={1} color="#E5E7EB" />
|
||||
|
||||
{/* 관계 목록 모달 - 캔버스 내부 우측 상단에 배치 */}
|
||||
{showRelationshipListModal && (
|
||||
<div className="pointer-events-auto absolute top-4 right-4 z-40 w-80 rounded-xl border border-blue-200 bg-white shadow-lg">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between rounded-t-xl border-b border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 p-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="rounded-full bg-blue-100 p-1">
|
||||
<span className="text-sm text-blue-600">🔗</span>
|
||||
</div>
|
||||
<div className="text-sm font-semibold text-gray-800">테이블 간 관계 목록</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setShowRelationshipListModal(false)}
|
||||
className="flex h-6 w-6 items-center justify-center rounded-full text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600"
|
||||
>
|
||||
<svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 관계 목록 */}
|
||||
<div className="p-3">
|
||||
<div className="max-h-96 space-y-2 overflow-y-auto">
|
||||
{selectedTablePairRelationships.map((relationship, index) => (
|
||||
<div
|
||||
key={relationship.id}
|
||||
onClick={() => {
|
||||
// 관계 선택 시 수정 모드로 전환
|
||||
setEditingRelationshipId(relationship.id);
|
||||
|
||||
// 관련 컬럼 하이라이트
|
||||
const newSelectedColumns: { [tableName: string]: string[] } = {};
|
||||
if (relationship.fromTable && relationship.fromColumns) {
|
||||
newSelectedColumns[relationship.fromTable] = [...relationship.fromColumns];
|
||||
}
|
||||
if (relationship.toTable && relationship.toColumns) {
|
||||
newSelectedColumns[relationship.toTable] = [...relationship.toColumns];
|
||||
}
|
||||
setSelectedColumns(newSelectedColumns);
|
||||
|
||||
// 모달 닫기
|
||||
setShowRelationshipListModal(false);
|
||||
}}
|
||||
className="cursor-pointer rounded-lg border border-gray-200 p-3 transition-all hover:border-blue-300 hover:bg-blue-50"
|
||||
>
|
||||
<div className="mb-1 flex items-center justify-between">
|
||||
<h4 className="text-sm font-medium text-gray-900">
|
||||
{relationship.fromTable} → {relationship.toTable}
|
||||
</h4>
|
||||
<svg className="h-4 w-4 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</div>
|
||||
<div className="space-y-1 text-xs text-gray-600">
|
||||
<p>타입: {relationship.connectionType}</p>
|
||||
<p>From: {relationship.fromTable}</p>
|
||||
<p>To: {relationship.toTable}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ReactFlow>
|
||||
|
||||
{/* 선택된 테이블 노드 팝업 - 캔버스 좌측 상단 고정 (새 관계 생성 시에만 표시) */}
|
||||
|
|
|
|||
Loading…
Reference in New Issue