Refatcored the Add New Table Modal for ER Diagram Screen #11
|
|
@ -17,7 +17,15 @@ import {
|
|||
Grid,
|
||||
Paper,
|
||||
Chip,
|
||||
Alert
|
||||
Alert,
|
||||
Autocomplete,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Checkbox
|
||||
} from '@mui/material';
|
||||
import {
|
||||
FaPlus as AddIcon,
|
||||
|
|
@ -25,7 +33,8 @@ import {
|
|||
FaTimes as CloseIcon,
|
||||
FaTable as TableIcon,
|
||||
FaKey as KeyIcon,
|
||||
FaLink as LinkIcon
|
||||
FaLink as LinkIcon,
|
||||
FaArrowLeft as BackIcon
|
||||
} from 'react-icons/fa';
|
||||
|
||||
const AddTableModal = ({
|
||||
|
|
@ -73,6 +82,10 @@ const AddTableModal = ({
|
|||
const [errors, setErrors] = useState({});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
// Keys section navigation state
|
||||
const [keysViewMode, setKeysViewMode] = useState('keys'); // 'keys' or 'columns'
|
||||
const [selectedKeyForColumns, setSelectedKeyForColumns] = useState(null);
|
||||
|
||||
// Reset form when modal opens/closes
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
|
|
@ -92,6 +105,8 @@ const AddTableModal = ({
|
|||
setRelations([]);
|
||||
setErrors({});
|
||||
setIsSubmitting(false);
|
||||
setKeysViewMode('keys');
|
||||
setSelectedKeyForColumns(null);
|
||||
};
|
||||
|
||||
// Form field handlers
|
||||
|
|
@ -126,12 +141,38 @@ const AddTableModal = ({
|
|||
setColumns(prev => prev.map(col =>
|
||||
col.id === id ? { ...col, [field]: value } : col
|
||||
));
|
||||
|
||||
// Clear column errors when user starts typing
|
||||
if (field === 'name' && errors.columns?.[id]) {
|
||||
setErrors(prev => ({
|
||||
...prev,
|
||||
columns: {
|
||||
...prev.columns,
|
||||
[id]: null
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to check if column name is duplicate
|
||||
const isColumnNameDuplicate = (columnId, columnName) => {
|
||||
if (!columnName.trim()) return false;
|
||||
|
||||
const trimmedName = columnName.trim().toLowerCase();
|
||||
return columns.some(col =>
|
||||
col.id !== columnId &&
|
||||
col.name.trim().toLowerCase() === trimmedName
|
||||
);
|
||||
};
|
||||
|
||||
const removeColumn = (id) => {
|
||||
setColumns(prev => prev.filter(col => col.id !== id));
|
||||
// Remove any keys that reference this column
|
||||
setKeys(prev => prev.filter(key => key.columnId !== id));
|
||||
// Remove any keys that reference this column or update key columns
|
||||
setKeys(prev => prev.map(key => ({
|
||||
...key,
|
||||
columnIds: key.columnIds?.filter(colId => colId !== id) || [],
|
||||
keyColumns: key.keyColumns?.filter(keyCol => keyCol.columnId !== id) || []
|
||||
})).filter(key => key.keyColumns?.length > 0 || key.columnIds?.length > 0));
|
||||
};
|
||||
|
||||
// Key management
|
||||
|
|
@ -139,9 +180,9 @@ const AddTableModal = ({
|
|||
const newKey = {
|
||||
id: Date.now(),
|
||||
name: '',
|
||||
columnId: '',
|
||||
sequence: 1,
|
||||
keyType: 'PRIMARY' // PRIMARY, FOREIGN, UNIQUE
|
||||
columnIds: [], // Changed to array for multi-select
|
||||
keyType: 'PRIMARY', // PRIMARY, FOREIGN, UNIQUE
|
||||
keyColumns: [] // Array of {columnId, sequence} objects
|
||||
};
|
||||
setKeys(prev => [...prev, newKey]);
|
||||
};
|
||||
|
|
@ -156,6 +197,70 @@ const AddTableModal = ({
|
|||
setKeys(prev => prev.filter(key => key.id !== id));
|
||||
};
|
||||
|
||||
// Key column management
|
||||
const addColumnToKey = (keyId, columnId) => {
|
||||
setKeys(prev => prev.map(key => {
|
||||
if (key.id === keyId) {
|
||||
const existingSequences = key.keyColumns?.map(kc => kc.sequence) || [];
|
||||
const nextSequence = existingSequences.length > 0 ? Math.max(...existingSequences) + 1 : 1;
|
||||
|
||||
return {
|
||||
...key,
|
||||
columnIds: [...(key.columnIds || []), columnId],
|
||||
keyColumns: [...(key.keyColumns || []), { columnId, sequence: nextSequence }]
|
||||
};
|
||||
}
|
||||
return key;
|
||||
}));
|
||||
};
|
||||
|
||||
const removeColumnFromKey = (keyId, columnId) => {
|
||||
setKeys(prev => prev.map(key => {
|
||||
if (key.id === keyId) {
|
||||
return {
|
||||
...key,
|
||||
columnIds: (key.columnIds || []).filter(id => id !== columnId),
|
||||
keyColumns: (key.keyColumns || []).filter(kc => kc.columnId !== columnId)
|
||||
};
|
||||
}
|
||||
return key;
|
||||
}));
|
||||
};
|
||||
|
||||
const updateKeyColumnSequence = (keyId, columnId, sequence) => {
|
||||
setKeys(prev => prev.map(key => {
|
||||
if (key.id === keyId) {
|
||||
return {
|
||||
...key,
|
||||
keyColumns: (key.keyColumns || []).map(kc =>
|
||||
kc.columnId === columnId ? { ...kc, sequence: parseInt(sequence) || 1 } : kc
|
||||
)
|
||||
};
|
||||
}
|
||||
return key;
|
||||
}));
|
||||
};
|
||||
|
||||
// Helper function to check for duplicate sequences within a key
|
||||
const hasSequenceDuplicates = (keyId) => {
|
||||
const key = keys.find(k => k.id === keyId);
|
||||
if (!key || !key.keyColumns) return false;
|
||||
|
||||
const sequences = key.keyColumns.map(kc => kc.sequence);
|
||||
return sequences.length !== new Set(sequences).size;
|
||||
};
|
||||
|
||||
// Keys section navigation functions
|
||||
const handleKeyRowClick = (keyId) => {
|
||||
setSelectedKeyForColumns(keyId);
|
||||
setKeysViewMode('columns');
|
||||
};
|
||||
|
||||
const handleBackToKeys = () => {
|
||||
setKeysViewMode('keys');
|
||||
setSelectedKeyForColumns(null);
|
||||
};
|
||||
|
||||
// Relation management
|
||||
const addRelation = () => {
|
||||
const newRelation = {
|
||||
|
|
@ -181,11 +286,18 @@ const AddTableModal = ({
|
|||
// Get available keys for relations
|
||||
const getAvailableKeys = () => {
|
||||
return keys.map(key => {
|
||||
const column = columns.find(col => col.id === key.columnId);
|
||||
const keyColumnNames = (key.keyColumns || [])
|
||||
.sort((a, b) => a.sequence - b.sequence)
|
||||
.map(kc => {
|
||||
const column = columns.find(col => col.id === kc.columnId);
|
||||
return column?.name || 'Unknown Column';
|
||||
})
|
||||
.join(', ');
|
||||
|
||||
return {
|
||||
id: key.id,
|
||||
name: key.name,
|
||||
columnName: column?.name || 'Unknown Column'
|
||||
columnName: keyColumnNames || 'No columns selected'
|
||||
};
|
||||
});
|
||||
};
|
||||
|
|
@ -222,9 +334,27 @@ const AddTableModal = ({
|
|||
|
||||
// Validate columns
|
||||
const columnErrors = {};
|
||||
const columnNames = new Set();
|
||||
const duplicateNames = new Set();
|
||||
|
||||
// First pass: identify duplicate names
|
||||
columns.forEach(col => {
|
||||
if (!col.name.trim()) {
|
||||
const trimmedName = col.name.trim().toLowerCase();
|
||||
if (trimmedName && columnNames.has(trimmedName)) {
|
||||
duplicateNames.add(trimmedName);
|
||||
}
|
||||
if (trimmedName) {
|
||||
columnNames.add(trimmedName);
|
||||
}
|
||||
});
|
||||
|
||||
// Second pass: validate each column
|
||||
columns.forEach(col => {
|
||||
const trimmedName = col.name.trim();
|
||||
if (!trimmedName) {
|
||||
columnErrors[col.id] = 'Column name is required';
|
||||
} else if (duplicateNames.has(trimmedName.toLowerCase())) {
|
||||
columnErrors[col.id] = 'Column name must be unique within the table';
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -238,8 +368,15 @@ const AddTableModal = ({
|
|||
if (!key.name.trim()) {
|
||||
keyErrors[key.id] = 'Key name is required';
|
||||
}
|
||||
if (!key.columnId) {
|
||||
keyErrors[key.id] = 'Column selection is required';
|
||||
if (!key.keyColumns || key.keyColumns.length === 0) {
|
||||
keyErrors[key.id] = 'At least one column must be selected';
|
||||
} else {
|
||||
// Check for duplicate sequences within the key
|
||||
const sequences = key.keyColumns.map(kc => kc.sequence);
|
||||
const duplicateSequences = sequences.filter((seq, index) => sequences.indexOf(seq) !== index);
|
||||
if (duplicateSequences.length > 0) {
|
||||
keyErrors[key.id] = `Duplicate sequence numbers found: ${[...new Set(duplicateSequences)].join(', ')}`;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -269,16 +406,22 @@ const AddTableModal = ({
|
|||
columns: columns.map(col => ({
|
||||
name: col.name.trim(),
|
||||
data_type: col.type,
|
||||
is_primary_key: keys.some(key => key.columnId === col.id && key.keyType === 'PRIMARY'),
|
||||
is_foreign_key: keys.some(key => key.columnId === col.id && key.keyType === 'FOREIGN'),
|
||||
is_primary_key: keys.some(key =>
|
||||
key.keyColumns?.some(kc => kc.columnId === col.id) && key.keyType === 'PRIMARY'
|
||||
),
|
||||
is_foreign_key: keys.some(key =>
|
||||
key.keyColumns?.some(kc => kc.columnId === col.id) && key.keyType === 'FOREIGN'
|
||||
),
|
||||
is_nullable: col.isNullable
|
||||
})),
|
||||
keys: keys.map(key => ({
|
||||
keys: keys.flatMap(key =>
|
||||
(key.keyColumns || []).map(kc => ({
|
||||
name: key.name.trim(),
|
||||
column_name: columns.find(col => col.id === key.columnId)?.name || '',
|
||||
column_name: columns.find(col => col.id === kc.columnId)?.name || '',
|
||||
key_type: key.keyType,
|
||||
sequence: key.sequence
|
||||
})),
|
||||
sequence: kc.sequence
|
||||
}))
|
||||
),
|
||||
relations: relations.map(rel => ({
|
||||
target_table: rel.targetTable,
|
||||
source_key: rel.sourceKey,
|
||||
|
|
@ -429,8 +572,11 @@ const AddTableModal = ({
|
|||
label="Column Name"
|
||||
value={column.name}
|
||||
onChange={(e) => updateColumn(column.id, 'name', e.target.value)}
|
||||
error={!!errors.columns?.[column.id]}
|
||||
helperText={errors.columns?.[column.id]}
|
||||
error={!!errors.columns?.[column.id] || isColumnNameDuplicate(column.id, column.name)}
|
||||
helperText={
|
||||
errors.columns?.[column.id] ||
|
||||
(isColumnNameDuplicate(column.id, column.name) ? 'Column name must be unique within the table' : '')
|
||||
}
|
||||
size="small"
|
||||
/>
|
||||
</Grid>
|
||||
|
|
@ -483,7 +629,14 @@ const AddTableModal = ({
|
|||
<Typography variant="h6" color="primary">
|
||||
<KeyIcon style={{ marginRight: '8px', verticalAlign: 'middle' }} />
|
||||
Keys
|
||||
{keysViewMode === 'columns' && selectedKeyForColumns && (
|
||||
<Typography component="span" variant="body2" sx={{ ml: 2, color: 'text.secondary' }}>
|
||||
- {keys.find(k => k.id === selectedKeyForColumns)?.name || 'Unnamed Key'}
|
||||
</Typography>
|
||||
)}
|
||||
</Typography>
|
||||
|
||||
{keysViewMode === 'keys' ? (
|
||||
<Button
|
||||
startIcon={<AddIcon />}
|
||||
onClick={addKey}
|
||||
|
|
@ -493,85 +646,210 @@ const AddTableModal = ({
|
|||
>
|
||||
Add Key
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
startIcon={<BackIcon />}
|
||||
onClick={handleBackToKeys}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
>
|
||||
Add Key
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{keys.length === 0 ? (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', py: 2 }}>
|
||||
{columns.length === 0
|
||||
? "Add columns first before defining keys."
|
||||
: "No keys defined yet. Click \"Add Key\" to create primary or foreign keys."
|
||||
}
|
||||
</Typography>
|
||||
) : (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
||||
{keysViewMode === 'keys' ? (
|
||||
// Keys Table View
|
||||
<TableContainer component={Paper} variant="outlined">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox disabled />
|
||||
</TableCell>
|
||||
<TableCell>Key Name</TableCell>
|
||||
<TableCell>Key Type</TableCell>
|
||||
<TableCell width="100">Actions</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{keys.map((key) => (
|
||||
<Paper key={key.id} variant="outlined" sx={{ p: 2 }}>
|
||||
<Grid container spacing={2} alignItems="center">
|
||||
<Grid item xs={12} md={3}>
|
||||
<TableRow
|
||||
key={key.id}
|
||||
hover
|
||||
onClick={() => handleKeyRowClick(key.id)}
|
||||
sx={{ cursor: 'pointer' }}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={false}
|
||||
onChange={(e) => e.stopPropagation()}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Key Name"
|
||||
value={key.name}
|
||||
onChange={(e) => updateKey(key.id, 'name', e.target.value)}
|
||||
onChange={(e) => {
|
||||
e.stopPropagation();
|
||||
updateKey(key.id, 'name', e.target.value);
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
size="small"
|
||||
variant="standard"
|
||||
error={!!errors.keys?.[key.id]}
|
||||
helperText={errors.keys?.[key.id]}
|
||||
size="small"
|
||||
placeholder="Enter key name"
|
||||
fullWidth
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={3}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Column</InputLabel>
|
||||
<Select
|
||||
value={key.columnId}
|
||||
onChange={(e) => updateKey(key.id, 'columnId', e.target.value)}
|
||||
label="Column"
|
||||
>
|
||||
{columns.map(col => (
|
||||
<MenuItem key={col.id} value={col.id}>
|
||||
{col.name || 'Unnamed Column'}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
<FormControl fullWidth size="small">
|
||||
<InputLabel>Key Type</InputLabel>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<FormControl size="small" variant="standard" sx={{ minWidth: 120 }}>
|
||||
<Select
|
||||
value={key.keyType}
|
||||
onChange={(e) => updateKey(key.id, 'keyType', e.target.value)}
|
||||
label="Key Type"
|
||||
onChange={(e) => {
|
||||
e.stopPropagation();
|
||||
updateKey(key.id, 'keyType', e.target.value);
|
||||
}}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
<MenuItem value="PRIMARY">Primary</MenuItem>
|
||||
<MenuItem value="FOREIGN">Foreign</MenuItem>
|
||||
<MenuItem value="UNIQUE">Unique</MenuItem>
|
||||
<MenuItem value="PRIMARY">Primary Key</MenuItem>
|
||||
<MenuItem value="FOREIGN">Foreign Key</MenuItem>
|
||||
<MenuItem value="UNIQUE">Unique Key</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
<TextField
|
||||
fullWidth
|
||||
label="Sequence"
|
||||
type="number"
|
||||
value={key.sequence}
|
||||
onChange={(e) => updateKey(key.id, 'sequence', parseInt(e.target.value) || 1)}
|
||||
size="small"
|
||||
inputProps={{ min: 1 }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={2}>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<IconButton
|
||||
onClick={() => removeKey(key.id)}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
removeKey(key.id);
|
||||
}}
|
||||
color="error"
|
||||
size="small"
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Paper>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</Box>
|
||||
{/* Empty row for adding new key */}
|
||||
<TableRow hover onClick={addKey} sx={{ cursor: 'pointer', backgroundColor: 'rgba(0, 0, 0, 0.02)' }}>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox disabled />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontStyle: 'italic' }}>
|
||||
Click to add new key...
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
-
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<AddIcon style={{ color: '#ccc' }} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
) : (
|
||||
// Columns Table View for Selected Key
|
||||
selectedKeyForColumns && (
|
||||
<TableContainer component={Paper} variant="outlined">
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox disabled />
|
||||
</TableCell>
|
||||
<TableCell>Column Name</TableCell>
|
||||
<TableCell>Sequence</TableCell>
|
||||
<TableCell width="100">Actions</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{columns.map((column) => {
|
||||
const selectedKey = keys.find(k => k.id === selectedKeyForColumns);
|
||||
const keyColumn = selectedKey?.keyColumns?.find(kc => kc.columnId === column.id);
|
||||
const isSelected = !!keyColumn;
|
||||
const hasSequenceError = hasSequenceDuplicates(selectedKeyForColumns);
|
||||
|
||||
return (
|
||||
<TableRow key={column.id}>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isSelected}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
addColumnToKey(selectedKeyForColumns, column.id);
|
||||
} else {
|
||||
removeColumnFromKey(selectedKeyForColumns, column.id);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>{column.name || 'Unnamed Column'}</TableCell>
|
||||
<TableCell>
|
||||
{isSelected ? (
|
||||
<TextField
|
||||
type="number"
|
||||
value={keyColumn?.sequence || 1}
|
||||
onChange={(e) => updateKeyColumnSequence(selectedKeyForColumns, column.id, e.target.value)}
|
||||
size="small"
|
||||
inputProps={{ min: 1, style: { width: '80px' } }}
|
||||
error={hasSequenceError}
|
||||
helperText={hasSequenceError ? 'Duplicate' : ''}
|
||||
/>
|
||||
) : (
|
||||
<Typography variant="body2" color="text.secondary">-</Typography>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{isSelected && (
|
||||
<IconButton
|
||||
size="small"
|
||||
color="error"
|
||||
onClick={() => removeColumnFromKey(selectedKeyForColumns, column.id)}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
{/* Empty row for adding new column */}
|
||||
<TableRow sx={{ backgroundColor: 'rgba(0, 0, 0, 0.02)' }}>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox disabled />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontStyle: 'italic' }}>
|
||||
Select columns above to add to this key...
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
-
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<AddIcon style={{ color: '#ccc' }} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* Show message when no columns exist */}
|
||||
{columns.length === 0 && (
|
||||
<Typography variant="body2" color="text.secondary" sx={{ textAlign: 'center', py: 2 }}>
|
||||
Add columns first before defining keys.
|
||||
</Typography>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
|
|
|
|||
|
|
@ -1229,11 +1229,8 @@ const ERDiagramCanvasContent = () => {
|
|||
const rows = Math.ceil(tableCount / tablesPerRow);
|
||||
|
||||
// Calculate schema dimensions with proper padding for tables
|
||||
const tableWidth = 260; // Width of each table node
|
||||
const tableHeight = 280; // Height of each table node (including spacing)
|
||||
const tableSpacingCalc = 330; // Horizontal spacing between tables
|
||||
const tableRowSpacingCalc = 320; // Vertical spacing between table rows
|
||||
const schemaPadding = 200; // Padding around the schema content
|
||||
|
||||
// Calculate required width and height based on table layout
|
||||
const tableStartX = 90; // Starting X position within schema
|
||||
|
|
@ -1241,8 +1238,8 @@ const ERDiagramCanvasContent = () => {
|
|||
const requiredWidth = tableStartX + (tablesPerRow * tableSpacingCalc) + 100; // Extra margin
|
||||
const requiredHeight = tableStartY + (rows * tableRowSpacingCalc) + 100; // Extra margin
|
||||
|
||||
const schemaWidth = Math.max(900, requiredWidth);
|
||||
const schemaHeight = Math.max(650, requiredHeight);
|
||||
const schemaWidth = Math.max(1000, requiredWidth);
|
||||
const schemaHeight = Math.max(850, requiredHeight);
|
||||
|
||||
console.log(`Schema "${schema.name || schema.sch}" layout: ${tableCount} tables, ${tablesPerRow} per row, ${rows} rows`);
|
||||
console.log(`Required dimensions: ${requiredWidth}x${requiredHeight}, Final: ${schemaWidth}x${schemaHeight}`);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,143 @@
|
|||
import React, { useState } from 'react';
|
||||
import { createSchema } from './mockData';
|
||||
|
||||
const SchemaCreationExample = () => {
|
||||
// State for form inputs
|
||||
const [schemaName, setSchemaName] = useState('');
|
||||
const [schemaDescription, setSchemaDescription] = useState('');
|
||||
const [dbSlug, setDbSlug] = useState('my_dwh'); // Default to my_dwh
|
||||
|
||||
// State for API response
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const [createdSchema, setCreatedSchema] = useState(null);
|
||||
|
||||
// Handle form submission
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Reset states
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
setSuccess(false);
|
||||
setCreatedSchema(null);
|
||||
|
||||
try {
|
||||
// Call the createSchema function
|
||||
const newSchema = await createSchema(dbSlug, schemaName, schemaDescription);
|
||||
|
||||
// Handle success
|
||||
setSuccess(true);
|
||||
setCreatedSchema(newSchema);
|
||||
|
||||
// Clear form
|
||||
setSchemaName('');
|
||||
setSchemaDescription('');
|
||||
} catch (err) {
|
||||
// Handle error
|
||||
setError(err.message || 'An error occurred while creating the schema');
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="schema-creation-container">
|
||||
<h2>Create New Schema</h2>
|
||||
|
||||
{/* Form for schema creation */}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="form-group">
|
||||
<label htmlFor="dbSlug">Database:</label>
|
||||
<select
|
||||
id="dbSlug"
|
||||
value={dbSlug}
|
||||
onChange={(e) => setDbSlug(e.target.value)}
|
||||
required
|
||||
>
|
||||
<option value="my_dwh">MyDataWarehouseDB</option>
|
||||
<option value="my_dwh2">Service 2 DB (No schemas allowed)</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="schemaName">Schema Name:</label>
|
||||
<input
|
||||
type="text"
|
||||
id="schemaName"
|
||||
value={schemaName}
|
||||
onChange={(e) => setSchemaName(e.target.value)}
|
||||
required
|
||||
placeholder="Enter schema name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="form-group">
|
||||
<label htmlFor="schemaDescription">Description:</label>
|
||||
<textarea
|
||||
id="schemaDescription"
|
||||
value={schemaDescription}
|
||||
onChange={(e) => setSchemaDescription(e.target.value)}
|
||||
placeholder="Enter schema description (optional)"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
className="submit-button"
|
||||
>
|
||||
{loading ? 'Creating...' : 'Create Schema'}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
{/* Display error message if any */}
|
||||
{error && (
|
||||
<div className="error-message">
|
||||
<p>Error: {error}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Display success message */}
|
||||
{success && (
|
||||
<div className="success-message">
|
||||
<p>Schema created successfully!</p>
|
||||
{createdSchema && (
|
||||
<div className="schema-details">
|
||||
<h3>Created Schema Details:</h3>
|
||||
<p><strong>Name:</strong> {createdSchema.name}</p>
|
||||
<p><strong>Slug:</strong> {createdSchema.slug || 'Not provided by API'}</p>
|
||||
<p><strong>Description:</strong> {createdSchema.description || 'None'}</p>
|
||||
<p><strong>Database:</strong> {createdSchema.database}</p>
|
||||
<p><strong>Created At:</strong> {createdSchema.created_at}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Example API call code */}
|
||||
<div className="api-example">
|
||||
<h3>API Call Example:</h3>
|
||||
<pre>
|
||||
{`// Example API call to create a schema
|
||||
const createNewSchema = async () => {
|
||||
try {
|
||||
const newSchema = await createSchema(
|
||||
'my_dwh', // Database slug
|
||||
'Schema3', // Schema name
|
||||
'This is a test schema.' // Schema description
|
||||
);
|
||||
console.log('Schema created:', newSchema);
|
||||
} catch (error) {
|
||||
console.error('Error creating schema:', error);
|
||||
}
|
||||
};`}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SchemaCreationExample;
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
// Example script to demonstrate how to use the createSchema function
|
||||
import { createSchema } from '../components/mockData';
|
||||
|
||||
/**
|
||||
* This example shows how to create a new schema using the createSchema function
|
||||
* It can be run directly or used as a reference for implementing schema creation
|
||||
* in your application.
|
||||
*/
|
||||
async function createSchemaExample() {
|
||||
try {
|
||||
console.log('Starting schema creation example...');
|
||||
|
||||
// Example payload from the requirements
|
||||
const dbSlug = 'my_dwh';
|
||||
const schemaName = 'Schema3';
|
||||
const schemaDescription = 'This is a test schema.';
|
||||
|
||||
console.log(`Creating schema "${schemaName}" in database "${dbSlug}"...`);
|
||||
|
||||
// Call the createSchema function
|
||||
const newSchema = await createSchema(dbSlug, schemaName, schemaDescription);
|
||||
|
||||
console.log('Schema created successfully!');
|
||||
console.log('New schema details:', newSchema);
|
||||
|
||||
return newSchema;
|
||||
} catch (error) {
|
||||
console.error('Error in schema creation example:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute the example if this script is run directly
|
||||
if (typeof require !== 'undefined' && require.main === module) {
|
||||
createSchemaExample()
|
||||
.then(result => {
|
||||
console.log('Example completed successfully');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Example failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
export default createSchemaExample;
|
||||
Loading…
Reference in New Issue