Compare commits

..

No commits in common. "ed0cc6ee15382f8942ce5411b415528e8514e019" and "28b8afcf83aeb3a7f66cab2ca8429da7dc102ef5" have entirely different histories.

2 changed files with 150 additions and 216 deletions

View File

@ -534,7 +534,8 @@ const AddTableModal = ({
// Call the parent component's add table function // Call the parent component's add table function
await onAddTable(tableData); await onAddTable(tableData);
// Reset form (modal will be closed by parent component) // Close modal and reset form
onClose();
resetForm(); resetForm();
} catch (error) { } catch (error) {
console.error('Error adding table:', error); console.error('Error adding table:', error);

View File

@ -551,7 +551,6 @@ const ERDiagramCanvasContent = () => {
SCHEMA_LIST: `${API_BASE_URL}/qbt_schema_list_get`, SCHEMA_LIST: `${API_BASE_URL}/qbt_schema_list_get`,
TABLE_LIST: `${API_BASE_URL}/qbt_table_list_get`, TABLE_LIST: `${API_BASE_URL}/qbt_table_list_get`,
COLUMN_LIST: `${API_BASE_URL}/qbt_column_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_CREATE: `${API_BASE_URL}/qbt_schema_create`,
SCHEMA_DELETE: `${API_BASE_URL}/qbt_schema_delete`, SCHEMA_DELETE: `${API_BASE_URL}/qbt_schema_delete`,
SCHEMA_UPDATE: `${API_BASE_URL}/qbt_schema_update`, SCHEMA_UPDATE: `${API_BASE_URL}/qbt_schema_update`,
@ -774,61 +773,6 @@ 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 // Function to fetch complete database structure with schemas, tables, and columns
const fetchCompleteDatabase = async (dbSlug) => { const fetchCompleteDatabase = async (dbSlug) => {
try { try {
@ -884,8 +828,48 @@ 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 // Fetch databases and generate ER diagram using real API
useEffect(() => { useEffect(() => {
@ -914,9 +898,6 @@ const ERDiagramCanvasContent = () => {
// Fetch complete structure (schemas, tables, columns) for the selected database // Fetch complete structure (schemas, tables, columns) for the selected database
const schemasWithTables = await fetchCompleteDatabase(targetDatabase.con); 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 // Create the complete database object using actual API data
const completeDatabase = { const completeDatabase = {
id: targetDatabase.id, id: targetDatabase.id,
@ -925,8 +906,7 @@ const ERDiagramCanvasContent = () => {
description: targetDatabase.description || `Database: ${targetDatabase.name}`, description: targetDatabase.description || `Database: ${targetDatabase.name}`,
service: selectedService?.name, service: selectedService?.name,
dataSource: selectedDataSource?.name, dataSource: selectedDataSource?.name,
schemas: schemasWithTables, schemas: schemasWithTables
relationships: relationships
}; };
console.log('Complete database structure:', completeDatabase); console.log('Complete database structure:', completeDatabase);
@ -1022,14 +1002,6 @@ const ERDiagramCanvasContent = () => {
} }
}, [nodes, isLoading, fitView]); }, [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 // Auto-alignment layout calculator
const calculateOptimalLayout = (databaseData) => { const calculateOptimalLayout = (databaseData) => {
const layouts = []; const layouts = [];
@ -1103,7 +1075,7 @@ const ERDiagramCanvasContent = () => {
key_type: key.key_type, key_type: key.key_type,
key_columns: [{ key_columns: [{
column_name: key.column_name, column_name: key.column_name,
sequence: key.sequence || 1 sequence: key.sequence
}] }]
})) }))
}; };
@ -1133,9 +1105,10 @@ const ERDiagramCanvasContent = () => {
created_at: new Date().toISOString() created_at: new Date().toISOString()
}; };
console.log('Table created successfully via API:', newTable); // Add the table to existing tables list
setExistingTables(prev => [...prev, newTable]);
// Update the current database structure immediately // Update the current database structure and regenerate the diagram
const updatedDatabase = { ...selectedDatabase }; const updatedDatabase = { ...selectedDatabase };
const schemaIndex = updatedDatabase.schemas.findIndex(s => s.sch === tableData.schema); const schemaIndex = updatedDatabase.schemas.findIndex(s => s.sch === tableData.schema);
@ -1145,51 +1118,14 @@ const ERDiagramCanvasContent = () => {
} }
updatedDatabase.schemas[schemaIndex].tables.push(newTable); 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 // Update the selected database state
setSelectedDatabase(updatedDatabase); setSelectedDatabase(updatedDatabase);
// Update databases list // Regenerate the ER diagram with the new table
setDatabases(prevDatabases => { generateERDiagram(updatedDatabase);
return prevDatabases.map(db => {
if (db.id === selectedDatabase?.id) {
return updatedDatabase;
}
return db;
});
});
// Regenerate the ER diagram with the new table immediately console.log('Table added successfully:', newTable);
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 { } else {
throw new Error(response.data.message || 'Failed to create table'); throw new Error(response.data.message || 'Failed to create table');
} }
@ -1197,9 +1133,6 @@ const ERDiagramCanvasContent = () => {
} catch (error) { } catch (error) {
console.error('Error adding table:', 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 // Check if it's an API error
if (error.response) { if (error.response) {
const errorMessage = error.response.data?.message || `API Error: ${error.response.status}`; const errorMessage = error.response.data?.message || `API Error: ${error.response.status}`;
@ -1214,22 +1147,16 @@ const ERDiagramCanvasContent = () => {
// Generate ER diagram with Database Wrapper structure // Generate ER diagram with Database Wrapper structure
const generateERDiagram = (database) => { const generateERDiagram = (database) => {
console.log('🔄 Starting ER diagram generation...');
console.log('Database received:', database?.name);
console.log('Total schemas:', database?.schemas?.length);
const newNodes = []; const newNodes = [];
const newEdges = []; // No edges - removing all connections const newEdges = [];
// Generate dummy relationships
const relationships = generateDummyERData(database);
// Process the selected database // Process the selected database
console.log('Generating ER diagram for database:', database?.name); console.log('Generating ER diagram for database:', database?.name);
console.log('Total schemas in database:', database?.schemas?.length); console.log('Total schemas in database:', database?.schemas?.length);
console.log('Schema details:', database?.schemas?.map(s => ({ console.log('Schema details:', database?.schemas?.map(s => ({ name: s.name, sch: s.sch, tableCount: s.tables?.length || 0 })));
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) { if (database && database.schemas && database.schemas.length > 0) {
// Calculate database wrapper dimensions // Calculate database wrapper dimensions
@ -1389,21 +1316,16 @@ const ERDiagramCanvasContent = () => {
console.warn(`Table "${table.name}" position (${tableX}, ${tableY}) exceeds schema bounds (${schemaLayout.width}x${schemaLayout.height})`); 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 based on relationships // Add primary key and foreign key indicators to columns
const enhancedColumns = (table.columns || []).map((col, colIndex) => { const enhancedColumns = (table.columns || []).map((col, colIndex) => ({
// Check if this column is a foreign key based on relationships ...col,
const isForeignKey = database.relationships?.some(rel => is_primary_key: colIndex === 0, // First column as primary key
rel.targetTable === table.tbl && is_foreign_key: relationships.some(rel =>
rel.targetSchema === schemaLayout.schema.sch && rel.destination_column_set.some(dest =>
rel.targetColumn === col.col dest.column_name === col.name && dest.table_name === table.name
) || 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({ newNodes.push({
id: tableId, id: tableId,
@ -1436,80 +1358,38 @@ const ERDiagramCanvasContent = () => {
}); });
} }
// Create relationship edges based on API data // Create edges for relationships
if (database.relationships && database.relationships.length > 0) { relationships.forEach((rel, index) => {
console.log('Creating relationship edges:', database.relationships.length); 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}`}`;
database.relationships.forEach((relationship, index) => {
// Find source and target table nodes // Check if both nodes exist
const sourceTableId = `table-${database.slug}-${relationship.sourceSchema}-${relationship.sourceTable}`; const sourceExists = newNodes.some(node => node.id === sourceTableId);
const targetTableId = `table-${database.slug}-${relationship.targetSchema}-${relationship.targetTable}`; const targetExists = newNodes.some(node => node.id === targetTableId);
// Check if both source and target tables exist in the nodes if (sourceExists && targetExists) {
const sourceNode = newNodes.find(node => newEdges.push({
node.type === 'erTable' && id: `relationship-${index}`,
(node.id === sourceTableId || type: 'erRelationship',
node.id.includes(relationship.sourceTable) && source: sourceTableId,
node.data?.schema === relationship.sourceSchema) target: targetTableId,
); data: {
relationship_type: rel.relationship_type,
const targetNode = newNodes.find(node => source_column: rel.source_column_set[0]?.column_name,
node.type === 'erTable' && target_column: rel.destination_column_set[0]?.column_name
(node.id === targetTableId || },
node.id.includes(relationship.targetTable) && style: {
node.data?.schema === relationship.targetSchema) stroke: '#8a2be2',
); strokeWidth: 2
},
if (sourceNode && targetNode) { animated: false
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); setNodes(newNodes);
setEdges(newEdges); setEdges(newEdges);
console.log('✅ ER diagram generation completed');
// Update available schemas and existing tables for the modal // Update available schemas and existing tables for the modal
if (database && database.schemas) { if (database && database.schemas) {
setAvailableSchemas(database.schemas); setAvailableSchemas(database.schemas);
@ -1544,7 +1424,23 @@ const ERDiagramCanvasContent = () => {
}, 300); }, 300);
}; };
// onConnect removed - no connections allowed between tables 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]
);
const handleAddClick = () => { const handleAddClick = () => {
setShowAddMenu(!showAddMenu); setShowAddMenu(!showAddMenu);
@ -1617,8 +1513,14 @@ const ERDiagramCanvasContent = () => {
edges={edges} edges={edges}
onNodesChange={onNodesChange} onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange} onEdgesChange={onEdgesChange}
onConnect={onConnect}
nodeTypes={nodeTypes} nodeTypes={nodeTypes}
edgeTypes={edgeTypes} edgeTypes={edgeTypes}
connectionLineStyle={{
stroke: '#8a2be2',
strokeWidth: 2,
}}
connectionLineType="bezier"
fitView fitView
fitViewOptions={{ fitViewOptions={{
padding: 0.15, padding: 0.15,
@ -1709,7 +1611,38 @@ const ERDiagramCanvasContent = () => {
</div> </div>
</Panel> </Panel>
{/* Relationship Legend Panel */}
<Panel position="bottom-left">
<div style={{
background: 'rgba(26, 26, 26, 0.9)',
padding: '12px 16px',
borderRadius: '8px',
border: '1px solid rgba(138, 43, 226, 0.3)',
color: '#fff',
fontSize: '12px',
backdropFilter: 'blur(5px)',
minWidth: '200px'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '8px' }}>
<FaLink style={{ color: '#8a2be2' }} />
<strong>Relationship Types</strong>
</div>
<div style={{ fontSize: '11px', lineHeight: '1.4' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '4px' }}>
<span style={{ color: '#8a2be2' }}></span>
<span><strong>1:N</strong> - One to Many</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '4px' }}>
<span style={{ color: '#8a2be2' }}></span>
<span><strong>1:1</strong> - One to One</span>
</div>
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '4px' }}>
<span style={{ color: '#8a2be2' }}></span>
<span><strong>N:M</strong> - Many to Many</span>
</div>
</div>
</div>
</Panel>
</ReactFlow> </ReactFlow>
</div> </div>