diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6d8675ce..e86951d3 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -18,6 +18,7 @@ import { AddTableModal, CardinalityModal, AnnotationsModal, + InferredRelationshipsModal, DiffModal, EditEdgeModal, EditTableModal, @@ -36,6 +37,7 @@ import { deleteAnnotation, deleteView, diffSnapshots, + fetchInferredRelationships, getSnapshot, getView, listAnnotations, @@ -71,7 +73,7 @@ import { } from "./erd/export"; import { exportMermaid } from "./erd/mermaid"; import { GRID_COLUMNS, GRID_X_GAP, GRID_Y_GAP } from "./erd/layoutConstants"; -import type { Connection, ConnectionTestResult, DiagramView, Project, SchemaDiff, Snapshot, SnapshotDetail, TableAnnotation } from "./types"; +import type { Connection, ConnectionTestResult, DiagramView, InferredRelationship, Project, SchemaDiff, Snapshot, SnapshotDetail, TableAnnotation } from "./types"; const TERMINAL_SNAPSHOT_STATUSES = new Set([ "succeeded", @@ -184,6 +186,11 @@ export default function App() { const [connTestResult, setConnTestResult] = useState(null); const [isTestingConn, setIsTestingConn] = useState(false); + const [isInferredModalOpen, setIsInferredModalOpen] = useState(false); + const [inferredRelationships, setInferredRelationships] = useState([]); + const [isInferredLoading, setIsInferredLoading] = useState(false); + const [inferredError, setInferredError] = useState(null); + const [editingEdge, setEditingEdge] = useState(null); const [editingNode, setEditingNode] = useState | null>(null); const [isEditTableModalOpen, setIsEditTableModalOpen] = useState(false); @@ -699,6 +706,22 @@ export default function App() { .finally(() => setIsTestingConn(false)); } + function onOpenInferredRelationships() { + setInferredError(null); + setInferredRelationships([]); + setIsInferredModalOpen(true); + if (!snapshotId) return; + setIsInferredLoading(true); + fetchInferredRelationships(snapshotId) + .then(setInferredRelationships) + .catch(() => setInferredError("추론된 관계를 불러오지 못했습니다.")) + .finally(() => setIsInferredLoading(false)); + } + + function onCloseInferredRelationships() { + setIsInferredModalOpen(false); + } + function onSelectDiffBase(baseSnapshotId: string) { setDiffBaseId(baseSnapshotId); if (!snapshotId) return; @@ -1664,6 +1687,19 @@ export default function App() { > 🗒 + + + + {isLoading ? ( + 분석 중… + ) : null} + {error ? ( +
{error}
+ ) : null} + + {!isLoading && !error ? ( +
+ {relationships.length === 0 ? ( + 추론된 관계가 없습니다. + ) : ( +
    + {relationships.map((r, i) => ( +
  • + + {r.child_table}.{r.child_column} → {r.parent_table}. + {r.parent_column} + + + {r.confidence === 'high' ? '높음' : '보통'} + +
  • + ))} +
+ )} +
+ ) : null} + + + ); +} diff --git a/frontend/src/components/modals/index.ts b/frontend/src/components/modals/index.ts index 001c3923..018f626e 100644 --- a/frontend/src/components/modals/index.ts +++ b/frontend/src/components/modals/index.ts @@ -1,5 +1,6 @@ export { AddTableModal } from './AddTableModal'; export { AnnotationsModal } from './AnnotationsModal'; +export { InferredRelationshipsModal } from './InferredRelationshipsModal'; export { CardinalityModal } from './CardinalityModal'; export { DiffModal } from './DiffModal'; export { EditEdgeModal } from './EditEdgeModal'; diff --git a/frontend/src/types.ts b/frontend/src/types.ts index ad78df91..afb60034 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -165,6 +165,17 @@ export type TableAnnotation = { updated_at: string } +export type InferredRelationship = { + child_schema: string + child_table: string + child_column: string + parent_schema: string + parent_table: string + parent_column: string + confidence: string + reason: string +} + export type SnapshotDetailResponse = Omit & { error_message: unknown }