Compare commits

...

3 Commits

8 changed files with 4213 additions and 191 deletions

View File

@ -10,22 +10,26 @@ function App() {
const getInitialTab = () => { const getInitialTab = () => {
const pathname = window.location.pathname; const pathname = window.location.pathname;
if (pathname === '/charts') return 'charts'; if (pathname === '/charts') return 'charts';
if (pathname === '/dataflow') return 'dataflow'; if (pathname === '/data_mappings') return 'data_mappigs';
if (pathname === '/settings') return 'settings'; if (pathname === '/settings') return 'settings';
// Default to canvas/explorer // Default to canvas/qubit_service
window.history.pushState(null, '', '/explorer'); window.history.pushState(null, '', '/qubit_service');
return 'canvas'; return 'canvas';
}; };
const [activeTab, setActiveTab] = useState(getInitialTab()); 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);
// Handle browser back/forward navigation // Handle browser back/forward navigation
useEffect(() => { useEffect(() => {
const handleLocationChange = () => { const handleLocationChange = () => {
const pathname = window.location.pathname; const pathname = window.location.pathname;
if (pathname === '/explorer') setActiveTab('canvas'); if (pathname === '/qubit_service') setActiveTab('canvas');
else if (pathname === '/charts') setActiveTab('charts'); else if (pathname === '/charts') setActiveTab('charts');
else if (pathname === '/dataflow') setActiveTab('dataflow'); else if (pathname === '/data_mappings') setActiveTab('data_mappigs');
// Settings tab would be handled here too // Settings tab would be handled here too
}; };
@ -43,10 +47,17 @@ function App() {
setActiveTab('dataflow'); setActiveTab('dataflow');
// Update URL path // Update URL path
window.history.pushState(null, '', '/dataflow'); window.history.pushState(null, '', '/data_mappings');
// Log the database info (in a real app, you would use this to initialize the DataFlow view) // 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('Viewing DataFlow for database:', event.detail);
console.log('Database has schemas:', !event.detail.isEmpty);
}; };
// Handler for switching to any tab // Handler for switching to any tab
@ -55,9 +66,9 @@ function App() {
setActiveTab(event.detail); setActiveTab(event.detail);
// Update URL path based on the tab // Update URL path based on the tab
let path = '/explorer'; let path = '/qubit_service';
if (event.detail === 'charts') path = '/charts'; if (event.detail === 'charts') path = '/charts';
if (event.detail === 'dataflow') path = '/dataflow'; if (event.detail === 'data_mappigs') path = '/data_mappings';
if (event.detail === 'settings') path = '/settings'; if (event.detail === 'settings') path = '/settings';
window.history.pushState(null, '', path); window.history.pushState(null, '', path);
@ -117,7 +128,7 @@ function App() {
{/* Navigation */} {/* Navigation */}
<div> <div>
<a <a
href="/explorer" href="/qubit_service"
style={{ style={{
marginRight: '25px', marginRight: '25px',
cursor: 'pointer', cursor: 'pointer',
@ -133,10 +144,10 @@ function App() {
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setActiveTab('canvas'); setActiveTab('canvas');
window.history.pushState(null, '', '/explorer'); window.history.pushState(null, '', '/qubit_service');
}} }}
> >
Data Explorer Overview
</a> </a>
<a <a
href="/charts" href="/charts"
@ -161,7 +172,7 @@ function App() {
Advanced Charts Advanced Charts
</a> </a>
<a <a
href="/dataflow" href="/data_mappings"
style={{ style={{
marginRight: '25px', marginRight: '25px',
cursor: 'pointer', cursor: 'pointer',
@ -177,10 +188,10 @@ function App() {
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setActiveTab('dataflow'); setActiveTab('dataflow');
window.history.pushState(null, '', '/dataflow'); window.history.pushState(null, '', '/data_mappings');
}} }}
> >
Data Flow Data Mapping
</a> </a>
<a <a
href="/settings" href="/settings"
@ -362,7 +373,11 @@ function App() {
{activeTab === 'dataflow' && ( {activeTab === 'dataflow' && (
<> <>
<DataflowCanvas /> <DataflowCanvas
key={`dataflow-${currentDbSlug || 'default'}-${hasSchemas ? 'has-schemas' : 'no-schemas'}-${Date.now()}`}
dbSlug={currentDbSlug}
hasSchemas={hasSchemas}
/>
<div style={{ <div style={{
position: 'absolute', position: 'absolute',
bottom: '20px', bottom: '20px',

View File

@ -0,0 +1,220 @@
import React, { useState } from 'react';
import { FaTimes, FaColumns, FaKey } from 'react-icons/fa';
const ColumnCreationPopup = ({ onClose, onCreateColumn, tableInfo }) => {
const [columnName, setColumnName] = useState('');
const [dataType, setDataType] = useState('TEXT');
const [description, setDescription] = useState('');
const [alias, setAlias] = useState('');
const [isKey, setIsKey] = useState(false);
const handleSubmit = (e) => {
e.preventDefault();
if (columnName.trim() === '') return;
onCreateColumn({
name: columnName,
data_type: dataType,
description: description,
alias: alias,
is_key: isKey ? 1 : 0,
tbl: tableInfo.tbl,
sch: tableInfo.sch,
con: tableInfo.con
});
onClose();
};
return (
<div className="popup-overlay" style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1000
}}>
<div className="popup-content" style={{
backgroundColor: 'white',
padding: '20px',
borderRadius: '8px',
width: '500px',
maxWidth: '90%',
maxHeight: '90vh',
overflowY: 'auto',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
<h2 style={{ margin: 0, display: 'flex', alignItems: 'center', gap: '10px', color: "black" }}>
<FaColumns />
Add New Column
</h2>
<button
onClick={onClose}
style={{
background: 'none',
border: 'none',
fontSize: '20px',
cursor: 'pointer',
color: '#999'
}}
>
<FaTimes />
</button>
</div>
<div style={{ marginBottom: '15px', padding: '10px', backgroundColor: '#f5f5f5', borderRadius: '4px' }}>
<p style={{ margin: 0, color: '#333' }}>
<strong>Table:</strong> {tableInfo.tbl}
<br />
<strong>Schema:</strong> {tableInfo.sch}
<br />
<strong>Database:</strong> {tableInfo.con}
</p>
</div>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}>
Column Name:
</label>
<input
type="text"
value={columnName}
onChange={(e) => setColumnName(e.target.value)}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9'
}}
placeholder="Enter column name"
required
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}>
Data Type:
</label>
<select
value={dataType}
onChange={(e) => setDataType(e.target.value)}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9'
}}
>
<option value="TEXT">TEXT</option>
<option value="INTEGER">INTEGER</option>
<option value="FLOAT">FLOAT</option>
<option value="BOOLEAN">BOOLEAN</option>
<option value="DATE">DATE</option>
<option value="TIMESTAMP">TIMESTAMP</option>
<option value="JSON">JSON</option>
</select>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}>
Description:
</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
minHeight: '80px'
}}
placeholder="Enter column description"
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}>
Display Alias:
</label>
<input
type="text"
value={alias}
onChange={(e) => setAlias(e.target.value)}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9'
}}
placeholder="Enter display alias (optional)"
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
cursor: 'pointer',
color: "black"
}}>
<input
type="checkbox"
checked={isKey}
onChange={(e) => setIsKey(e.target.checked)}
style={{ margin: 0 }}
/>
<FaKey style={{ color: isKey ? '#faad14' : '#d9d9d9' }} />
Is Key Column
</label>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '10px', marginTop: '20px' }}>
<button
type="button"
onClick={onClose}
style={{
padding: '8px 16px',
background: 'white',
color: '#333',
border: '1px solid #d9d9d9',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Cancel
</button>
<button
type="submit"
style={{
padding: '8px 16px',
background: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '5px'
}}
>
<FaColumns size={14} />
Add Column
</button>
</div>
</form>
</div>
</div>
);
};
export default ColumnCreationPopup;

View File

@ -0,0 +1,551 @@
import React, { useState, useEffect } from 'react';
import { FaTimes, FaColumns, FaKey, FaPlus, FaEdit, FaTrash, FaSave, FaUndo } from 'react-icons/fa';
const ColumnManagementPopup = ({ onClose, onCreateColumn, onUpdateColumn, onDeleteColumn, tableInfo, existingColumns = [] }) => {
// State for the list of columns
const [columns, setColumns] = useState([]);
// State for the new column form
const [newColumn, setNewColumn] = useState({
name: '',
data_type: 'TEXT',
description: '',
alias: '',
is_key: false
});
// State for tracking which column is being edited
const [editingColumnIndex, setEditingColumnIndex] = useState(null);
// State for the column being edited
const [editingColumn, setEditingColumn] = useState(null);
// Load existing columns when the component mounts
useEffect(() => {
console.log('Existing columns received:', existingColumns);
if (existingColumns && existingColumns.length > 0) {
// Make sure each column has both slug and col properties
const processedColumns = existingColumns.map(column => {
// Ensure we have a valid column identifier
const colId = column.col || column.slug || column.id || column.column_id;
if (!colId) {
console.warn('Column without identifier:', column);
}
return {
...column,
col: colId, // Ensure col exists
slug: colId // Ensure slug exists
};
});
console.log('Processed columns:', processedColumns);
setColumns(processedColumns);
} else {
console.log('No existing columns or empty array received');
setColumns([]);
}
}, [existingColumns]);
// Handle input change for the new column form
const handleNewColumnChange = (e) => {
const { name, value, type, checked } = e.target;
setNewColumn({
...newColumn,
[name]: type === 'checkbox' ? checked : value
});
};
// Handle input change for the editing column form
const handleEditColumnChange = (e) => {
const { name, value, type, checked } = e.target;
setEditingColumn({
...editingColumn,
[name]: type === 'checkbox' ? checked : value
});
};
// Handle adding a new column
const handleAddColumn = () => {
if (newColumn.name.trim() === '') return;
// Call the create column function
onCreateColumn({
name: newColumn.name,
data_type: newColumn.data_type,
description: newColumn.description,
alias: newColumn.alias,
is_key: newColumn.is_key ? 1 : 0,
tbl: tableInfo.tbl,
sch: tableInfo.sch,
con: tableInfo.con
});
// Reset the form
setNewColumn({
name: '',
data_type: 'TEXT',
description: '',
alias: '',
is_key: false
});
};
// Handle editing a column
const handleEditColumn = (index) => {
setEditingColumnIndex(index);
setEditingColumn({ ...columns[index] });
};
// Handle saving an edited column
const handleSaveEdit = () => {
if (!editingColumn || editingColumn.name.trim() === '') return;
// Call the update column function
onUpdateColumn({
col: editingColumn.col || editingColumn.slug, // Use col if available, otherwise use slug
name: editingColumn.name,
data_type: editingColumn.data_type,
description: editingColumn.description,
alias: editingColumn.alias,
is_key: editingColumn.is_key ? 1 : 0,
tbl: tableInfo.tbl,
sch: tableInfo.sch,
con: tableInfo.con
});
// Update the columns array
const updatedColumns = [...columns];
updatedColumns[editingColumnIndex] = editingColumn;
setColumns(updatedColumns);
// Reset the editing state
setEditingColumnIndex(null);
setEditingColumn(null);
};
// Handle canceling an edit
const handleCancelEdit = () => {
setEditingColumnIndex(null);
setEditingColumn(null);
};
// Handle deleting a column
const handleDeleteColumn = (index) => {
const column = columns[index];
console.log('Attempting to delete column:', column);
console.log('Column properties:', {
name: column.name,
slug: column.slug,
col: column.col,
data_type: column.data_type
});
// Confirm deletion
if (window.confirm(`Are you sure you want to delete the column "${column.name}"?`)) {
// Create the payload with both col and slug for maximum compatibility
const payload = {
col: column.col || column.slug, // Use col if available, otherwise use slug
slug: column.slug || column.col, // Include slug as well
tbl: tableInfo.tbl,
sch: tableInfo.sch,
con: tableInfo.con,
name: column.name // Include name for logging
};
console.log('Sending delete payload:', payload);
// Call the delete column function
onDeleteColumn(payload);
// Remove the column from the array
const updatedColumns = [...columns];
updatedColumns.splice(index, 1);
setColumns(updatedColumns);
}
};
return (
<div className="popup-overlay" style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1000
}}>
<div className="popup-content" style={{
backgroundColor: 'white',
padding: '20px',
borderRadius: '8px',
width: '700px',
maxWidth: '90%',
maxHeight: '90vh',
overflowY: 'auto',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
<h2 style={{ margin: 0, display: 'flex', alignItems: 'center', gap: '10px', color: "black" }}>
<FaColumns />
Manage Columns
</h2>
<button
onClick={onClose}
style={{
background: 'none',
border: 'none',
fontSize: '20px',
cursor: 'pointer',
color: '#999'
}}
>
<FaTimes />
</button>
</div>
<div style={{ marginBottom: '15px', padding: '10px', backgroundColor: '#f5f5f5', borderRadius: '4px' }}>
<p style={{ margin: 0, color: '#333' }}>
<strong>Table:</strong> {tableInfo.tbl}
<br />
<strong>Schema:</strong> {tableInfo.sch}
<br />
<strong>Database:</strong> {tableInfo.con}
</p>
</div>
{/* Existing Columns Section */}
<div style={{ marginBottom: '20px' }}>
<h3 style={{ color: 'black', borderBottom: '1px solid #eee', paddingBottom: '8px' }}>
Existing Columns
</h3>
{columns.length === 0 ? (
<div style={{ padding: '15px', textAlign: 'center', color: 'black', backgroundColor: '#f9f9f9', borderRadius: '4px' }}>
No columns found for this table.
</div>
) : (
<div style={{
border: '1px solid #eee',
borderRadius: '4px',
overflow: 'hidden'
}}>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr style={{ backgroundColor: '#f5f5f5' }}>
<th style={{ padding: '10px', textAlign: 'left', borderBottom: '1px solid #eee', color: 'black', fontWeight: 'bold' }}>Name</th>
<th style={{ padding: '10px', textAlign: 'left', borderBottom: '1px solid #eee', color: 'black', fontWeight: 'bold' }}>Type</th>
<th style={{ padding: '10px', textAlign: 'left', borderBottom: '1px solid #eee', color: 'black', fontWeight: 'bold' }}>Key</th>
<th style={{ padding: '10px', textAlign: 'right', borderBottom: '1px solid #eee', color: 'black', fontWeight: 'bold' }}>Actions</th>
</tr>
</thead>
<tbody>
{columns.map((column, index) => (
<tr key={`column-${index}-${column.name}`} style={{ borderBottom: '1px solid #eee' }}>
{editingColumnIndex === index ? (
// Editing mode
<>
<td style={{ padding: '10px' }}>
<input
type="text"
name="name"
value={editingColumn.name}
onChange={handleEditColumnChange}
style={{
width: '100%',
padding: '6px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
color: 'black'
}}
/>
</td>
<td style={{ padding: '10px' }}>
<select
name="data_type"
value={editingColumn.data_type}
onChange={handleEditColumnChange}
style={{
width: '100%',
padding: '6px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
color: 'black'
}}
>
<option value="TEXT" style={{ color: 'black' }}>TEXT</option>
<option value="INTEGER" style={{ color: 'black' }}>INTEGER</option>
<option value="FLOAT" style={{ color: 'black' }}>FLOAT</option>
<option value="BOOLEAN" style={{ color: 'black' }}>BOOLEAN</option>
<option value="DATE" style={{ color: 'black' }}>DATE</option>
<option value="TIMESTAMP" style={{ color: 'black' }}>TIMESTAMP</option>
<option value="JSON" style={{ color: 'black' }}>JSON</option>
</select>
</td>
<td style={{ padding: '10px' }}>
<label style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
<input
type="checkbox"
name="is_key"
checked={editingColumn.is_key}
onChange={handleEditColumnChange}
/>
<FaKey style={{ color: editingColumn.is_key ? '#faad14' : '#d9d9d9' }} />
</label>
</td>
<td style={{ padding: '10px', textAlign: 'right' }}>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '5px' }}>
<button
onClick={handleSaveEdit}
style={{
background: '#52c41a',
color: 'white',
border: 'none',
borderRadius: '4px',
padding: '5px 10px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '5px'
}}
>
<FaSave size={12} />
Save
</button>
<button
onClick={handleCancelEdit}
style={{
background: '#f5f5f5',
color: '#333',
border: '1px solid #d9d9d9',
borderRadius: '4px',
padding: '5px 10px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '5px'
}}
>
<FaUndo size={12} />
Cancel
</button>
</div>
</td>
</>
) : (
// View mode
<>
<td style={{ padding: '10px', color: 'black' }}>{column.name}</td>
<td style={{ padding: '10px', color: 'black' }}>{column.data_type}</td>
<td style={{ padding: '10px', color: 'black' }}>
{column.is_key && <FaKey style={{ color: '#faad14' }} />}
</td>
<td style={{ padding: '10px', textAlign: 'right' }}>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '5px' }}>
<button
onClick={() => handleEditColumn(index)}
style={{
background: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
padding: '5px 10px',
cursor: 'pointer'
}}
>
<FaEdit size={12} />
</button>
<button
onClick={() => handleDeleteColumn(index)}
style={{
background: '#ff4d4f',
color: 'white',
border: 'none',
borderRadius: '4px',
padding: '5px 10px',
cursor: 'pointer'
}}
>
<FaTrash size={12} />
</button>
</div>
</td>
</>
)}
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
{/* Add New Column Section */}
<div style={{ marginTop: '20px' }}>
<h3 style={{ color: 'black', borderBottom: '1px solid #eee', paddingBottom: '8px' }}>
Add New Column
</h3>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '15px', marginBottom: '15px' }}>
<div>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}>
Column Name:
</label>
<input
type="text"
name="name"
value={newColumn.name}
onChange={handleNewColumnChange}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
color: 'black'
}}
placeholder="Enter column name"
required
/>
</div>
<div>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}>
Data Type:
</label>
<select
name="data_type"
value={newColumn.data_type}
onChange={handleNewColumnChange}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
color: 'black'
}}
>
<option value="TEXT" style={{ color: 'black' }}>TEXT</option>
<option value="INTEGER" style={{ color: 'black' }}>INTEGER</option>
<option value="FLOAT" style={{ color: 'black' }}>FLOAT</option>
<option value="BOOLEAN" style={{ color: 'black' }}>BOOLEAN</option>
<option value="DATE" style={{ color: 'black' }}>DATE</option>
<option value="TIMESTAMP" style={{ color: 'black' }}>TIMESTAMP</option>
<option value="JSON" style={{ color: 'black' }}>JSON</option>
</select>
</div>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}>
Description:
</label>
<textarea
name="description"
value={newColumn.description}
onChange={handleNewColumnChange}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
minHeight: '60px',
color: 'black'
}}
placeholder="Enter column description"
/>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: '15px', marginBottom: '15px', alignItems: 'end' }}>
<div>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}>
Display Alias:
</label>
<input
type="text"
name="alias"
value={newColumn.alias}
onChange={handleNewColumnChange}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
color: 'black'
}}
placeholder="Enter display alias (optional)"
/>
</div>
<div>
<label style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
cursor: 'pointer',
color: "black",
padding: '8px',
border: '1px solid #d9d9d9',
borderRadius: '4px',
backgroundColor: newColumn.is_key ? '#fffbe6' : 'transparent'
}}>
<input
type="checkbox"
name="is_key"
checked={newColumn.is_key}
onChange={handleNewColumnChange}
style={{ margin: 0 }}
/>
<FaKey style={{ color: newColumn.is_key ? '#faad14' : '#d9d9d9' }} />
Is Key Column
</label>
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', marginTop: '20px' }}>
<button
onClick={handleAddColumn}
style={{
padding: '8px 16px',
background: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '5px'
}}
>
<FaPlus size={14} />
Add Column
</button>
</div>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '10px', marginTop: '20px', borderTop: '1px solid #eee', paddingTop: '20px' }}>
<button
onClick={onClose}
style={{
padding: '8px 16px',
background: 'white',
color: '#333',
border: '1px solid #d9d9d9',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Close
</button>
</div>
</div>
</div>
);
};
export default ColumnManagementPopup;

