diff --git a/frontend/src/pages/DatabaseSchema.tsx b/frontend/src/pages/DatabaseSchema.tsx index 3e6c326..e795fc1 100644 --- a/frontend/src/pages/DatabaseSchema.tsx +++ b/frontend/src/pages/DatabaseSchema.tsx @@ -1,4 +1,4 @@ -import { useState, useMemo, useEffect, useCallback } from 'react'; +import { useState, useMemo, useEffect } from 'react'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { ReactFlow, @@ -15,7 +15,7 @@ import { } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import Dagre from '@dagrejs/dagre'; -import { Database as DatabaseIcon, Loader2, Key, Link, RefreshCw, Search, X } from 'lucide-react'; +import { Database as DatabaseIcon, Loader2, Key, Link, RefreshCw } from 'lucide-react'; import { databasesApi, schemaApi, TableInfo, SchemaData } from '@/services/api'; import { Database } from '@/types'; @@ -93,7 +93,6 @@ function getNodeHeight(table: TableInfo): number { const headerHeight = 44; const columnHeight = 26; const fkBarHeight = table.foreign_keys.length > 0 ? 28 : 0; - // No max limit - show all columns return headerHeight + (table.columns.length * columnHeight) + fkBarHeight + 8; } @@ -101,9 +100,9 @@ function getLayoutedElements(schema: SchemaData): { nodes: Node[]; edges: Edge[] const g = new Dagre.graphlib.Graph().setDefaultEdgeLabel(() => ({})); g.setGraph({ - rankdir: 'LR', // Left to Right - nodesep: 80, // Horizontal separation between nodes - ranksep: 120, // Vertical separation between ranks + rankdir: 'LR', + nodesep: 80, + ranksep: 120, marginx: 50, marginy: 50, }); @@ -151,24 +150,18 @@ function getLayoutedElements(schema: SchemaData): { nodes: Node[]; edges: Edge[] // Create edges const edges: Edge[] = []; - let totalFks = 0; - let matchedFks = 0; schema.tables.forEach((table) => { const sourceId = `${table.schema}.${table.name}`; table.foreign_keys.forEach((fk) => { - totalFks++; - // Try to find target table - check both same schema and other schemas let targetTable = schema.tables.find(t => t.name === fk.references_table && t.schema === table.schema ); - // If not found in same schema, try any schema if (!targetTable) { targetTable = schema.tables.find(t => t.name === fk.references_table); } if (targetTable) { - matchedFks++; const targetId = `${targetTable.schema}.${targetTable.name}`; edges.push({ id: `${fk.constraint_name}`, @@ -186,13 +179,11 @@ function getLayoutedElements(schema: SchemaData): { nodes: Node[]; edges: Edge[] labelBgStyle: { fill: 'white', fillOpacity: 0.9 }, labelBgPadding: [4, 2] as [number, number], }); - } else { - console.warn(`[Schema] FK not matched: ${sourceId}.${fk.column} -> ${fk.references_table}.${fk.references_column}`); } }); }); - console.log(`[Schema] Created ${edges.length} edges from ${matchedFks}/${totalFks} FKs`); + console.log(`[Schema] Created ${nodes.length} nodes, ${edges.length} edges`); return { nodes, edges }; } @@ -200,8 +191,6 @@ function getLayoutedElements(schema: SchemaData): { nodes: Node[]; edges: Edge[] export default function DatabaseSchema() { const queryClient = useQueryClient(); const [selectedDbId, setSelectedDbId] = useState(''); - const [searchQuery, setSearchQuery] = useState(''); - const [selectedTables, setSelectedTables] = useState>(new Set()); const { data: databases = [], isLoading: isLoadingDatabases } = useQuery({ queryKey: ['databases'], @@ -229,84 +218,14 @@ export default function DatabaseSchema() { const schema = schemaResponse?.data; - // Filter tables based on search - const filteredTableList = useMemo(() => { - if (!schema || !searchQuery.trim()) return []; - const query = searchQuery.toLowerCase(); - return schema.tables - .filter(t => t.name.toLowerCase().includes(query)) - .slice(0, 50); // Limit suggestions - }, [schema, searchQuery]); - - // Get related tables (tables connected via FK) - const getRelatedTables = useCallback((tableNames: Set, allTables: TableInfo[]): Set => { - const related = new Set(); - tableNames.forEach(name => related.add(name)); - - allTables.forEach(table => { - const tableKey = `${table.schema}.${table.name}`; - if (tableNames.has(table.name) || tableNames.has(tableKey)) { - // Add tables that this table references - table.foreign_keys.forEach(fk => { - related.add(fk.references_table); - }); - } - // Add tables that reference selected tables - table.foreign_keys.forEach(fk => { - if (tableNames.has(fk.references_table)) { - related.add(table.name); - } - }); - }); - - return related; - }, []); - - // Filter schema for selected tables + related tables - const filteredSchema = useMemo((): SchemaData | null => { - if (!schema) return null; - if (selectedTables.size === 0) return null; // Don't render anything if nothing selected - - const relatedTables = getRelatedTables(selectedTables, schema.tables); - const filteredTables = schema.tables.filter(t => - relatedTables.has(t.name) || relatedTables.has(`${t.schema}.${t.name}`) - ); - - return { - tables: filteredTables, - updated_at: schema.updated_at, - }; - }, [schema, selectedTables, getRelatedTables]); - const { nodes: layoutedNodes, edges: layoutedEdges } = useMemo(() => { - if (!filteredSchema || filteredSchema.tables.length === 0) return { nodes: [], edges: [] }; - return getLayoutedElements(filteredSchema); - }, [filteredSchema]); - - // Add table to selection - const addTable = (tableName: string) => { - setSelectedTables(prev => new Set([...prev, tableName])); - setSearchQuery(''); - }; - - // Remove table from selection - const removeTable = (tableName: string) => { - setSelectedTables(prev => { - const next = new Set(prev); - next.delete(tableName); - return next; - }); - }; - - // Clear all selected tables - const clearSelection = () => { - setSelectedTables(new Set()); - }; + if (!schema) return { nodes: [], edges: [] }; + return getLayoutedElements(schema); + }, [schema]); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); - // Update nodes when schema changes useEffect(() => { setNodes(layoutedNodes); setEdges(layoutedEdges); @@ -328,11 +247,7 @@ export default function DatabaseSchema() { - {selectedDbId && schema && ( -
- - setSearchQuery(e.target.value)} - placeholder="Поиск таблиц..." - className="w-full pl-9 pr-3 py-1.5 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-primary-500" - /> - {filteredTableList.length > 0 && ( -
- {filteredTableList.map(table => ( - - ))} -
- )} -
- )} - {selectedDbId && ( - - ))} - - {filteredSchema && ( - - Показано: {filteredSchema.tables.length} таблиц (включая связанные) - - )} - - )} - {/* Schema visualization */}
{!selectedDbId && ( @@ -443,16 +295,6 @@ export default function DatabaseSchema() {
)} - {selectedDbId && !isLoadingSchema && schema && selectedTables.size === 0 && ( -
-
- -

Начните вводить название таблицы в поиске

-

Выберите таблицы для отображения на схеме

-
-
- )} - {selectedDbId && !isLoadingSchema && schema && nodes.length > 0 && ( )} @@ -483,12 +328,6 @@ export default function DatabaseSchema() { В базе данных нет таблиц )} - - {selectedDbId && !isLoadingSchema && schema && selectedTables.size > 0 && nodes.length === 0 && ( -
- Таблицы не найдены -
- )} );