diff --git a/src/components/AddTableModal.jsx b/src/components/AddTableModal.jsx index 34b824d..5b99162 100644 --- a/src/components/AddTableModal.jsx +++ b/src/components/AddTableModal.jsx @@ -534,8 +534,7 @@ const AddTableModal = ({ // Call the parent component's add table function await onAddTable(tableData); - // Close modal and reset form - onClose(); + // Reset form (modal will be closed by parent component) resetForm(); } catch (error) { console.error('Error adding table:', error); diff --git a/src/components/ERDiagramCanvas.jsx b/src/components/ERDiagramCanvas.jsx index 5779d1f..6247b34 100644 --- a/src/components/ERDiagramCanvas.jsx +++ b/src/components/ERDiagramCanvas.jsx @@ -551,6 +551,7 @@ const ERDiagramCanvasContent = () => { SCHEMA_LIST: `${API_BASE_URL}/qbt_schema_list_get`, TABLE_LIST: `${API_BASE_URL}/qbt_table_list_get`, COLUMN_LIST: `${API_BASE_URL}/qbt_column_list_get`, + DATASOURCE_KEY_LIST: `${API_BASE_URL}/qbt_datasource_key_list_get`, SCHEMA_CREATE: `${API_BASE_URL}/qbt_schema_create`, SCHEMA_DELETE: `${API_BASE_URL}/qbt_schema_delete`, SCHEMA_UPDATE: `${API_BASE_URL}/qbt_schema_update`, @@ -773,6 +774,61 @@ const ERDiagramCanvasContent = () => { } }; + // Function to fetch relationships/foreign keys from the API + const fetchRelationships = async (dbSlug) => { + try { + console.log(`Fetching relationships for database: ${dbSlug}`); + const response = await axios.post( + ENDPOINTS.DATASOURCE_KEY_LIST, + { + token: token, + org: orgSlug, + con: dbSlug + }, + { + headers: { + 'Content-Type': 'application/json' + } + } + ); + + console.log(`Relationships response for database ${dbSlug}:`, response.data); + + let relationships = []; + if (response.data.status === 200 && Array.isArray(response.data.items)) { + relationships = response.data.items.map((item, index) => { + // Extract source and destination information + const source = item.source?.[0]; + const destination = item.destination?.[0]; + + if (source && destination) { + return { + id: `relationship-${index}`, + sourceTable: source.table_slug, + sourceSchema: source.schema_slug, + sourceColumn: source.column_slug, + sourceKeyName: source.key_name, + targetTable: destination.table_slug, + targetSchema: destination.schema_slug, + targetColumn: destination.column_slug, + targetKeyName: destination.key_name, + relationship_type: '1:N' // Default relationship type, can be enhanced later + }; + } + return null; + }).filter(Boolean); // Remove null entries + } + + console.log(`Number of relationships found for database ${dbSlug}: ${relationships.length}`); + console.log('Parsed relationships:', relationships); + return relationships; + } catch (error) { + console.error(`Error fetching relationships for database ${dbSlug}:`, error); + // Return empty array on error to prevent breaking the diagram + return []; + } + }; + // Function to fetch complete database structure with schemas, tables, and columns const fetchCompleteDatabase = async (dbSlug) => { try { @@ -828,48 +884,8 @@ const ERDiagramCanvasContent = () => { } }; - // Generate dummy ER relationship data based on single database structure - const generateDummyERData = (database) => { - const erData = []; - - if (database.schemas && database.schemas.length > 0) { - database.schemas.forEach(schema => { - if (schema.tables && schema.tables.length > 0) { - // Create relationships between tables in the same schema - for (let i = 0; i < schema.tables.length - 1; i++) { - const sourceTable = schema.tables[i]; - const targetTable = schema.tables[i + 1]; - - // Create a dummy relationship with varied types - const relationshipTypes = ['1:N', '1:1', 'N:M']; - const randomType = relationshipTypes[Math.floor(Math.random() * relationshipTypes.length)]; - - erData.push({ - source_column_set: [ - { - table_id: sourceTable.id, - column_name: sourceTable.columns?.[0]?.name || 'id', - table_name: sourceTable.name, - schema_name: schema.sch - } - ], - destination_column_set: [ - { - table_id: targetTable.id, - column_name: `${sourceTable.name}_id`, - table_name: targetTable.name, - schema_name: schema.sch - } - ], - relationship_type: randomType - }); - } - } - }); - } - - return erData; - }; + + // Fetch databases and generate ER diagram using real API useEffect(() => { @@ -898,6 +914,9 @@ const ERDiagramCanvasContent = () => { // Fetch complete structure (schemas, tables, columns) for the selected database const schemasWithTables = await fetchCompleteDatabase(targetDatabase.con); + // Fetch relationships for the selected database + const relationships = await fetchRelationships(targetDatabase.con); + // Create the complete database object using actual API data const completeDatabase = { id: targetDatabase.id, @@ -906,7 +925,8 @@ const ERDiagramCanvasContent = () => { description: targetDatabase.description || `Database: ${targetDatabase.name}`, service: selectedService?.name, dataSource: selectedDataSource?.name, - schemas: schemasWithTables + schemas: schemasWithTables, + relationships: relationships }; console.log('Complete database structure:', completeDatabase); @@ -1002,6 +1022,14 @@ const ERDiagramCanvasContent = () => { } }, [nodes, isLoading, fitView]); + // Regenerate diagram when selectedDatabase changes (including after adding new table) + useEffect(() => { + if (selectedDatabase && selectedDatabase.schemas) { + console.log('Selected database changed, regenerating diagram...'); + generateERDiagram(selectedDatabase); + } + }, [selectedDatabase]); + // Auto-alignment layout calculator const calculateOptimalLayout = (databaseData) => { const layouts = []; @@ -1075,7 +1103,7 @@ const ERDiagramCanvasContent = () => { key_type: key.key_type, key_columns: [{ column_name: key.column_name, - sequence: key.sequence + sequence: key.sequence || 1 }] })) }; @@ -1105,10 +1133,9 @@ const ERDiagramCanvasContent = () => { created_at: new Date().toISOString() }; - // Add the table to existing tables list - setExistingTables(prev => [...prev, newTable]); + console.log('Table created successfully via API:', newTable); - // Update the current database structure and regenerate the diagram + // Update the current database structure immediately const updatedDatabase = { ...selectedDatabase }; const schemaIndex = updatedDatabase.schemas.findIndex(s => s.sch === tableData.schema); @@ -1118,14 +1145,51 @@ const ERDiagramCanvasContent = () => { } updatedDatabase.schemas[schemaIndex].tables.push(newTable); + console.log(`Added table "${newTable.name}" to schema "${tableData.schema}"`); + console.log('Updated database structure:', updatedDatabase); + // Update the selected database state setSelectedDatabase(updatedDatabase); - // Regenerate the ER diagram with the new table - generateERDiagram(updatedDatabase); + // Update databases list + setDatabases(prevDatabases => { + return prevDatabases.map(db => { + if (db.id === selectedDatabase?.id) { + return updatedDatabase; + } + return db; + }); + }); - console.log('Table added successfully:', newTable); + // Regenerate the ER diagram with the new table immediately + console.log('Scheduling diagram regeneration...'); + setTimeout(() => { + console.log('Regenerating diagram with updated database...'); + generateERDiagram(updatedDatabase); + }, 100); } + + // Update existing tables list for the modal + setExistingTables(prev => [...prev, newTable]); + + // Close the modal immediately after successful creation + setIsAddTableModalOpen(false); + + console.log('Table added successfully and modal closed'); + + // Force a small delay to ensure all state updates are processed and then fit view + setTimeout(() => { + if (fitView) { + fitView({ + padding: 0.1, + includeHiddenNodes: false, + minZoom: 0.15, + maxZoom: 0.8, + duration: 800 + }); + } + }, 500); + } else { throw new Error(response.data.message || 'Failed to create table'); } @@ -1133,6 +1197,9 @@ const ERDiagramCanvasContent = () => { } catch (error) { console.error('Error adding table:', error); + // Always close the modal on error to prevent it from being stuck + setIsAddTableModalOpen(false); + // Check if it's an API error if (error.response) { const errorMessage = error.response.data?.message || `API Error: ${error.response.status}`; @@ -1147,16 +1214,22 @@ const ERDiagramCanvasContent = () => { // Generate ER diagram with Database Wrapper structure const generateERDiagram = (database) => { - const newNodes = []; - const newEdges = []; + console.log('🔄 Starting ER diagram generation...'); + console.log('Database received:', database?.name); + console.log('Total schemas:', database?.schemas?.length); - // Generate dummy relationships - const relationships = generateDummyERData(database); + const newNodes = []; + const newEdges = []; // No edges - removing all connections // Process the selected database console.log('Generating ER diagram for database:', database?.name); console.log('Total schemas in database:', database?.schemas?.length); - console.log('Schema details:', database?.schemas?.map(s => ({ name: s.name, sch: s.sch, tableCount: s.tables?.length || 0 }))); + console.log('Schema details:', database?.schemas?.map(s => ({ + name: s.name, + sch: s.sch, + tableCount: s.tables?.length || 0, + tableNames: s.tables?.map(t => t.name) || [] + }))); if (database && database.schemas && database.schemas.length > 0) { // Calculate database wrapper dimensions @@ -1316,16 +1389,21 @@ const ERDiagramCanvasContent = () => { console.warn(`Table "${table.name}" position (${tableX}, ${tableY}) exceeds schema bounds (${schemaLayout.width}x${schemaLayout.height})`); } - // Add primary key and foreign key indicators to columns - const enhancedColumns = (table.columns || []).map((col, colIndex) => ({ - ...col, - is_primary_key: colIndex === 0, // First column as primary key - is_foreign_key: relationships.some(rel => - rel.destination_column_set.some(dest => - dest.column_name === col.name && dest.table_name === table.name - ) - ) - })); + // Add primary key and foreign key indicators to columns based on relationships + const enhancedColumns = (table.columns || []).map((col, colIndex) => { + // Check if this column is a foreign key based on relationships + const isForeignKey = database.relationships?.some(rel => + rel.targetTable === table.tbl && + rel.targetSchema === schemaLayout.schema.sch && + rel.targetColumn === col.col + ) || false; + + return { + ...col, + is_primary_key: col.is_primary_key || colIndex === 0, // Use existing or first column as primary key + is_foreign_key: isForeignKey + }; + }); newNodes.push({ id: tableId, @@ -1358,38 +1436,80 @@ const ERDiagramCanvasContent = () => { }); } - // Create edges for relationships - relationships.forEach((rel, index) => { - const sourceTableId = `table-${rel.source_column_set[0]?.table_id || `${rel.source_column_set[0]?.schema_name}-${rel.source_column_set[0]?.table_name}`}`; - const targetTableId = `table-${rel.destination_column_set[0]?.table_id || `${rel.destination_column_set[0]?.schema_name}-${rel.destination_column_set[0]?.table_name}`}`; - - // Check if both nodes exist - const sourceExists = newNodes.some(node => node.id === sourceTableId); - const targetExists = newNodes.some(node => node.id === targetTableId); - - if (sourceExists && targetExists) { - newEdges.push({ - id: `relationship-${index}`, - type: 'erRelationship', - source: sourceTableId, - target: targetTableId, - data: { - relationship_type: rel.relationship_type, - source_column: rel.source_column_set[0]?.column_name, - target_column: rel.destination_column_set[0]?.column_name - }, - style: { - stroke: '#8a2be2', - strokeWidth: 2 - }, - animated: false - }); - } - }); + // Create relationship edges based on API data + if (database.relationships && database.relationships.length > 0) { + console.log('Creating relationship edges:', database.relationships.length); + + database.relationships.forEach((relationship, index) => { + // Find source and target table nodes + const sourceTableId = `table-${database.slug}-${relationship.sourceSchema}-${relationship.sourceTable}`; + const targetTableId = `table-${database.slug}-${relationship.targetSchema}-${relationship.targetTable}`; + + // Check if both source and target tables exist in the nodes + const sourceNode = newNodes.find(node => + node.type === 'erTable' && + (node.id === sourceTableId || + node.id.includes(relationship.sourceTable) && + node.data?.schema === relationship.sourceSchema) + ); + + const targetNode = newNodes.find(node => + node.type === 'erTable' && + (node.id === targetTableId || + node.id.includes(relationship.targetTable) && + node.data?.schema === relationship.targetSchema) + ); + + if (sourceNode && targetNode) { + const edgeId = `edge-${relationship.id || index}`; + + console.log(`Creating edge: ${sourceNode.id} -> ${targetNode.id}`); + console.log(`Relationship: ${relationship.sourceTable}.${relationship.sourceColumn} -> ${relationship.targetTable}.${relationship.targetColumn}`); + + newEdges.push({ + id: edgeId, + source: sourceNode.id, + target: targetNode.id, + type: 'erRelationship', + data: { + relationship_type: relationship.relationship_type || '1:N', + source_column: relationship.sourceColumn, + target_column: relationship.targetColumn, + source_key_name: relationship.sourceKeyName, + target_key_name: relationship.targetKeyName + }, + style: { + stroke: '#8a2be2', + strokeWidth: 2, + }, + markerEnd: { + type: MarkerType.ArrowClosed, + color: '#8a2be2', + }, + animated: false, + selectable: true + }); + } else { + console.warn(`Could not find nodes for relationship: ${relationship.sourceTable} -> ${relationship.targetTable}`); + console.warn(`Source node found: ${!!sourceNode}, Target node found: ${!!targetNode}`); + console.warn(`Looking for source ID: ${sourceTableId}, target ID: ${targetTableId}`); + } + }); + + console.log(`Created ${newEdges.length} relationship edges out of ${database.relationships.length} relationships`); + } else { + console.log('No relationships found in database data'); + } + console.log('📊 Setting nodes and edges...'); + console.log('New nodes count:', newNodes.length); + console.log('New edges count:', newEdges.length); + setNodes(newNodes); setEdges(newEdges); + console.log('✅ ER diagram generation completed'); + // Update available schemas and existing tables for the modal if (database && database.schemas) { setAvailableSchemas(database.schemas); @@ -1424,23 +1544,7 @@ const ERDiagramCanvasContent = () => { }, 300); }; - const onConnect = useCallback( - (params) => setEdges((eds) => addEdge({ - ...params, - type: 'erRelationship', - data: { - relationship_type: '1:N', - source_column: 'id', - target_column: 'foreign_key_id' - }, - style: { - stroke: '#8a2be2', - strokeWidth: 2 - }, - animated: true - }, eds)), - [setEdges] - ); + // onConnect removed - no connections allowed between tables const handleAddClick = () => { setShowAddMenu(!showAddMenu); @@ -1513,14 +1617,8 @@ const ERDiagramCanvasContent = () => { edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} - onConnect={onConnect} nodeTypes={nodeTypes} edgeTypes={edgeTypes} - connectionLineStyle={{ - stroke: '#8a2be2', - strokeWidth: 2, - }} - connectionLineType="bezier" fitView fitViewOptions={{ padding: 0.15, @@ -1611,38 +1709,7 @@ const ERDiagramCanvasContent = () => { - {/* Relationship Legend Panel */} - -
-
- - Relationship Types -
-
-
- → - 1:N - One to Many -
-
- → - 1:1 - One to One -
-
- ↔ - N:M - Many to Many -
-
-
-
+