File diff suppressed because it is too large Load Diff

View File

@ -1051,13 +1051,13 @@ const DatabaseNode = ({ data = {} }) => {
</span> </span>
</div> </div>
<div style={{ fontSize: '0.8em', color: '#aaa', marginBottom: '5px' }}> {/* <div style={{ fontSize: '0.8em', color: '#aaa', marginBottom: '5px' }}>
{`${data.schemas} schemas • ${data.tables} tables`} {`${data.schemas} schemas • ${data.tables} tables`}
</div> </div>
<div style={{ fontSize: '0.7em', color: '#888', marginBottom: '10px' }}> <div style={{ fontSize: '0.7em', color: '#888', marginBottom: '10px' }}>
Connection: {dbSlug} Connection: {dbSlug}
</div> </div> */}
{/* View Details Button */} {/* View Details Button */}
<div style={{ display: 'flex', justifyContent: 'center' }}> <div style={{ display: 'flex', justifyContent: 'center' }}>
@ -1091,7 +1091,7 @@ const DatabaseNode = ({ data = {} }) => {
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="12" height="12" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 4.5C7 4.5 2.73 7.61 1 12C2.73 16.39 7 19.5 12 19.5C17 19.5 21.27 16.39 23 12C21.27 7.61 17 4.5 12 4.5ZM12 17C9.24 17 7 14.76 7 12C7 9.24 9.24 7 12 7C14.76 7 17 9.24 17 12C17 14.76 14.76 17 12 17ZM12 9C10.34 9 9 10.34 9 12C9 13.66 10.34 15 12 15C13.66 15 15 13.66 15 12C15 10.34 13.66 9 12 9Z" fill="white"/> <path d="M12 4.5C7 4.5 2.73 7.61 1 12C2.73 16.39 7 19.5 12 19.5C17 19.5 21.27 16.39 23 12C21.27 7.61 17 4.5 12 4.5ZM12 17C9.24 17 7 14.76 7 12C7 9.24 9.24 7 12 7C14.76 7 17 9.24 17 12C17 14.76 14.76 17 12 17ZM12 9C10.34 9 9 10.34 9 12C9 13.66 10.34 15 12 15C13.66 15 15 13.66 15 12C15 10.34 13.66 9 12 9Z" fill="white"/>
</svg> </svg>
View Data Flow View Data Mapping
</button> </button>
</div> </div>
</div> </div>
@ -1908,20 +1908,24 @@ const InfiniteCanvas = () => {
console.log(`Viewing data flow for database: ${dbName} (${dbSlug})`); console.log(`Viewing data flow for database: ${dbName} (${dbSlug})`);
// Clear any existing schemas in localStorage for this database // Selectively clear cached data for the current database only
try { try {
// Clear any existing schemas for this database // Only clear the cache for the current database
localStorage.removeItem(`schemas_${dbSlug}`);
localStorage.removeItem(`tables_${dbSlug}`);
// If this is "service 2" (my_dwh2), set empty schemas
if (dbSlug === 'my_dwh2') { if (dbSlug === 'my_dwh2') {
console.log('Setting empty schemas for service 2 database'); console.log('Setting empty schemas for service 2 database');
// For service 2, we want to ensure it always shows as empty
localStorage.setItem(`schemas_${dbSlug}`, JSON.stringify([])); localStorage.setItem(`schemas_${dbSlug}`, JSON.stringify([]));
localStorage.setItem(`tables_${dbSlug}`, JSON.stringify([])); localStorage.setItem(`tables_${dbSlug}`, JSON.stringify([]));
} else {
// For other databases like MyDataWarehouseDB, we want to preserve their data
// but ensure we're not using stale data
console.log(`Preserving schemas for database: ${dbName}`);
// We don't need to clear anything for MyDataWarehouseDB
// This ensures its schemas will be loaded from the API or mock data
} }
} catch (error) { } catch (error) {
console.error('Error clearing schemas from localStorage:', error); console.error('Error managing cached data in localStorage:', error);
} }
// Store the selected database info in localStorage for the DataFlow component to use // Store the selected database info in localStorage for the DataFlow component to use
@ -1929,23 +1933,34 @@ const InfiniteCanvas = () => {
id: dbId, id: dbId,
name: dbName, name: dbName,
slug: dbSlug, // Include the slug for API calls slug: dbSlug, // Include the slug for API calls
isEmpty: dbSlug === 'my_dwh2' // Flag to indicate if this database has no schemas isEmpty: dbSlug === 'my_dwh2', // Flag to indicate if this database has no schemas
timestamp: Date.now() // Add timestamp to ensure uniqueness
})); }));
// Set the current database slug in mockData.js // Set the current database slug in mockData.js
if (typeof window.setCurrentDbSlug === 'function') { if (typeof window.setCurrentDbSlug === 'function') {
window.setCurrentDbSlug(dbSlug); // First clear any existing data
window.setCurrentDbSlug(null);
// Then set the new database slug
setTimeout(() => {
window.setCurrentDbSlug(dbSlug);
}, 50);
} else { } else {
console.warn('window.setCurrentDbSlug function is not available'); console.warn('window.setCurrentDbSlug function is not available');
} }
// Check if this database should have empty schemas
const isEmpty = dbSlug === 'my_dwh2';
// Trigger an event that App.jsx can listen to // Trigger an event that App.jsx can listen to
const event = new CustomEvent('viewDataFlow', { const event = new CustomEvent('viewDataFlow', {
detail: { detail: {
databaseId: dbId, databaseId: dbId,
databaseName: dbName, databaseName: dbName,
databaseSlug: dbSlug, databaseSlug: dbSlug,
isEmpty: dbSlug === 'my_dwh2' // Flag to indicate if this database has no schemas isEmpty: isEmpty, // Flag to indicate if this database has no schemas
timestamp: Date.now() // Add timestamp to ensure uniqueness
} }
}); });
window.dispatchEvent(event); window.dispatchEvent(event);
@ -2515,7 +2530,7 @@ const InfiniteCanvas = () => {
padding: 0, padding: 0,
cursor: 'pointer' cursor: 'pointer'
}} }}
title="Add Entity" title="Add DataSource"
> >
<svg width="99" height="36" viewBox="0 0 99 36" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="99" height="36" viewBox="0 0 99 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="0.5" y="0.5" width="98" height="35" rx="7.5" fill="#3EA29A"/> <rect x="0.5" y="0.5" width="98" height="35" rx="7.5" fill="#3EA29A"/>
@ -2809,7 +2824,7 @@ const InfiniteCanvas = () => {
{/* Entity Creation Modal */} {/* Entity Creation Modal */}
<Modal <Modal
isOpen={showAddEntityModal} isOpen={showAddEntityModal}
title="Add Entity" title="Add DataSource"
onClose={() => { onClose={() => {
setShowAddEntityModal(false); setShowAddEntityModal(false);
setActiveEntityType('database'); setActiveEntityType('database');

View File

@ -2,19 +2,50 @@ import React, { useState } from 'react';
import { FaPlus, FaTimes, FaTable, FaLayerGroup } from 'react-icons/fa'; import { FaPlus, FaTimes, FaTable, FaLayerGroup } from 'react-icons/fa';
import { CustomDatabaseIcon, CustomDocumentIcon, CustomDimensionIcon } from './CustomIcons'; import { CustomDatabaseIcon, CustomDocumentIcon, CustomDimensionIcon } from './CustomIcons';
const TableCreationPopup = ({ onClose, onCreateTable }) => { // Add a style tag for animations
const styles = `
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes fadeOut {
from { opacity: 1; transform: translateY(0); }
to { opacity: 0; transform: translateY(-10px); }
}
`;
const TableCreationPopup = ({ onClose, onCreateTable, schemaInfo }) => {
const [tableName, setTableName] = useState(''); const [tableName, setTableName] = useState('');
const [externalName, setExternalName] = useState('');
const [tableType, setTableType] = useState('stage'); const [tableType, setTableType] = useState('stage');
const [columns, setColumns] = useState([{ name: '', type: 'string' }]); const [description, setDescription] = useState('');
const [columns, setColumns] = useState([{ name: '', type: 'TEXT' }]);
const [newColumnName, setNewColumnName] = useState(''); const [newColumnName, setNewColumnName] = useState('');
const [newColumnType, setNewColumnType] = useState('string'); const [newColumnType, setNewColumnType] = useState('TEXT');
const [addSuccess, setAddSuccess] = useState(false); // To show success message
const handleAddColumn = () => { const handleAddColumn = () => {
if (newColumnName.trim() === '') return; console.log('handleAddColumn called');
if (newColumnName.trim() === '') {
console.log('Column name is empty, not adding');
return;
}
console.log('Adding new column:', { name: newColumnName, type: newColumnType });
setColumns([...columns, { name: newColumnName, type: newColumnType }]); setColumns([...columns, { name: newColumnName, type: newColumnType }]);
// Show success message
setAddSuccess(true);
// Clear the form
setNewColumnName(''); setNewColumnName('');
setNewColumnType('string'); setNewColumnType('TEXT'); // Changed from 'string' to 'TEXT' to match our options
// Hide success message after 2 seconds
setTimeout(() => {
setAddSuccess(false);
}, 2000);
}; };
const handleRemoveColumn = (index) => { const handleRemoveColumn = (index) => {
@ -33,8 +64,15 @@ const TableCreationPopup = ({ onClose, onCreateTable }) => {
onCreateTable({ onCreateTable({
name: tableName, name: tableName,
type: tableType, external_name: externalName || tableName,
columns: validColumns.map(col => col.name) table_type: tableType,
description: description,
sch: schemaInfo.sch,
con: schemaInfo.con,
columns: validColumns.map(col => ({
name: col.name,
data_type: col.type
}))
}); });
onClose(); onClose();
@ -54,7 +92,11 @@ const TableCreationPopup = ({ onClose, onCreateTable }) => {
}; };
return ( return (
<div className="popup-overlay" style={{ <>
{/* Add the style tag for animations */}
<style dangerouslySetInnerHTML={{ __html: styles }} />
<div className="popup-overlay" style={{
position: 'fixed', position: 'fixed',
top: 0, top: 0,
left: 0, left: 0,
@ -96,6 +138,14 @@ const TableCreationPopup = ({ onClose, onCreateTable }) => {
</div> </div>
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div style={{ marginBottom: '15px', padding: '10px', backgroundColor: '#f5f5f5', borderRadius: '4px' }}>
<p style={{ margin: 0, color: '#333' }}>
<strong>Schema:</strong> {schemaInfo?.sch}
<br />
<strong>Database:</strong> {schemaInfo?.con}
</p>
</div>
<div style={{ marginBottom: '15px' }}> <div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black"}}> <label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black"}}>
Table Name: Table Name:
@ -114,6 +164,42 @@ const TableCreationPopup = ({ onClose, onCreateTable }) => {
required required
/> />
</div> </div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black"}}>
External Name (Optional):
</label>
<input
type="text"
value={externalName}
onChange={(e) => setExternalName(e.target.value)}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9'
}}
placeholder="Enter external name"
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black"}}>
Description:
</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
minHeight: '80px'
}}
placeholder="Enter table description"
/>
</div>
<div style={{ marginBottom: '15px' }}> <div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}> <label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "black" }}>
@ -246,10 +332,13 @@ const TableCreationPopup = ({ onClose, onCreateTable }) => {
border: '1px solid #d9d9d9' border: '1px solid #d9d9d9'
}} }}
> >
<option value="string">String</option> <option value="TEXT">TEXT</option>
<option value="number">Number</option> <option value="INTEGER">INTEGER</option>
<option value="date">Date</option> <option value="FLOAT">FLOAT</option>
<option value="boolean">Boolean</option> <option value="BOOLEAN">BOOLEAN</option>
<option value="DATE">DATE</option>
<option value="TIMESTAMP">TIMESTAMP</option>
<option value="JSON">JSON</option>
</select> </select>
</div> </div>
<button <button
@ -281,12 +370,38 @@ const TableCreationPopup = ({ onClose, onCreateTable }) => {
)} )}
</div> </div>
<div style={{ display: 'flex', gap: '10px', alignItems: 'flex-end' }}> <div style={{ marginTop: '15px', marginBottom: '5px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: "#333", fontSize: '14px' }}>
Add New Column:
</label>
{/* Success message */}
{addSuccess && (
<div style={{
color: '#52c41a',
fontSize: '14px',
display: 'flex',
alignItems: 'center',
gap: '5px',
animation: 'fadeIn 0.3s ease-in-out'
}}>
<span style={{ fontSize: '16px' }}></span> Column added!
</div>
)}
</div>
<div style={{ display: 'flex', gap: '10px', alignItems: 'flex-end', marginBottom: '10px' }}>
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
<input <input
type="text" type="text"
value={newColumnName} value={newColumnName}
onChange={(e) => setNewColumnName(e.target.value)} onChange={(e) => setNewColumnName(e.target.value)}
onKeyDown={(e) => {
// Add column when Enter key is pressed
if (e.key === 'Enter' && newColumnName.trim() !== '') {
e.preventDefault();
handleAddColumn();
}
}}
placeholder="New column name" placeholder="New column name"
style={{ style={{
width: '100%', width: '100%',
@ -307,25 +422,45 @@ const TableCreationPopup = ({ onClose, onCreateTable }) => {
border: '1px solid #d9d9d9' border: '1px solid #d9d9d9'
}} }}
> >
<option value="string">String</option> <option value="TEXT">TEXT</option>
<option value="number">Number</option> <option value="INTEGER">INTEGER</option>
<option value="date">Date</option> <option value="FLOAT">FLOAT</option>
<option value="boolean">Boolean</option> <option value="BOOLEAN">BOOLEAN</option>
<option value="DATE">DATE</option>
<option value="TIMESTAMP">TIMESTAMP</option>
<option value="JSON">JSON</option>
</select> </select>
</div> </div>
<button <button
type="button" type="button"
onClick={handleAddColumn} onClick={(e) => {
e.preventDefault(); // Prevent form submission
console.log('Add button clicked');
handleAddColumn();
}}
style={{ style={{
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center',
gap: '5px', gap: '5px',
padding: '8px 12px', padding: '8px 12px',
background: '#1890ff', background: '#1890ff',
color: 'white', color: 'white',
border: 'none', border: 'none',
borderRadius: '4px', borderRadius: '4px',
cursor: 'pointer' cursor: 'pointer',
minWidth: '80px',
fontWeight: 'bold',
transition: 'all 0.2s ease',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)'
}}
onMouseOver={(e) => {
e.currentTarget.style.background = '#096dd9';
e.currentTarget.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)';
}}
onMouseOut={(e) => {
e.currentTarget.style.background = '#1890ff';
e.currentTarget.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.1)';
}} }}
> >
<FaPlus size={12} /> <FaPlus size={12} />
@ -370,6 +505,7 @@ const TableCreationPopup = ({ onClose, onCreateTable }) => {
</form> </form>
</div> </div>
</div> </div>
</>
); );
}; };

