From 8fc253a87f089bcb0324d787b4f6754f0dd393dd Mon Sep 17 00:00:00 2001 From: Devika Date: Tue, 1 Jul 2025 12:17:41 +0530 Subject: [PATCH] Added piplines Page and changes in the App.jsx --- src/App.css | 235 ++++++++++++++++++ src/App.jsx | 367 ++++++---------------------- src/components/AddTableModal.jsx | 15 +- src/components/ERDiagramCanvas.jsx | 73 +++++- src/components/ERDiagramCanvas.scss | 34 ++- src/components/PipelinesCanvas.jsx | 163 ++++++++++++ src/components/PipelinesCanvas.scss | 317 ++++++++++++++++++++++++ src/components/UpdateTableModal.jsx | 87 +++++-- 8 files changed, 958 insertions(+), 333 deletions(-) create mode 100644 src/components/PipelinesCanvas.jsx create mode 100644 src/components/PipelinesCanvas.scss diff --git a/src/App.css b/src/App.css index 9c220ca..cf1809c 100644 --- a/src/App.css +++ b/src/App.css @@ -493,3 +493,238 @@ 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; +} diff --git a/src/App.jsx b/src/App.jsx index a4329de..414f522 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,6 +4,7 @@ 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' @@ -36,6 +37,7 @@ 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'); @@ -58,6 +60,7 @@ 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 }; @@ -98,6 +101,7 @@ 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); @@ -117,48 +121,20 @@ function App() { return ( -
+
{/* Logo and Title */} -
-
- +
+
+
-

- {/* The Metricverse - */} - Qubit Service +

+ Qubit Service

{/* User profile and controls */} -
+
@@ -197,235 +173,119 @@ function App() {
-
+
{/* Sidebar navigation with dropdown */} -
-
+
+
{/* Main Qbit Item with Dropdown */}
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' - }} > - - + + Qbit {isDropdownOpen ? - : - + : + }
{/* Dropdown items container with animation */} -
+
{/* Overview Item */}
{ 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' - }} > - - - Overview + + + Services
{/* Data Entity Item */}
{ 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' - }} > - - + + Data Entity
{/* Data Mapping Item */}
{ 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' - }} > - - - Data Mapping + + + Process
- {/* Settings Item */} + {/* Pipelines Item */}
{ - 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' + setActiveTab('pipelines'); + window.history.pushState(null, '', '/pipelines'); }} > - - - Dashboard + + + Pipelines
- {/* Advanced Charts Item */} - {/*
{ - 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' + alert('Triggers functionality not implemented yet'); }} > - - - Advanced Charts + + + Triggers -
*/} +
+ + {/* Schedulers Item */} +
{ + alert('Schedulers functionality not implemented yet'); + }} + > + + + Schedulers + +
+
-
+
{activeTab === 'canvas' && ( <> -
-

+
+

Overview

-
-

- - {/* Infinite Canvas - Zoom and pan without limits */} +

+

+ The Only Limit is Your Imagination.

-

+

Scroll to zoom • Drag to pan • Connect nodes

)} - {activeTab === 'charts' && ( -
- {/* */} -
- )} {activeTab === 'dataflow' && ( <> @@ -513,44 +335,12 @@ function App() { dbSlug={currentDbSlug} hasSchemas={hasSchemas} /> -
-

- +

+

+ Visualize data flows between tables

-

+

Add tables • Create processes • Connect data flows

@@ -559,21 +349,18 @@ function App() { {activeTab === 'er_diagram' && ( <> -
-

+
+

Data Entity

)} + + {activeTab === 'pipelines' && ( + + )}

diff --git a/src/components/AddTableModal.jsx b/src/components/AddTableModal.jsx index d29d083..179908f 100644 --- a/src/components/AddTableModal.jsx +++ b/src/components/AddTableModal.jsx @@ -1150,20 +1150,7 @@ const AddTableModal = ({ - {/* - - Relation Type - - - */} + removeRelation(relation.id)} diff --git a/src/components/ERDiagramCanvas.jsx b/src/components/ERDiagramCanvas.jsx index 3c0918a..15a0a20 100644 --- a/src/components/ERDiagramCanvas.jsx +++ b/src/components/ERDiagramCanvas.jsx @@ -34,11 +34,13 @@ import { FaChevronRight, FaServer, FaCloud, - FaHdd + FaHdd, + FaEdit } 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'; @@ -69,8 +71,20 @@ const ERTableNode = ({ data, id }) => { />
- {getTableIcon()} - {data.name} +
+ {getTableIcon()} + {data.name} +
+ {/* */}
    @@ -570,6 +584,10 @@ 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"; @@ -1259,6 +1277,40 @@ 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...'); @@ -1525,6 +1577,7 @@ const ERDiagramCanvasContent = () => { columns: enhancedColumns, schema: schemaLayout.schema.sch, database: database.name + // onUpdateTable: handleUpdateTable }, draggable: true, parentNode: schemaGroupId, @@ -1874,6 +1927,20 @@ const ERDiagramCanvasContent = () => { existingTables={existingTables} position="bottom-right" /> + + {/* Update Table Modal */} + {/* { + setIsUpdateTableModalOpen(false); + setSelectedTableForUpdate(null); + }} + onUpdateTable={handleUpdateTableSubmit} + tableData={selectedTableForUpdate} + schemas={availableSchemas} + existingTables={existingTables} + position="center" + /> */}
); diff --git a/src/components/ERDiagramCanvas.scss b/src/components/ERDiagramCanvas.scss index d4279b1..a2bd06a 100644 --- a/src/components/ERDiagramCanvas.scss +++ b/src/components/ERDiagramCanvas.scss @@ -200,7 +200,7 @@ border-radius: 8px 8px 0 0; display: flex; align-items: center; - gap: 10px; + justify-content: space-between; font-weight: bold; font-size: 15px; @@ -215,6 +215,38 @@ &.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 { diff --git a/src/components/PipelinesCanvas.jsx b/src/components/PipelinesCanvas.jsx new file mode 100644 index 0000000..550c121 --- /dev/null +++ b/src/components/PipelinesCanvas.jsx @@ -0,0 +1,163 @@ +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 ; + case 'stopped': + return ; + case 'scheduled': + return ; + default: + return ; + } + }; + + return ( +
+ {/* Header */} +
+
+
+ +

Data Pipelines

+
+

+ Manage and monitor your data processing pipelines +

+
+ +
+ + {/* Pipeline Stats */} +
+
+
3
+
Total Pipelines
+
+
+
1
+
Running
+
+
+
1
+
Scheduled
+
+
+
1
+
Stopped
+
+
+ + {/* Pipelines List */} +
+
+ {mockPipelines.map((pipeline) => ( +
+
+
+

{pipeline.name}

+
+ {getStatusIcon(pipeline.status)} + {pipeline.status.charAt(0).toUpperCase() + pipeline.status.slice(1)} +
+
+
+ +
+

{pipeline.description}

+
+ +
+
+ Last Run: + {pipeline.lastRun} +
+
+ Next Run: + {pipeline.nextRun} +
+
+ +
+ + + +
+
+ ))} +
+
+ + {/* Coming Soon Notice */} +
+
+ +

Pipeline Management Coming Soon

+

Advanced pipeline creation, scheduling, and monitoring features are under development.

+
+
+
+ ); +}; + +export default PipelinesCanvas; \ No newline at end of file diff --git a/src/components/PipelinesCanvas.scss b/src/components/PipelinesCanvas.scss new file mode 100644 index 0000000..7c6d6e7 --- /dev/null +++ b/src/components/PipelinesCanvas.scss @@ -0,0 +1,317 @@ +// 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; + } + } +} \ No newline at end of file diff --git a/src/components/UpdateTableModal.jsx b/src/components/UpdateTableModal.jsx index 64e90dd..8f91c8d 100644 --- a/src/components/UpdateTableModal.jsx +++ b/src/components/UpdateTableModal.jsx @@ -73,7 +73,10 @@ const UpdateTableModal = ({ name: '', description: '', tableType: '', - schema: '' + schema: '', + tbl: '', + con: '', + sch: '' }); // Dynamic sections state @@ -118,7 +121,10 @@ const UpdateTableModal = ({ name: tableData.name || '', description: tableData.description || '', tableType: tableData.table_type || '', - schema: tableData.schema || '' + schema: tableData.schema || '', + tbl: tableData.tbl || tableData.slug || '', + con: tableData.con || 'my_dwh', + sch: tableData.sch || tableData.schema || '' }); // Populate columns @@ -283,7 +289,10 @@ const UpdateTableModal = ({ name: '', description: '', tableType: '', - schema: '' + schema: '', + tbl: '', + con: '', + sch: '' }); setColumns([]); setKeys([]); @@ -469,42 +478,65 @@ const UpdateTableModal = ({ setIsSubmitting(true); try { - const tableUpdateData = { - id: tableData.id, + // 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, name: formData.name.trim(), - description: formData.description.trim(), + tbl: formData.tbl || tableData?.tbl || tableData?.slug, table_type: formData.tableType, - schema: formData.schema, + description: formData.description.trim(), columns: columns.map(col => ({ - id: col.id, - name: col.name.trim(), + column_name: col.name.trim(), data_type: col.type, - is_primary_key: col.isPrimaryKey, - is_foreign_key: col.isForeignKey, is_nullable: col.isNullable })), - 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 - })) - ), + 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 !== '') + })), relations: relations.map(rel => ({ - target_table: rel.targetTable, + target_table_name: rel.targetTable, source_key: rel.sourceKey, - target_key: rel.targetKey, - table_key: rel.tableKey, - relation_type: rel.relationType + reference_key: rel.targetKey, + reference_name: rel.tableKey })) }; - await onUpdateTable(tableUpdateData); - onClose(); + 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' }); + } } catch (error) { console.error('Error updating table:', error); + setErrors({ submit: 'Network error occurred while updating table' }); } finally { setIsSubmitting(false); } @@ -548,6 +580,11 @@ const UpdateTableModal = ({ + {errors.submit && ( + + {errors.submit} + + )} {/* Basic Information */} -- 2.40.1