Qubit_EPM/src/App.jsx

585 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react'
import './App.css'
import InfiniteCanvas from './components/InfiniteCanvas'
import AdvancedCharts from './components/AdvancedCharts'
import DataflowCanvas from './components/DataflowCanvas'
import ERDiagramCanvas from './components/ERDiagramCanvas'
import { FaDatabase, FaChartBar, FaProjectDiagram, FaSitemap, FaFolder, FaCog, FaChevronDown, FaChevronRight, FaTachometerAlt } from 'react-icons/fa'
import { Breadcrumbs, Link, Typography } from '@mui/material'
import { createTheme, ThemeProvider } from '@mui/material/styles'
// Create a custom Material UI theme to match your application's dark theme
const darkTheme = createTheme({
palette: {
mode: 'dark',
primary: {
main: '#00a99d',
},
text: {
primary: '#ffffff',
secondary: '#aaaaaa',
},
background: {
default: '#121212',
paper: '#1a1a1a',
},
},
typography: {
fontFamily: '"Inter", "Roboto", "Helvetica", "Arial", sans-serif',
},
});
function App() {
// Initialize activeTab based on URL pathname if present
const getInitialTab = () => {
const pathname = window.location.pathname;
if (pathname === '/charts') return 'charts';
if (pathname === '/data_mappings') return 'data_mappigs';
if (pathname === '/er_diagram') return 'er_diagram';
if (pathname === '/settings') return 'settings';
// Default to canvas/qubit_service
window.history.pushState(null, '', '/qubit_service');
return 'canvas';
};
const [activeTab, setActiveTab] = useState(getInitialTab());
// Add state to track the current database for DataflowCanvas
const [currentDbSlug, setCurrentDbSlug] = useState(null);
// Add state to track if the current database has schemas
const [hasSchemas, setHasSchemas] = useState(true);
// Add state to track whether the sidebar dropdown is open
const [isDropdownOpen, setIsDropdownOpen] = useState(true);
// Handle browser back/forward navigation
useEffect(() => {
const handleLocationChange = () => {
const pathname = window.location.pathname;
if (pathname === '/qubit_service') setActiveTab('canvas');
else if (pathname === '/charts') setActiveTab('charts');
else if (pathname === '/data_mappings') setActiveTab('data_mappigs');
else if (pathname === '/er_diagram') setActiveTab('er_diagram');
// Settings tab would be handled here too
};
window.addEventListener('popstate', handleLocationChange);
return () => {
window.removeEventListener('popstate', handleLocationChange);
};
}, []);
// Listen for custom events to switch tabs
useEffect(() => {
// Handler for switching to DataFlow tab
const handleViewDataFlow = (event) => {
// Switch to the dataflow tab
setActiveTab('dataflow');
// Update URL path
window.history.pushState(null, '', '/data_mappings');
// Set the current database slug to force DataflowCanvas to re-render
setCurrentDbSlug(event.detail.databaseSlug);
// Set whether this database has schemas
setHasSchemas(!event.detail.isEmpty);
// Log the database info
console.log('Viewing DataFlow for database:', event.detail);
console.log('Database has schemas:', !event.detail.isEmpty);
};
// Handler for switching to any tab
const handleSwitchToTab = (event) => {
// Switch to the specified tab
setActiveTab(event.detail);
// Update URL path based on the tab
let path = '/qubit_service';
if (event.detail === 'charts') path = '/charts';
if (event.detail === 'data_mappigs') path = '/data_mappings';
if (event.detail === 'er_diagram') path = '/er_diagram';
if (event.detail === 'settings') path = '/settings';
window.history.pushState(null, '', path);
console.log('Switching to tab:', event.detail);
};
// Add event listeners
window.addEventListener('viewDataFlow', handleViewDataFlow);
window.addEventListener('switchToTab', handleSwitchToTab);
// Clean up
return () => {
window.removeEventListener('viewDataFlow', handleViewDataFlow);
window.removeEventListener('switchToTab', handleSwitchToTab);
};
}, []);
return (
<ThemeProvider theme={darkTheme}>
<div className="app-container" style={{
width: '100vw',
height: '100vh',
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
background: '#121212', /* Dark background */
color: '#ffffff'
}}>
<header>
{/* Logo and Title */}
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{
width: '40px',
height: '40px',
background: 'linear-gradient(45deg, #00a99d, #52c41a)',
borderRadius: '8px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginRight: '15px',
boxShadow: '0 0 10px rgba(82, 196, 26, 0.5)',
animation: 'pulse 2s infinite'
}}>
<FaDatabase style={{ fontSize: '20px', color: 'white' }} />
</div>
<h1 style={{
margin: 0,
fontSize: '1.5rem',
background: 'linear-gradient(90deg, #00a99d, #52c41a, #fa8c16)',
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
textShadow: '0 0 20px rgba(82, 196, 26, 0.3)'
}}>
{/* The Metricverse
*/}
Qubit Service
</h1>
</div>
{/* User profile and controls */}
<div style={{ display: 'flex', alignItems: 'center' }}>
<div className="user-profile">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
style={{ marginRight: '8px' }}
>
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
<circle cx="12" cy="7" r="4"></circle>
</svg>
<span>John Doe</span>
</div>
<div className="notification-button">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
</svg>
</div>
</div>
</header>
<div style={{ flex: 1, display: 'flex', flexDirection: 'row', overflow: 'hidden' }}>
{/* Sidebar navigation with dropdown */}
<div className="sidebar-nav" style={{
width: '220px',
background: '#1a1a1a',
borderRight: '1px solid #333',
padding: '20px 0',
display: 'flex',
flexDirection: 'column'
}}>
<div style={{ display: 'flex', flexDirection: 'column', gap: '5px', paddingLeft: '15px' }}>
{/* Main Qbit Item with Dropdown */}
<div
className="nav-button main-item"
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
style={{
display: 'flex',
alignItems: 'center',
padding: '12px 15px',
borderRadius: '6px',
cursor: 'pointer',
background: 'rgba(0, 169, 157, 0.15)',
transition: 'all 0.2s ease',
marginBottom: '5px'
}}
>
<FaFolder style={{
fontSize: '20px',
color: '#00a99d',
marginRight: '12px'
}} />
<span style={{
color: '#fff',
fontWeight: '600',
flex: 1
}}>
Qbit
</span>
{isDropdownOpen ?
<FaChevronDown style={{ color: '#aaa', fontSize: '14px' }} /> :
<FaChevronRight style={{ color: '#aaa', fontSize: '14px' }} />
}
</div>
{/* Dropdown items container with animation */}
<div
className="dropdown-container"
style={{
display: isDropdownOpen ? 'flex' : 'none',
flexDirection: 'column',
gap: '4px',
paddingLeft: '15px',
marginLeft: '10px',
borderLeft: '1px solid rgba(170, 170, 170, 0.2)',
transition: 'all 0.3s ease'
}}
>
{/* Overview Item */}
<div
className={`nav-button ${activeTab === 'canvas' ? 'active' : ''}`}
onClick={() => {
setActiveTab('canvas');
window.history.pushState(null, '', '/qubit_service');
}}
style={{
display: 'flex',
alignItems: 'center',
padding: '8px 15px',
borderRadius: '6px',
cursor: 'pointer',
background: activeTab === 'canvas' ? 'rgba(0, 169, 157, 0.1)' : 'transparent',
transition: 'all 0.2s ease'
}}
>
<FaProjectDiagram style={{
fontSize: '16px',
color: activeTab === 'canvas' ? '#00a99d' : '#aaa',
marginRight: '12px'
}} />
<span style={{
color: activeTab === 'canvas' ? '#fff' : '#aaa',
fontWeight: activeTab === 'canvas' ? '500' : 'normal'
}}>
Overview
</span>
</div>
{/* ER Diagram Item */}
<div
className={`nav-button ${activeTab === 'er_diagram' ? 'active' : ''}`}
onClick={() => {
setActiveTab('er_diagram');
window.history.pushState(null, '', '/er_diagram');
}}
style={{
display: 'flex',
alignItems: 'center',
padding: '8px 15px',
borderRadius: '6px',
cursor: 'pointer',
background: activeTab === 'er_diagram' ? 'rgba(0, 169, 157, 0.1)' : 'transparent',
transition: 'all 0.2s ease'
}}
>
<FaSitemap style={{
fontSize: '16px',
color: activeTab === 'er_diagram' ? '#00a99d' : '#aaa',
marginRight: '12px'
}} />
<span style={{
color: activeTab === 'er_diagram' ? '#fff' : '#aaa',
fontWeight: activeTab === 'er_diagram' ? '500' : 'normal'
}}>
ER Diagram
</span>
</div>
{/* Data Mapping Item */}
<div
className={`nav-button ${activeTab === 'dataflow' ? 'active' : ''}`}
onClick={() => {
setActiveTab('dataflow');
window.history.pushState(null, '', '/data_mappings');
}}
style={{
display: 'flex',
alignItems: 'center',
padding: '8px 15px',
borderRadius: '6px',
cursor: 'pointer',
background: activeTab === 'dataflow' ? 'rgba(0, 169, 157, 0.1)' : 'transparent',
transition: 'all 0.2s ease'
}}
>
<FaDatabase style={{
fontSize: '16px',
color: activeTab === 'dataflow' ? '#00a99d' : '#aaa',
marginRight: '12px'
}} />
<span style={{
color: activeTab === 'dataflow' ? '#fff' : '#aaa',
fontWeight: activeTab === 'dataflow' ? '500' : 'normal'
}}>
Data Mapping
</span>
</div>
{/* Settings Item */}
<div
className={`nav-button ${activeTab === 'settings' ? 'active' : ''}`}
onClick={() => {
alert('Settings functionality not implemented yet');
}}
style={{
display: 'flex',
alignItems: 'center',
padding: '8px 15px',
borderRadius: '6px',
cursor: 'pointer',
background: activeTab === 'settings' ? 'rgba(0, 169, 157, 0.1)' : 'transparent',
transition: 'all 0.2s ease'
}}
>
<FaTachometerAlt
style={{
fontSize: '16px',
color: activeTab === 'settings' ? '#00a99d' : '#aaa',
marginRight: '12px'
}}
/>
<span style={{
color: activeTab === 'settings' ? '#fff' : '#aaa',
fontWeight: activeTab === 'settings' ? '500' : 'normal'
}}>
Dashboard
</span>
</div>
{/* Advanced Charts Item */}
{/* <div
className={`nav-button ${activeTab === 'charts' ? 'active' : ''}`}
onClick={() => {
setActiveTab('charts');
window.history.pushState(null, '', '/charts');
}}
style={{
display: 'flex',
alignItems: 'center',
padding: '8px 15px',
borderRadius: '6px',
cursor: 'pointer',
background: activeTab === 'charts' ? 'rgba(0, 169, 157, 0.1)' : 'transparent',
transition: 'all 0.2s ease'
}}
>
<FaChartBar style={{
fontSize: '16px',
color: activeTab === 'charts' ? '#00a99d' : '#aaa',
marginRight: '12px'
}} />
<span style={{
color: activeTab === 'charts' ? '#fff' : '#aaa',
fontWeight: activeTab === 'charts' ? '500' : 'normal'
}}>
Advanced Charts
</span>
</div> */}
</div>
</div>
</div>
<main style={{
flex: 1,
overflow: 'hidden',
position: 'relative',
padding: '20px'
}}>
{activeTab === 'canvas' && (
<>
<div style={{
marginBottom: '20px'
}}>
<h1 style={{
fontSize: '28px',
fontWeight: '600',
margin: '0 0 8px 0',
color: '#ffffff'
}}>
Overview
</h1>
<Breadcrumbs
aria-label="breadcrumb"
separator=""
sx={{
'& .MuiBreadcrumbs-separator': {
color: '#666',
margin: '0 8px',
},
fontSize: '14px',
}}
>
<Link
underline="hover"
color="primary"
href="#"
onClick={(e) => {
e.preventDefault();
setIsDropdownOpen(true); // Ensure dropdown is open
}}
sx={{ fontWeight: 500 }}
>
Qubit
</Link>
<Typography color="text.secondary">Overview</Typography>
</Breadcrumbs>
</div>
<InfiniteCanvas />
<div style={{
position: 'absolute',
bottom: '20px',
left: '20px',
background: 'rgba(26, 26, 26, 0.8)',
padding: '12px 16px',
borderRadius: '8px',
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
fontSize: '12px',
pointerEvents: 'none',
backdropFilter: 'blur(5px)',
border: '1px solid rgba(82, 196, 26, 0.3)',
color: '#ffffff',
maxWidth: '300px',
animation: 'fadeIn 0.5s ease'
}}>
<p style={{
margin: 0,
display: 'flex',
alignItems: 'center',
fontSize: '13px'
}}>
<span style={{
display: 'inline-block',
width: '8px',
height: '8px',
background: '#52c41a',
borderRadius: '50%',
marginRight: '8px',
boxShadow: '0 0 8px #52c41a'
}}></span>
{/* Infinite Canvas - Zoom and pan without limits */}
The Only Limit is Your Imagination.
</p>
<p style={{
margin: '5px 0 0 16px',
fontSize: '11px',
opacity: 0.7
}}>
Scroll to zoom Drag to pan Connect nodes
</p>
</div>
</>
)}
{activeTab === 'charts' && (
<div style={{ padding: '20px', height: '100%', overflow: 'auto' }}>
{/* <AdvancedCharts /> */}
</div>
)}
{activeTab === 'dataflow' && (
<>
<DataflowCanvas
key={`dataflow-${currentDbSlug || 'default'}-${hasSchemas ? 'has-schemas' : 'no-schemas'}-${Date.now()}`}
dbSlug={currentDbSlug}
hasSchemas={hasSchemas}
/>
<div style={{
position: 'absolute',
bottom: '20px',
left: '20px',
background: 'rgba(26, 26, 26, 0.8)',
padding: '12px 16px',
borderRadius: '8px',
boxShadow: '0 4px 12px rgba(0,0,0,0.3)',
fontSize: '12px',
pointerEvents: 'none',
backdropFilter: 'blur(5px)',
border: '1px solid rgba(250, 140, 22, 0.3)',
color: '#ffffff',
maxWidth: '300px',
animation: 'fadeIn 0.5s ease'
}}>
<p style={{
margin: 0,
display: 'flex',
alignItems: 'center',
fontSize: '13px'
}}>
<span style={{
display: 'inline-block',
width: '8px',
height: '8px',
background: '#fa8c16',
borderRadius: '50%',
marginRight: '8px',
boxShadow: '0 0 8px #fa8c16'
}}></span>
Visualize data flows between tables
</p>
<p style={{
margin: '5px 0 0 16px',
fontSize: '11px',
opacity: 0.7
}}>
Add tables Create processes Connect data flows
</p>
</div>
</>
)}
{activeTab === 'er_diagram' && (
<>
<div style={{
marginBottom: '20px'
}}>
<h1 style={{
fontSize: '28px',
fontWeight: '600',
margin: '0 0 8px 0',
color: '#ffffff'
}}>
ER Diagram
</h1>
</div>
<ERDiagramCanvas />
</>
)}
</main>
</div>
</div>
</ThemeProvider>
)
}
export default App