View File

@ -0,0 +1,286 @@
import React, { useState, useEffect } from 'react';
import { FaTimes, FaTable, FaSave } from 'react-icons/fa';
import { CustomDatabaseIcon, CustomDocumentIcon, CustomDimensionIcon } from './CustomIcons';
const TableUpdatePopup = ({ onClose, onUpdateTable, tableInfo }) => {
const [tableName, setTableName] = useState('');
const [externalName, setExternalName] = useState('');
const [tableType, setTableType] = useState('stage');
const [description, setDescription] = useState('');
// Initialize form with table info
useEffect(() => {
if (tableInfo) {
setTableName(tableInfo.name || '');
setExternalName(tableInfo.external_name || '');
setTableType(tableInfo.table_type || 'stage');
setDescription(tableInfo.description || '');
}
}, [tableInfo]);
// Handle form submission
const handleSubmit = (e) => {
e.preventDefault();
// Validate form
if (!tableName.trim()) {
alert('Table name is required');
return;
}
// Call the update table function
onUpdateTable({
tbl: tableInfo.tbl,
sch: tableInfo.sch,
con: tableInfo.con,
name: tableName,
external_name: externalName,
table_type: tableType,
description: description
});
};
// Get the appropriate icon based on table type
const getTableIcon = () => {
switch (tableType) {
case 'stage':
return <CustomDatabaseIcon width="16" height="16" />;
case 'dimension':
return <CustomDimensionIcon width="16" height="16" />;
case 'fact':
return <CustomDocumentIcon width="16" height="16" />;
default:
return <FaTable />;
}
};
return (
<div className="popup-overlay" style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.5)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1000
}}>
<div className="popup-content" style={{
backgroundColor: 'white',
padding: '20px',
borderRadius: '8px',
width: '500px',
maxWidth: '90%',
maxHeight: '90vh',
overflowY: 'auto',
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '20px' }}>
<h2 style={{ margin: 0, display: 'flex', alignItems: 'center', gap: '10px', color: 'black' }}>
{getTableIcon()}
Update Table
</h2>
<button
onClick={onClose}
style={{
background: 'none',
border: 'none',
fontSize: '20px',
cursor: 'pointer',
color: '#999'
}}
>
<FaTimes />
</button>
</div>
<div style={{ marginBottom: '15px', padding: '10px', backgroundColor: '#f5f5f5', borderRadius: '4px' }}>
<p style={{ margin: 0, color: '#333' }}>
<strong>Table:</strong> {tableInfo.tbl}
<br />
<strong>Schema:</strong> {tableInfo.sch}
<br />
<strong>Database:</strong> {tableInfo.con}
</p>
</div>
<form onSubmit={handleSubmit}>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: 'black' }}>
Table Name:
</label>
<input
type="text"
value={tableName}
onChange={(e) => setTableName(e.target.value)}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
color: 'black'
}}
placeholder="Enter table name"
required
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: 'black' }}>
External Name:
</label>
<input
type="text"
value={externalName}
onChange={(e) => setExternalName(e.target.value)}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
color: 'black'
}}
placeholder="Enter external name (optional)"
/>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: 'black' }}>
Table Type:
</label>
<div style={{ display: 'flex', gap: '15px' }}>
<label style={{
display: 'flex',
alignItems: 'center',
gap: '5px',
cursor: 'pointer',
padding: '8px 12px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
backgroundColor: tableType === 'stage' ? 'rgba(250, 140, 22, 0.1)' : 'transparent',
borderColor: tableType === 'stage' ? '#fa8c16' : '#d9d9d9',
color: 'black'
}}>
<input
type="radio"
name="tableType"
value="stage"
checked={tableType === 'stage'}
onChange={() => setTableType('stage')}
style={{ marginRight: '5px' }}
/>
<CustomDatabaseIcon width="16" height="16" style={{ marginRight: '5px' }} />
Stage
</label>
<label style={{
display: 'flex',
alignItems: 'center',
gap: '5px',
cursor: 'pointer',
padding: '8px 12px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
backgroundColor: tableType === 'fact' ? 'rgba(24, 144, 255, 0.1)' : 'transparent',
borderColor: tableType === 'fact' ? '#1890ff' : '#d9d9d9',
color: 'black'
}}>
<input
type="radio"
name="tableType"
value="fact"
checked={tableType === 'fact'}
onChange={() => setTableType('fact')}
style={{ marginRight: '5px' }}
/>
<CustomDocumentIcon width="16" height="16" style={{ marginRight: '5px' }} />
Fact
</label>
<label style={{
display: 'flex',
alignItems: 'center',
gap: '5px',
cursor: 'pointer',
padding: '8px 12px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
backgroundColor: tableType === 'dimension' ? 'rgba(82, 196, 26, 0.1)' : 'transparent',
borderColor: tableType === 'dimension' ? '#52c41a' : '#d9d9d9',
color: 'black'
}}>
<input
type="radio"
name="tableType"
value="dimension"
checked={tableType === 'dimension'}
onChange={() => setTableType('dimension')}
style={{ marginRight: '5px' }}
/>
<CustomDimensionIcon width="16" height="16" style={{ marginRight: '5px' }} />
Dimension
</label>
</div>
</div>
<div style={{ marginBottom: '15px' }}>
<label style={{ display: 'block', marginBottom: '5px', fontWeight: 'bold', color: 'black' }}>
Description:
</label>
<textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
style={{
width: '100%',
padding: '8px',
borderRadius: '4px',
border: '1px solid #d9d9d9',
minHeight: '80px',
color: 'black'
}}
placeholder="Enter table description (optional)"
/>
</div>
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: '10px', marginTop: '20px' }}>
<button
type="button"
onClick={onClose}
style={{
padding: '8px 16px',
background: 'white',
color: '#333',
border: '1px solid #d9d9d9',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Cancel
</button>
<button
type="submit"
style={{
padding: '8px 16px',
background: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
gap: '5px'
}}
>
<FaSave size={14} />
Update Table
</button>
</div>
</form>
</div>
</div>
);
};
export default TableUpdatePopup;

File diff suppressed because it is too large Load Diff