diff --git a/src/components/AddTableModal.jsx b/src/components/AddTableModal.jsx
index cc86bdf..095f747 100644
--- a/src/components/AddTableModal.jsx
+++ b/src/components/AddTableModal.jsx
@@ -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 = ({
@@ -72,6 +81,10 @@ const AddTableModal = ({
// Validation and UI state
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(() => {
@@ -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 => ({
- name: key.name.trim(),
- column_name: columns.find(col => col.id === key.columnId)?.name || '',
- key_type: key.keyType,
- sequence: key.sequence
- })),
+ keys: keys.flatMap(key =>
+ (key.keyColumns || []).map(kc => ({
+ name: key.name.trim(),
+ column_name: columns.find(col => col.id === kc.columnId)?.name || '',
+ key_type: key.keyType,
+ sequence: kc.sequence
+ }))
+ ),
relations: relations.map(rel => ({
target_table: 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"
/>
@@ -483,95 +629,227 @@ const AddTableModal = ({
+
+
+
+
Error: {error}
+Schema created successfully!
+ {createdSchema && ( +Name: {createdSchema.name}
+Slug: {createdSchema.slug || 'Not provided by API'}
+Description: {createdSchema.description || 'None'}
+Database: {createdSchema.database}
+Created At: {createdSchema.created_at}
+
+{`// 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);
+ }
+};`}
+
+