Compare commits
No commits in common. "333c3061af663f7f82570f923c163049a699e852" and "5fa97985655e9191c9eb611bdfbe0248056d715c" have entirely different histories.
333c3061af
...
5fa9798565
235
src/App.css
235
src/App.css
|
|
@ -493,238 +493,3 @@ main {
|
|||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Header specific styles */
|
||||
.header-logo-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background: linear-gradient(45deg, #00a99d, #52c41a);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 15px;
|
||||
box-shadow: 0 0 10px rgba(82, 196, 26, 0.5);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
.header-logo-icon {
|
||||
font-size: 20px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
margin: 0;
|
||||
font-size: 1.5rem;
|
||||
background: linear-gradient(90deg, #00a99d, #52c41a, #fa8c16);
|
||||
/* -webkit-background-clip: text; */
|
||||
-webkit-text-fill-color: transparent;
|
||||
text-shadow: 0 0 20px rgba(82, 196, 26, 0.3);
|
||||
}
|
||||
|
||||
.header-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-profile-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* Main layout styles */
|
||||
.main-layout {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
width: 220px;
|
||||
background: #1a1a1a;
|
||||
border-right: 1px solid #333;
|
||||
padding: 20px 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar-nav-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.nav-button.main-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px 15px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
background: rgba(0, 169, 157, 0.15);
|
||||
transition: all 0.2s ease;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.main-item-icon {
|
||||
font-size: 20px;
|
||||
color: #00a99d;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.main-item-text {
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.dropdown-chevron {
|
||||
color: #aaa;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.dropdown-container {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
padding-left: 15px;
|
||||
margin-left: 10px;
|
||||
border-left: 1px solid rgba(170, 170, 170, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.dropdown-container.open {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.dropdown-container.closed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-button.dropdown-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 15px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-button.dropdown-item:not(.active) {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.nav-button.dropdown-item.active {
|
||||
background: rgba(0, 169, 157, 0.1);
|
||||
}
|
||||
|
||||
.dropdown-item-icon {
|
||||
font-size: 16px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.dropdown-item-icon.active {
|
||||
color: #00a99d;
|
||||
}
|
||||
|
||||
.dropdown-item-icon.inactive {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.dropdown-item-text {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.dropdown-item-text.active {
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dropdown-item-text.inactive {
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
/* Main content area */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
margin: 0 0 8px 0;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.charts-container {
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* Info panels */
|
||||
.info-panel {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 20px;
|
||||
background: rgba(26, 26, 26, 0.8);
|
||||
padding: 12px 16px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
backdrop-filter: blur(5px);
|
||||
color: #ffffff;
|
||||
max-width: 300px;
|
||||
animation: fadeIn 0.5s ease;
|
||||
}
|
||||
|
||||
.info-panel.canvas {
|
||||
border: 1px solid rgba(82, 196, 26, 0.3);
|
||||
}
|
||||
|
||||
.info-panel.dataflow {
|
||||
border: 1px solid rgba(250, 140, 22, 0.3);
|
||||
}
|
||||
|
||||
.info-panel-main {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.info-panel-indicator {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.info-panel-indicator.canvas {
|
||||
background: #52c41a;
|
||||
box-shadow: 0 0 8px #52c41a;
|
||||
}
|
||||
|
||||
.info-panel-indicator.dataflow {
|
||||
background: #fa8c16;
|
||||
box-shadow: 0 0 8px #fa8c16;
|
||||
}
|
||||
|
||||
.info-panel-sub {
|
||||
margin: 5px 0 0 16px;
|
||||
font-size: 11px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
|
|
|||
367
src/App.jsx
367
src/App.jsx
|
|
@ -4,7 +4,6 @@ import InfiniteCanvas from './components/InfiniteCanvas'
|
|||
import AdvancedCharts from './components/AdvancedCharts'
|
||||
import DataflowCanvas from './components/DataflowCanvas'
|
||||
import ERDiagramCanvas from './components/ERDiagramCanvas'
|
||||
import PipelinesCanvas from './components/PipelinesCanvas'
|
||||
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'
|
||||
|
|
@ -37,7 +36,6 @@ function App() {
|
|||
if (pathname === '/charts') return 'charts';
|
||||
if (pathname === '/data_mappings') return 'data_mappigs';
|
||||
if (pathname === '/er_diagram') return 'er_diagram';
|
||||
if (pathname === '/pipelines') return 'pipelines';
|
||||
if (pathname === '/settings') return 'settings';
|
||||
// Default to canvas/qubit_service
|
||||
window.history.pushState(null, '', '/qubit_service');
|
||||
|
|
@ -60,7 +58,6 @@ function App() {
|
|||
else if (pathname === '/charts') setActiveTab('charts');
|
||||
else if (pathname === '/data_mappings') setActiveTab('data_mappigs');
|
||||
else if (pathname === '/er_diagram') setActiveTab('er_diagram');
|
||||
else if (pathname === '/pipelines') setActiveTab('pipelines');
|
||||
// Settings tab would be handled here too
|
||||
};
|
||||
|
||||
|
|
@ -101,7 +98,6 @@ function App() {
|
|||
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 === 'pipelines') path = '/pipelines';
|
||||
if (event.detail === 'settings') path = '/settings';
|
||||
|
||||
window.history.pushState(null, '', path);
|
||||
|
|
@ -121,20 +117,48 @@ function App() {
|
|||
|
||||
return (
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<div className="app-container">
|
||||
<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 className="header-logo-container">
|
||||
<div className="header-logo">
|
||||
<FaDatabase className="header-logo-icon" />
|
||||
<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 className="header-title">
|
||||
Qubit Service
|
||||
<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 className="header-controls">
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<div className="user-profile">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -146,7 +170,7 @@ function App() {
|
|||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="user-profile-icon"
|
||||
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>
|
||||
|
|
@ -173,119 +197,235 @@ function App() {
|
|||
</div>
|
||||
</header>
|
||||
|
||||
<div className="main-layout">
|
||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'row', overflow: 'hidden' }}>
|
||||
{/* Sidebar navigation with dropdown */}
|
||||
<div className="sidebar-nav">
|
||||
<div className="sidebar-nav-content">
|
||||
<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 className="main-item-icon" />
|
||||
<span className="main-item-text">
|
||||
<FaFolder style={{
|
||||
fontSize: '20px',
|
||||
color: '#00a99d',
|
||||
marginRight: '12px'
|
||||
}} />
|
||||
<span style={{
|
||||
color: '#fff',
|
||||
fontWeight: '600',
|
||||
flex: 1
|
||||
}}>
|
||||
Qbit
|
||||
</span>
|
||||
{isDropdownOpen ?
|
||||
<FaChevronDown className="dropdown-chevron" /> :
|
||||
<FaChevronRight className="dropdown-chevron" />
|
||||
<FaChevronDown style={{ color: '#aaa', fontSize: '14px' }} /> :
|
||||
<FaChevronRight style={{ color: '#aaa', fontSize: '14px' }} />
|
||||
}
|
||||
</div>
|
||||
|
||||
{/* Dropdown items container with animation */}
|
||||
<div className={`dropdown-container ${isDropdownOpen ? 'open' : 'closed'}`}>
|
||||
<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 dropdown-item ${activeTab === 'canvas' ? 'active' : ''}`}
|
||||
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 className={`dropdown-item-icon ${activeTab === 'canvas' ? 'active' : 'inactive'}`} />
|
||||
<span className={`dropdown-item-text ${activeTab === 'canvas' ? 'active' : 'inactive'}`}>
|
||||
Services
|
||||
<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>
|
||||
|
||||
{/* Data Entity Item */}
|
||||
<div
|
||||
className={`nav-button dropdown-item ${activeTab === 'er_diagram' ? 'active' : ''}`}
|
||||
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 className={`dropdown-item-icon ${activeTab === 'er_diagram' ? 'active' : 'inactive'}`} />
|
||||
<span className={`dropdown-item-text ${activeTab === 'er_diagram' ? 'active' : 'inactive'}`}>
|
||||
<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'
|
||||
}}>
|
||||
Data Entity
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Data Mapping Item */}
|
||||
<div
|
||||
className={`nav-button dropdown-item ${activeTab === 'dataflow' ? 'active' : ''}`}
|
||||
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 className={`dropdown-item-icon ${activeTab === 'dataflow' ? 'active' : 'inactive'}`} />
|
||||
<span className={`dropdown-item-text ${activeTab === 'dataflow' ? 'active' : 'inactive'}`}>
|
||||
Process
|
||||
<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>
|
||||
|
||||
|
||||
{/* Pipelines Item */}
|
||||
{/* Settings Item */}
|
||||
<div
|
||||
className={`nav-button dropdown-item ${activeTab === 'pipelines' ? 'active' : ''}`}
|
||||
className={`nav-button ${activeTab === 'settings' ? 'active' : ''}`}
|
||||
onClick={() => {
|
||||
setActiveTab('pipelines');
|
||||
window.history.pushState(null, '', '/pipelines');
|
||||
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 className={`dropdown-item-icon ${activeTab === 'pipelines' ? 'active' : 'inactive'}`} />
|
||||
<span className={`dropdown-item-text ${activeTab === 'pipelines' ? 'active' : 'inactive'}`}>
|
||||
Pipelines
|
||||
<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>
|
||||
|
||||
{/* Triggers Item */}
|
||||
<div
|
||||
className={`nav-button dropdown-item ${activeTab === 'triggers' ? 'active' : ''}`}
|
||||
{/* Advanced Charts Item */}
|
||||
{/* <div
|
||||
className={`nav-button ${activeTab === 'charts' ? 'active' : ''}`}
|
||||
onClick={() => {
|
||||
alert('Triggers functionality not implemented yet');
|
||||
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'
|
||||
}}
|
||||
>
|
||||
<FaTachometerAlt className={`dropdown-item-icon ${activeTab === 'triggers' ? 'active' : 'inactive'}`} />
|
||||
<span className={`dropdown-item-text ${activeTab === 'triggers' ? 'active' : 'inactive'}`}>
|
||||
Triggers
|
||||
<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>
|
||||
|
||||
{/* Schedulers Item */}
|
||||
<div
|
||||
className={`nav-button dropdown-item ${activeTab === 'schedulers' ? 'active' : ''}`}
|
||||
onClick={() => {
|
||||
alert('Schedulers functionality not implemented yet');
|
||||
}}
|
||||
>
|
||||
<FaTachometerAlt className={`dropdown-item-icon ${activeTab === 'schedulers' ? 'active' : 'inactive'}`} />
|
||||
<span className={`dropdown-item-text ${activeTab === 'schedulers' ? 'active' : 'inactive'}`}>
|
||||
Schedulers
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main className="main-content">
|
||||
<main style={{
|
||||
flex: 1,
|
||||
overflow: 'hidden',
|
||||
position: 'relative',
|
||||
padding: '20px'
|
||||
}}>
|
||||
{activeTab === 'canvas' && (
|
||||
<>
|
||||
<div className="page-header">
|
||||
<h1 className="page-title">
|
||||
<div style={{
|
||||
marginBottom: '20px'
|
||||
}}>
|
||||
<h1 style={{
|
||||
fontSize: '28px',
|
||||
fontWeight: '600',
|
||||
margin: '0 0 8px 0',
|
||||
color: '#ffffff'
|
||||
}}>
|
||||
Overview
|
||||
</h1>
|
||||
<Breadcrumbs
|
||||
|
|
@ -315,18 +455,56 @@ function App() {
|
|||
</Breadcrumbs>
|
||||
</div>
|
||||
<InfiniteCanvas />
|
||||
<div className="info-panel canvas">
|
||||
<p className="info-panel-main">
|
||||
<span className="info-panel-indicator canvas"></span>
|
||||
<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 className="info-panel-sub">
|
||||
<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' && (
|
||||
<>
|
||||
|
|
@ -335,12 +513,44 @@ function App() {
|
|||
dbSlug={currentDbSlug}
|
||||
hasSchemas={hasSchemas}
|
||||
/>
|
||||
<div className="info-panel dataflow">
|
||||
<p className="info-panel-main">
|
||||
<span className="info-panel-indicator dataflow"></span>
|
||||
<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 className="info-panel-sub">
|
||||
<p style={{
|
||||
margin: '5px 0 0 16px',
|
||||
fontSize: '11px',
|
||||
opacity: 0.7
|
||||
}}>
|
||||
Add tables • Create processes • Connect data flows
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -349,18 +559,21 @@ function App() {
|
|||
|
||||
{activeTab === 'er_diagram' && (
|
||||
<>
|
||||
<div className="page-header">
|
||||
<h1 className="page-title">
|
||||
<div style={{
|
||||
marginBottom: '20px'
|
||||
}}>
|
||||
<h1 style={{
|
||||
fontSize: '28px',
|
||||
fontWeight: '600',
|
||||
margin: '0 0 8px 0',
|
||||
color: '#ffffff'
|
||||
}}>
|
||||
Data Entity
|
||||
</h1>
|
||||
</div>
|
||||
<ERDiagramCanvas />
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeTab === 'pipelines' && (
|
||||
<PipelinesCanvas />
|
||||
)}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1150,7 +1150,20 @@ const AddTableModal = ({
|
|||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
|
||||
{/* <Grid item xs={12} md={2}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Relation Type</InputLabel>
|
||||
<Select
|
||||
value={relation.relationType}
|
||||
onChange={(e) => updateRelation(relation.id, 'relationType', e.target.value)}
|
||||
label="Relation Type"
|
||||
>
|
||||
<MenuItem value="1:1">One to One</MenuItem>
|
||||
<MenuItem value="1:N">One to Many</MenuItem>
|
||||
<MenuItem value="N:M">Many to Many</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid> */}
|
||||
<Grid item xs={12} md={1.5}>
|
||||
<IconButton
|
||||
onClick={() => removeRelation(relation.id)}
|
||||
|
|
|
|||
|
|
@ -34,13 +34,11 @@ import {
|
|||
FaChevronRight,
|
||||
FaServer,
|
||||
FaCloud,
|
||||
FaHdd,
|
||||
FaEdit
|
||||
FaHdd
|
||||
} from 'react-icons/fa';
|
||||
import { CustomDatabaseIcon, CustomDocumentIcon, CustomDimensionIcon } from './CustomIcons';
|
||||
import { Breadcrumbs, Link, Typography, Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material';
|
||||
import AddTableModal from './AddTableModal';
|
||||
// import UpdateTableModal from './UpdateTableModal';
|
||||
|
||||
|
||||
|
||||
|
|
@ -71,20 +69,8 @@ const ERTableNode = ({ data, id }) => {
|
|||
/>
|
||||
|
||||
<div className={`er-table-header ${data.table_type || 'default'}`}>
|
||||
<div className="er-table-header-content">
|
||||
{getTableIcon()}
|
||||
<span>{data.name}</span>
|
||||
</div>
|
||||
{/* <button
|
||||
className="er-table-update-btn"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
data.onUpdateTable && data.onUpdateTable(data);
|
||||
}}
|
||||
title="Update Table"
|
||||
>
|
||||
<FaEdit />
|
||||
</button> */}
|
||||
{getTableIcon()}
|
||||
<span>{data.name}</span>
|
||||
</div>
|
||||
|
||||
<ul className="er-column-list">
|
||||
|
|
@ -584,10 +570,6 @@ const ERDiagramCanvasContent = () => {
|
|||
const [availableSchemas, setAvailableSchemas] = useState([]);
|
||||
const [existingTables, setExistingTables] = useState([]);
|
||||
|
||||
// Update Table Modal state
|
||||
const [isUpdateTableModalOpen, setIsUpdateTableModalOpen] = useState(false);
|
||||
const [selectedTableForUpdate, setSelectedTableForUpdate] = useState(null);
|
||||
|
||||
// API Configuration
|
||||
const API_BASE_URL = 'https://sandbox.kezel.io/api';
|
||||
const token = "abdhsg";
|
||||
|
|
@ -1277,40 +1259,6 @@ const ERDiagramCanvasContent = () => {
|
|||
}
|
||||
};
|
||||
|
||||
// Handle update table action
|
||||
const handleUpdateTable = (tableData) => {
|
||||
console.log('Opening update modal for table:', tableData);
|
||||
setSelectedTableForUpdate(tableData);
|
||||
setIsUpdateTableModalOpen(true);
|
||||
};
|
||||
|
||||
// Handle table update submission
|
||||
const handleUpdateTableSubmit = async (updatedTableData) => {
|
||||
try {
|
||||
console.log('Updating table with data:', updatedTableData);
|
||||
|
||||
// The API call is handled in the UpdateTableModal component
|
||||
// After successful update, we need to refresh the diagram
|
||||
|
||||
// Close the modal
|
||||
setIsUpdateTableModalOpen(false);
|
||||
setSelectedTableForUpdate(null);
|
||||
|
||||
// Refresh the diagram by re-fetching data
|
||||
// You can implement a more efficient update by modifying the existing nodes
|
||||
// For now, let's trigger a refresh of the current database
|
||||
if (selectedDatabase) {
|
||||
generateERDiagram(selectedDatabase);
|
||||
}
|
||||
|
||||
console.log('Table updated successfully');
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error updating table:', error);
|
||||
// Error handling is done in the UpdateTableModal component
|
||||
}
|
||||
};
|
||||
|
||||
// Generate Data Entity with Database Wrapper structure
|
||||
const generateERDiagram = (database) => {
|
||||
console.log('🔄 Starting Data Entity generation...');
|
||||
|
|
@ -1577,7 +1525,6 @@ const ERDiagramCanvasContent = () => {
|
|||
columns: enhancedColumns,
|
||||
schema: schemaLayout.schema.sch,
|
||||
database: database.name
|
||||
// onUpdateTable: handleUpdateTable
|
||||
},
|
||||
draggable: true,
|
||||
parentNode: schemaGroupId,
|
||||
|
|
@ -1928,20 +1875,6 @@ const ERDiagramCanvasContent = () => {
|
|||
position="bottom-right"
|
||||
/>
|
||||
|
||||
{/* Update Table Modal */}
|
||||
{/* <UpdateTableModal
|
||||
open={isUpdateTableModalOpen}
|
||||
onClose={() => {
|
||||
setIsUpdateTableModalOpen(false);
|
||||
setSelectedTableForUpdate(null);
|
||||
}}
|
||||
onUpdateTable={handleUpdateTableSubmit}
|
||||
tableData={selectedTableForUpdate}
|
||||
schemas={availableSchemas}
|
||||
existingTables={existingTables}
|
||||
position="center"
|
||||
/> */}
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -200,7 +200,7 @@
|
|||
border-radius: 8px 8px 0 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
font-weight: bold;
|
||||
font-size: 15px;
|
||||
|
||||
|
|
@ -215,38 +215,6 @@
|
|||
&.dimension {
|
||||
background: linear-gradient(135deg, #52c41a, #73d13d);
|
||||
}
|
||||
|
||||
.er-table-header-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.er-table-update-btn {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
color: white;
|
||||
padding: 6px 8px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
|
||||
&:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
opacity: 1;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.er-column-list {
|
||||
|
|
|
|||
|
|
@ -1,163 +0,0 @@
|
|||
import React from 'react';
|
||||
import { FaTachometerAlt, FaPlus, FaPlay, FaPause, FaStop, FaCog } from 'react-icons/fa';
|
||||
import './PipelinesCanvas.scss';
|
||||
|
||||
const PipelinesCanvas = () => {
|
||||
// Mock pipeline data
|
||||
const mockPipelines = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Data Ingestion Pipeline',
|
||||
status: 'running',
|
||||
lastRun: '2024-01-15 10:30:00',
|
||||
nextRun: '2024-01-15 11:30:00',
|
||||
description: 'Ingests data from external sources into the data warehouse'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'ETL Processing Pipeline',
|
||||
status: 'stopped',
|
||||
lastRun: '2024-01-15 09:00:00',
|
||||
nextRun: '2024-01-15 12:00:00',
|
||||
description: 'Transforms and loads processed data into target tables'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Data Quality Pipeline',
|
||||
status: 'scheduled',
|
||||
lastRun: '2024-01-14 23:00:00',
|
||||
nextRun: '2024-01-15 23:00:00',
|
||||
description: 'Validates data quality and generates quality reports'
|
||||
}
|
||||
];
|
||||
|
||||
const getStatusColor = (status) => {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return '#52c41a';
|
||||
case 'stopped':
|
||||
return '#ff4d4f';
|
||||
case 'scheduled':
|
||||
return '#faad14';
|
||||
default:
|
||||
return '#aaa';
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusIcon = (status) => {
|
||||
switch (status) {
|
||||
case 'running':
|
||||
return <FaPlay />;
|
||||
case 'stopped':
|
||||
return <FaStop />;
|
||||
case 'scheduled':
|
||||
return <FaPause />;
|
||||
default:
|
||||
return <FaCog />;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="pipelines-container">
|
||||
{/* Header */}
|
||||
<div className="pipelines-header">
|
||||
<div className="header-content">
|
||||
<div className="header-title">
|
||||
<FaTachometerAlt className="header-icon" />
|
||||
<h1>Data Pipelines</h1>
|
||||
</div>
|
||||
<p className="header-description">
|
||||
Manage and monitor your data processing pipelines
|
||||
</p>
|
||||
</div>
|
||||
<button className="add-pipeline-btn">
|
||||
<FaPlus />
|
||||
Create Pipeline
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Pipeline Stats */}
|
||||
<div className="pipeline-stats">
|
||||
<div className="stat-card">
|
||||
<div className="stat-value">3</div>
|
||||
<div className="stat-label">Total Pipelines</div>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<div className="stat-value">1</div>
|
||||
<div className="stat-label">Running</div>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<div className="stat-value">1</div>
|
||||
<div className="stat-label">Scheduled</div>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<div className="stat-value">1</div>
|
||||
<div className="stat-label">Stopped</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Pipelines List */}
|
||||
<div className="pipelines-content">
|
||||
<div className="pipelines-grid">
|
||||
{mockPipelines.map((pipeline) => (
|
||||
<div key={pipeline.id} className="pipeline-card">
|
||||
<div className="pipeline-header">
|
||||
<div className="pipeline-title">
|
||||
<h3>{pipeline.name}</h3>
|
||||
<div
|
||||
className="pipeline-status"
|
||||
style={{ color: getStatusColor(pipeline.status) }}
|
||||
>
|
||||
{getStatusIcon(pipeline.status)}
|
||||
<span>{pipeline.status.charAt(0).toUpperCase() + pipeline.status.slice(1)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pipeline-description">
|
||||
<p>{pipeline.description}</p>
|
||||
</div>
|
||||
|
||||
<div className="pipeline-details">
|
||||
<div className="detail-item">
|
||||
<span className="detail-label">Last Run:</span>
|
||||
<span className="detail-value">{pipeline.lastRun}</span>
|
||||
</div>
|
||||
<div className="detail-item">
|
||||
<span className="detail-label">Next Run:</span>
|
||||
<span className="detail-value">{pipeline.nextRun}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="pipeline-actions">
|
||||
<button className="action-btn primary">
|
||||
<FaPlay />
|
||||
Run
|
||||
</button>
|
||||
<button className="action-btn secondary">
|
||||
<FaCog />
|
||||
Configure
|
||||
</button>
|
||||
<button className="action-btn danger">
|
||||
<FaStop />
|
||||
Stop
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Coming Soon Notice */}
|
||||
<div className="coming-soon-notice">
|
||||
<div className="notice-content">
|
||||
<FaTachometerAlt className="notice-icon" />
|
||||
<h3>Pipeline Management Coming Soon</h3>
|
||||
<p>Advanced pipeline creation, scheduling, and monitoring features are under development.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PipelinesCanvas;
|
||||
|
|
@ -1,317 +0,0 @@
|
|||
// PipelinesCanvas SCSS Styles
|
||||
.pipelines-container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
background: #121212;
|
||||
color: #fff;
|
||||
padding: 20px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
|
||||
// Header Section
|
||||
.pipelines-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid #333;
|
||||
|
||||
.header-content {
|
||||
.header-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.header-icon {
|
||||
font-size: 28px;
|
||||
color: #00a99d;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
font-size: 2rem;
|
||||
font-weight: 600;
|
||||
background: linear-gradient(90deg, #00a99d, #52c41a);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.header-description {
|
||||
margin: 0;
|
||||
color: #aaa;
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.add-pipeline-btn {
|
||||
background: linear-gradient(135deg, #00a99d, #52c41a);
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
transition: all 0.2s ease;
|
||||
box-shadow: 0 2px 8px rgba(0, 169, 157, 0.3);
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 169, 157, 0.4);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pipeline Stats
|
||||
.pipeline-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.stat-card {
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #333;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #00a99d;
|
||||
box-shadow: 0 4px 12px rgba(0, 169, 157, 0.1);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: bold;
|
||||
color: #00a99d;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #aaa;
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pipelines Content
|
||||
.pipelines-content {
|
||||
flex: 1;
|
||||
|
||||
.pipelines-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
||||
gap: 20px;
|
||||
|
||||
.pipeline-card {
|
||||
background: #1a1a1a;
|
||||
border: 1px solid #333;
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #00a99d;
|
||||
box-shadow: 0 4px 16px rgba(0, 169, 157, 0.1);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.pipeline-header {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.pipeline-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 8px;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pipeline-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pipeline-description {
|
||||
margin-bottom: 16px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: #aaa;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.pipeline-details {
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.detail-label {
|
||||
color: #aaa;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
color: #fff;
|
||||
font-size: 0.85rem;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pipeline-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
.action-btn {
|
||||
flex: 1;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 8px 12px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
&.primary {
|
||||
background: #52c41a;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: #73d13d;
|
||||
}
|
||||
}
|
||||
|
||||
&.secondary {
|
||||
background: #faad14;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: #ffc53d;
|
||||
}
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background: #ff4d4f;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: #ff7875;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Coming Soon Notice
|
||||
.coming-soon-notice {
|
||||
background: linear-gradient(135deg, rgba(0, 169, 157, 0.1), rgba(82, 196, 26, 0.1));
|
||||
border: 1px solid rgba(0, 169, 157, 0.3);
|
||||
border-radius: 12px;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
|
||||
.notice-content {
|
||||
.notice-icon {
|
||||
font-size: 3rem;
|
||||
color: #00a99d;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 1.5rem;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: #aaa;
|
||||
font-size: 1rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive Design
|
||||
@media (max-width: 768px) {
|
||||
.pipelines-container {
|
||||
padding: 15px;
|
||||
|
||||
.pipelines-header {
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
align-items: stretch;
|
||||
|
||||
.add-pipeline-btn {
|
||||
align-self: flex-start;
|
||||
}
|
||||
}
|
||||
|
||||
.pipeline-stats {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.pipelines-content .pipelines-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.pipelines-container {
|
||||
.pipeline-stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.pipeline-card .pipeline-actions {
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -73,10 +73,7 @@ const UpdateTableModal = ({
|
|||
name: '',
|
||||
description: '',
|
||||
tableType: '',
|
||||
schema: '',
|
||||
tbl: '',
|
||||
con: '',
|
||||
sch: ''
|
||||
schema: ''
|
||||
});
|
||||
|
||||
// Dynamic sections state
|
||||
|
|
@ -121,10 +118,7 @@ const UpdateTableModal = ({
|
|||
name: tableData.name || '',
|
||||
description: tableData.description || '',
|
||||
tableType: tableData.table_type || '',
|
||||
schema: tableData.schema || '',
|
||||
tbl: tableData.tbl || tableData.slug || '',
|
||||
con: tableData.con || 'my_dwh',
|
||||
sch: tableData.sch || tableData.schema || ''
|
||||
schema: tableData.schema || ''
|
||||
});
|
||||
|
||||
// Populate columns
|
||||
|
|
@ -289,10 +283,7 @@ const UpdateTableModal = ({
|
|||
name: '',
|
||||
description: '',
|
||||
tableType: '',
|
||||
schema: '',
|
||||
tbl: '',
|
||||
con: '',
|
||||
sch: ''
|
||||
schema: ''
|
||||
});
|
||||
setColumns([]);
|
||||
setKeys([]);
|
||||
|
|
@ -478,65 +469,42 @@ const UpdateTableModal = ({
|
|||
|
||||
setIsSubmitting(true);
|
||||
try {
|
||||
// Prepare the payload according to the API specification
|
||||
const payload = {
|
||||
token: "abdhsg",
|
||||
org: "sN05Pjv11qvH",
|
||||
con: formData.con || tableData?.con || "my_dwh",
|
||||
sch: formData.sch || tableData?.sch || formData.schema,
|
||||
const tableUpdateData = {
|
||||
id: tableData.id,
|
||||
name: formData.name.trim(),
|
||||
tbl: formData.tbl || tableData?.tbl || tableData?.slug,
|
||||
table_type: formData.tableType,
|
||||
description: formData.description.trim(),
|
||||
table_type: formData.tableType,
|
||||
schema: formData.schema,
|
||||
columns: columns.map(col => ({
|
||||
column_name: col.name.trim(),
|
||||
id: col.id,
|
||||
name: col.name.trim(),
|
||||
data_type: col.type,
|
||||
is_primary_key: col.isPrimaryKey,
|
||||
is_foreign_key: col.isForeignKey,
|
||||
is_nullable: col.isNullable
|
||||
})),
|
||||
keys: keys.map(key => ({
|
||||
key_name: key.name.trim(),
|
||||
key_type: key.keyType,
|
||||
key_columns: (key.keyColumns || []).map(kc => {
|
||||
const column = columns.find(col => col.id === kc.columnId);
|
||||
return {
|
||||
column_name: column ? column.name.trim() : '',
|
||||
sequence: kc.sequence
|
||||
};
|
||||
}).filter(kc => kc.column_name !== '')
|
||||
})),
|
||||
keys: keys.flatMap(key =>
|
||||
(key.keyColumns || []).map(kc => ({
|
||||
name: key.name.trim(),
|
||||
column_name: columns.find(col => col.id === kc.columnId)?.name || '',
|
||||
key_type: key.keyType,
|
||||
sequence: kc.sequence
|
||||
}))
|
||||
),
|
||||
relations: relations.map(rel => ({
|
||||
target_table_name: rel.targetTable,
|
||||
target_table: rel.targetTable,
|
||||
source_key: rel.sourceKey,
|
||||
reference_key: rel.targetKey,
|
||||
reference_name: rel.tableKey
|
||||
target_key: rel.targetKey,
|
||||
table_key: rel.tableKey,
|
||||
relation_type: rel.relationType
|
||||
}))
|
||||
};
|
||||
|
||||
console.log('Update table payload:', payload);
|
||||
|
||||
// Call the update API
|
||||
const response = await fetch('https://sandbox.kezel.io/api/qbt_table_update', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.status === 200) {
|
||||
console.log('Table updated successfully:', result);
|
||||
await onUpdateTable(payload);
|
||||
onClose();
|
||||
} else {
|
||||
console.error('Failed to update table:', result.message);
|
||||
setErrors({ submit: result.message || 'Failed to update table' });
|
||||
}
|
||||
await onUpdateTable(tableUpdateData);
|
||||
onClose();
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error updating table:', error);
|
||||
setErrors({ submit: 'Network error occurred while updating table' });
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
|
|
@ -580,11 +548,6 @@ const UpdateTableModal = ({
|
|||
|
||||
<DialogContent sx={{ p: 0, overflow: 'hidden' }}>
|
||||
<Box sx={{ p: 3, height: '100%', overflow: 'auto' }}>
|
||||
{errors.submit && (
|
||||
<Alert severity="error" sx={{ mb: 2 }}>
|
||||
{errors.submit}
|
||||
</Alert>
|
||||
)}
|
||||
{/* Basic Information */}
|
||||
<Paper variant="outlined" sx={{ p: 2, mb: 2 }}>
|
||||
<Typography variant="h6" gutterBottom sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
|
|
|
|||
Loading…
Reference in New Issue