import { useState, useCallback, useRef, useEffect, useMemo } from 'react'; import ReactFlow, { MiniMap, Controls, Background, useNodesState, useEdgesState, addEdge, Panel, useReactFlow, ReactFlowProvider, Handle, Position, BaseEdge, EdgeLabelRenderer, getBezierPath } from 'reactflow'; import 'reactflow/dist/style.css'; // Import React Flow components // Add custom CSS for React Flow const generateCustomStyles = () => { return ` .react-flow__node { z-index: 1; margin: 20px; /* Add margin to all nodes */ } /* Ensure tables don't overlap */ .react-flow__node.table-node { margin: 30px; min-height: 120px; } .schema-background-node { z-index: -1; pointer-events: all; border-radius: 25px; cursor: grab; user-select: none; transition: all 0.2s ease; padding: 30px; /* Add padding inside schema */ } .schema-background-node:hover { box-shadow: 0 0 30px rgba(0, 0, 0, 0.08); } .schema-background-node:active { cursor: grabbing; } .schema-background-node.dragging { opacity: 0.8; } .schema-label { position: absolute; top: 20px; left: 20px; background: rgba(255, 255, 255, 0.9); padding: 10px 15px; border-radius: 8px; font-size: 14px; font-weight: bold; box-shadow: 0 4px 8px rgba(0,0,0,0.1); display: flex; align-items: center; gap: 10px; z-index: 10; } /* Ensure proper spacing between nodes */ .react-flow__node-table { margin-bottom: 30px !important; margin-right: 30px !important; } /* Styling for draggable new tables */ .draggable-new-table { cursor: grab !important; transition: all 0.2s ease !important; z-index: 10 !important; } .draggable-new-table:hover { transform: scale(1.02) !important; box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15) !important; } .draggable-new-table:active { cursor: grabbing !important; } /* Process node styling */ .react-flow__node-process { z-index: 5 !important; background: white !important; border-radius: 8px !important; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1) !important; } /* Edge styling for left-to-right data flow */ .react-flow__edge { z-index: 4 !important; } .react-flow__edge-path { stroke-width: 1.5 !important; } /* Encourage horizontal edges for left-to-right flow */ .react-flow__edge-path { --react-flow-bezier-edge-control-point-distance: 60 !important; } /* Edge label styling */ .react-flow__edge-textbg { background-color: white !important; padding: 2px !important; border-radius: 4px !important; } .react-flow__edge-text { font-size: 8px !important; } `; }; // Import icons from react-icons import { FaTable, FaArrowRight, FaExchangeAlt, FaFilter, FaCode, FaSync, FaCalculator, FaChartLine, FaEdit, FaTrash, FaChevronDown } from 'react-icons/fa'; import { BiSolidData, BiTransfer } from 'react-icons/bi'; import { AiFillFolder } from 'react-icons/ai'; import { TbTransform } from 'react-icons/tb'; import { CustomDatabaseIcon, CustomDocumentIcon, CustomDimensionIcon, CustomProcessIcon, CustomSchemaIcon } from './CustomIcons'; // Import Material-UI components for breadcrumb import { Breadcrumbs, Link, Typography, Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material'; // Import API data import mockApiData, { useApiData, createSchema, deleteSchema, updateSchema, createTable, updateTable, deleteTable, createColumn, updateColumn, deleteColumn, fetchColumns } from './mockData'; // Import components import ProcessForm from './ProcessForm'; import TableCreationPopup from './TableCreationPopup'; import ColumnManagementPopup from './ColumnManagementPopup'; import TableUpdatePopup from './TableUpdatePopup'; // Schema Background Node const SchemaBackgroundNode = ({ data }) => { // Different background colors for each schema const bgColor = data.slug === 'edw_schema' ? 'rgba(24, 144, 255, 0.1)' // Light blue for Schema_1 : 'rgba(82, 196, 26, 0.1)'; // Light green for Schema_2 // Border color based on schema const borderColor = data.slug === 'edw_schema' ? '#1890ff' // Blue for Schema_1 : '#52c41a'; // Green for Schema_2 // State for delete confirmation const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [deleteError, setDeleteError] = useState(null); // State for schema update popup const [showUpdatePopup, setShowUpdatePopup] = useState(false); const [updatedSchemaName, setUpdatedSchemaName] = useState(data.name || ''); const [updatedSchemaDescription, setUpdatedSchemaDescription] = useState(data.description || ''); const [isUpdatingSchema, setIsUpdatingSchema] = useState(false); const [schemaUpdateError, setSchemaUpdateError] = useState(null); // Function to handle schema deletion const handleDeleteSchema = async (e) => { e.stopPropagation(); // Prevent event bubbling if (!data.database || !data.slug) { setDeleteError('Missing database or schema information'); return; } setIsDeleting(true); setDeleteError(null); try { console.log(`Deleting schema "${data.name}" (${data.slug}) from database ${data.database}`); // Call the deleteSchema function await deleteSchema(data.database, data.slug); console.log('Schema deleted successfully'); // Close the confirmation dialog setShowDeleteConfirm(false); // Refresh the data to update the UI // This assumes window.refreshCanvasData is set in the parent component if (typeof window.refreshCanvasData === 'function') { window.refreshCanvasData(); } else { // If the refresh function is not available, reload the page window.location.reload(); } } catch (error) { console.error('Error deleting schema:', error); setDeleteError(error.message || 'Failed to delete schema'); } finally { setIsDeleting(false); } }; // Function to handle schema update const handleUpdateSchema = async (e) => { e.stopPropagation(); // Prevent event bubbling if (!data || !data.database || !data.slug) { setSchemaUpdateError('Missing database or schema information'); return; } if (!updatedSchemaName.trim()) { setSchemaUpdateError('Schema name is required'); return; } setIsUpdatingSchema(true); setSchemaUpdateError(null); // Store the original values for potential rollback const originalName = data.name; const originalDescription = data.description; try { console.log(`Updating schema "${data.name}" (${data.slug}) in database ${data.database}`); // Close the update popup immediately for better UX setShowUpdatePopup(false); // Call the updateSchema function const updatedSchema = await updateSchema( data.database, data.slug, updatedSchemaName.trim(), updatedSchemaDescription.trim() ); console.log('Schema updated successfully:', updatedSchema); // Update the node data directly to reflect changes immediately // This provides immediate visual feedback after the API call if (data) { data.name = updatedSchemaName.trim(); data.description = updatedSchemaDescription.trim(); } // Notify any listeners about the schema update if (typeof window.onSchemaUpdated === 'function') { try { window.onSchemaUpdated(updatedSchema); } catch (callbackError) { console.error('Error in onSchemaUpdated callback:', callbackError); } } // Refresh the data to update the UI if (typeof window.refreshCanvasData === 'function') { console.log('Calling window.refreshCanvasData to update the UI'); try { // Call refreshCanvasData directly - no need for setTimeout window.refreshCanvasData(); } catch (refreshError) { console.error('Error calling refreshCanvasData:', refreshError); } } } catch (error) { console.error('Error updating schema:', error); setSchemaUpdateError(error.message || 'Failed to update schema'); // Re-open the popup to show the error setShowUpdatePopup(true); // Revert the data changes if there was an error if (data) { data.name = originalName; data.description = originalDescription; } } finally { setIsUpdatingSchema(false); } }; return (
{data.name}
{/* Add Table button */} {/* Update button */} {/* Delete button */}
{/* Schema Update Popup */} {showUpdatePopup && (
e.stopPropagation()} >

Update Schema

Database: {data.database || 'Unknown'}

{schemaUpdateError && (
{schemaUpdateError}
)}
setUpdatedSchemaName(e.target.value)} style={{ width: '100%', padding: '8px', border: '1px solid #d9d9d9', borderRadius: '4px', fontSize: '14px' }} placeholder="Enter schema name" disabled={isUpdatingSchema} />