Integrated keys and column type list get apis in the Add New Table Modal #12
|
|
@ -43,6 +43,7 @@ const AddTableModal = ({
|
||||||
onAddTable,
|
onAddTable,
|
||||||
schemas = [],
|
schemas = [],
|
||||||
existingTables = [],
|
existingTables = [],
|
||||||
|
position = 'center', // 'center' | 'bottom-right'
|
||||||
tableTypes = [
|
tableTypes = [
|
||||||
{ value: 'fact', label: 'Fact Table' },
|
{ value: 'fact', label: 'Fact Table' },
|
||||||
{ value: 'dimension', label: 'Dimension Table' },
|
{ value: 'dimension', label: 'Dimension Table' },
|
||||||
|
|
@ -78,6 +79,14 @@ const AddTableModal = ({
|
||||||
const [keys, setKeys] = useState([]);
|
const [keys, setKeys] = useState([]);
|
||||||
const [relations, setRelations] = useState([]);
|
const [relations, setRelations] = useState([]);
|
||||||
|
|
||||||
|
// Key types from API
|
||||||
|
const [keyTypes, setKeyTypes] = useState([]);
|
||||||
|
const [loadingKeyTypes, setLoadingKeyTypes] = useState(false);
|
||||||
|
|
||||||
|
// Column types from API
|
||||||
|
const [columnTypesFromAPI, setColumnTypesFromAPI] = useState([]);
|
||||||
|
const [loadingColumnTypes, setLoadingColumnTypes] = useState(false);
|
||||||
|
|
||||||
// Validation and UI state
|
// Validation and UI state
|
||||||
const [errors, setErrors] = useState({});
|
const [errors, setErrors] = useState({});
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
|
|
@ -90,9 +99,91 @@ const AddTableModal = ({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open) {
|
if (open) {
|
||||||
resetForm();
|
resetForm();
|
||||||
|
fetchKeyTypes();
|
||||||
|
fetchColumnTypes();
|
||||||
}
|
}
|
||||||
}, [open]);
|
}, [open]);
|
||||||
|
|
||||||
|
// Fetch key types from API
|
||||||
|
const fetchKeyTypes = async () => {
|
||||||
|
setLoadingKeyTypes(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://sandbox.kezel.io/api/qbt_table_key_type_list_get', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: "abdhsg",
|
||||||
|
org: "sN05Pjv11qvH"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.status === 200 && data.items) {
|
||||||
|
setKeyTypes(data.items);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to fetch key types:', data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching key types:', error);
|
||||||
|
// Fallback to default key types
|
||||||
|
setKeyTypes([
|
||||||
|
{ kytp: 'PRIMARY', name: 'Primary' },
|
||||||
|
{ kytp: 'FOREIGN', name: 'Foreign' },
|
||||||
|
{ kytp: 'UNIQUE', name: 'Unique' }
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
setLoadingKeyTypes(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch column types from API
|
||||||
|
const fetchColumnTypes = async () => {
|
||||||
|
setLoadingColumnTypes(true);
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://sandbox.kezel.io/api/qbt_column_type_list_get', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: "abdhsg",
|
||||||
|
org: "sN05Pjv11qvH"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.status === 200 && data.items) {
|
||||||
|
setColumnTypesFromAPI(data.items);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to fetch column types:', data.message);
|
||||||
|
// Fallback to default column types
|
||||||
|
setColumnTypesFromAPI([
|
||||||
|
{ cltp: 'VARCHAR', name: 'VARCHAR', description: 'Variable-length character string' },
|
||||||
|
{ cltp: 'INT', name: 'INT', description: 'Standard integer value' },
|
||||||
|
{ cltp: 'TEXT', name: 'TEXT', description: 'Variable-length character string for long text' },
|
||||||
|
{ cltp: 'DATE', name: 'DATE', description: 'Date value' },
|
||||||
|
{ cltp: 'TIMESTAMP', name: 'TIMESTAMP', description: 'Timestamp value' },
|
||||||
|
{ cltp: 'BOOLEAN', name: 'BOOLEAN', description: 'Logical boolean value' }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching column types:', error);
|
||||||
|
// Fallback to default column types
|
||||||
|
setColumnTypesFromAPI([
|
||||||
|
{ cltp: 'VARCHAR', name: 'VARCHAR', description: 'Variable-length character string' },
|
||||||
|
{ cltp: 'INT', name: 'INT', description: 'Standard integer value' },
|
||||||
|
{ cltp: 'TEXT', name: 'TEXT', description: 'Variable-length character string for long text' },
|
||||||
|
{ cltp: 'DATE', name: 'DATE', description: 'Date value' },
|
||||||
|
{ cltp: 'TIMESTAMP', name: 'TIMESTAMP', description: 'Timestamp value' },
|
||||||
|
{ cltp: 'BOOLEAN', name: 'BOOLEAN', description: 'Logical boolean value' }
|
||||||
|
]);
|
||||||
|
} finally {
|
||||||
|
setLoadingColumnTypes(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
setFormData({
|
setFormData({
|
||||||
name: '',
|
name: '',
|
||||||
|
|
@ -103,6 +194,10 @@ const AddTableModal = ({
|
||||||
setColumns([]);
|
setColumns([]);
|
||||||
setKeys([]);
|
setKeys([]);
|
||||||
setRelations([]);
|
setRelations([]);
|
||||||
|
setKeyTypes([]);
|
||||||
|
setLoadingKeyTypes(false);
|
||||||
|
setColumnTypesFromAPI([]);
|
||||||
|
setLoadingColumnTypes(false);
|
||||||
setErrors({});
|
setErrors({});
|
||||||
setIsSubmitting(false);
|
setIsSubmitting(false);
|
||||||
setKeysViewMode('keys');
|
setKeysViewMode('keys');
|
||||||
|
|
@ -126,10 +221,11 @@ const AddTableModal = ({
|
||||||
|
|
||||||
// Column management
|
// Column management
|
||||||
const addColumn = () => {
|
const addColumn = () => {
|
||||||
|
const defaultColumnType = columnTypesFromAPI.length > 0 ? columnTypesFromAPI[0].name : 'VARCHAR';
|
||||||
const newColumn = {
|
const newColumn = {
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
name: '',
|
name: '',
|
||||||
type: 'VARCHAR(255)',
|
type: defaultColumnType,
|
||||||
isPrimaryKey: false,
|
isPrimaryKey: false,
|
||||||
isForeignKey: false,
|
isForeignKey: false,
|
||||||
isNullable: true
|
isNullable: true
|
||||||
|
|
@ -177,11 +273,12 @@ const AddTableModal = ({
|
||||||
|
|
||||||
// Key management
|
// Key management
|
||||||
const addKey = () => {
|
const addKey = () => {
|
||||||
|
const defaultKeyType = keyTypes.length > 0 ? keyTypes[0].kytp : 'PRIMARY';
|
||||||
const newKey = {
|
const newKey = {
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
name: '',
|
name: '',
|
||||||
columnIds: [], // Changed to array for multi-select
|
columnIds: [], // Changed to array for multi-select
|
||||||
keyType: 'PRIMARY', // PRIMARY, FOREIGN, UNIQUE
|
keyType: defaultKeyType, // Use first available key type from API
|
||||||
keyColumns: [] // Array of {columnId, sequence} objects
|
keyColumns: [] // Array of {columnId, sequence} objects
|
||||||
};
|
};
|
||||||
setKeys(prev => [...prev, newKey]);
|
setKeys(prev => [...prev, newKey]);
|
||||||
|
|
@ -406,12 +503,16 @@ const AddTableModal = ({
|
||||||
columns: columns.map(col => ({
|
columns: columns.map(col => ({
|
||||||
name: col.name.trim(),
|
name: col.name.trim(),
|
||||||
data_type: col.type,
|
data_type: col.type,
|
||||||
is_primary_key: keys.some(key =>
|
is_primary_key: keys.some(key => {
|
||||||
key.keyColumns?.some(kc => kc.columnId === col.id) && key.keyType === 'PRIMARY'
|
const keyTypeObj = keyTypes.find(kt => kt.kytp === key.keyType);
|
||||||
),
|
return key.keyColumns?.some(kc => kc.columnId === col.id) &&
|
||||||
is_foreign_key: keys.some(key =>
|
keyTypeObj?.name?.toLowerCase() === 'primary';
|
||||||
key.keyColumns?.some(kc => kc.columnId === col.id) && key.keyType === 'FOREIGN'
|
}),
|
||||||
),
|
is_foreign_key: keys.some(key => {
|
||||||
|
const keyTypeObj = keyTypes.find(kt => kt.kytp === key.keyType);
|
||||||
|
return key.keyColumns?.some(kc => kc.columnId === col.id) &&
|
||||||
|
keyTypeObj?.name?.toLowerCase() === 'foreign';
|
||||||
|
}),
|
||||||
is_nullable: col.isNullable
|
is_nullable: col.isNullable
|
||||||
})),
|
})),
|
||||||
keys: keys.flatMap(key =>
|
keys: keys.flatMap(key =>
|
||||||
|
|
@ -444,14 +545,44 @@ const AddTableModal = ({
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Define positioning styles based on position prop
|
||||||
|
const getDialogStyles = () => {
|
||||||
|
if (position === 'bottom-right') {
|
||||||
|
return {
|
||||||
|
'& .MuiDialog-container': {
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
padding: '20px',
|
||||||
|
},
|
||||||
|
'& .MuiDialog-paper': {
|
||||||
|
margin: 0,
|
||||||
|
maxWidth: '780px', // Increased by 30% from 600px
|
||||||
|
width: '780px', // Increased by 30% from 600px
|
||||||
|
maxHeight: '80vh',
|
||||||
|
minHeight: '60vh',
|
||||||
|
borderRadius: '12px',
|
||||||
|
boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)',
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
'& .MuiDialog-paper': {
|
||||||
|
minHeight: '80vh',
|
||||||
|
maxHeight: '90vh',
|
||||||
|
maxWidth: '1170px', // Increased by 30% from 900px (lg breakpoint)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
open={open}
|
open={open}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
maxWidth="lg"
|
maxWidth={position === 'bottom-right' ? false : 'lg'}
|
||||||
fullWidth
|
fullWidth={position !== 'bottom-right'}
|
||||||
|
sx={getDialogStyles()}
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
sx: {
|
sx: position === 'bottom-right' ? {} : {
|
||||||
minHeight: '80vh',
|
minHeight: '80vh',
|
||||||
maxHeight: '90vh'
|
maxHeight: '90vh'
|
||||||
}
|
}
|
||||||
|
|
@ -467,7 +598,7 @@ const AddTableModal = ({
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
|
|
||||||
<DialogContent dividers sx={{ p: 3 }}>
|
<DialogContent dividers sx={{ p: position === 'bottom-right' ? 2 : 3 }}>
|
||||||
{errors.submit && (
|
{errors.submit && (
|
||||||
<Alert severity="error" sx={{ mb: 3 }}>
|
<Alert severity="error" sx={{ mb: 3 }}>
|
||||||
{errors.submit}
|
{errors.submit}
|
||||||
|
|
@ -475,7 +606,7 @@ const AddTableModal = ({
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Basic Table Information */}
|
{/* Basic Table Information */}
|
||||||
<Paper elevation={1} sx={{ p: 3, mb: 3 }}>
|
<Paper elevation={1} sx={{ p: position === 'bottom-right' ? 2 : 3, mb: position === 'bottom-right' ? 2 : 3 }}>
|
||||||
<Typography variant="h6" gutterBottom color="primary">
|
<Typography variant="h6" gutterBottom color="primary">
|
||||||
Table Information
|
Table Information
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|
@ -542,7 +673,7 @@ const AddTableModal = ({
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Columns Section */}
|
{/* Columns Section */}
|
||||||
<Paper elevation={1} sx={{ p: 3, mb: 3 }}>
|
<Paper elevation={1} sx={{ p: position === 'bottom-right' ? 2 : 3, mb: position === 'bottom-right' ? 2 : 3 }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||||
<Typography variant="h6" color="primary">
|
<Typography variant="h6" color="primary">
|
||||||
Columns
|
Columns
|
||||||
|
|
@ -587,12 +718,30 @@ const AddTableModal = ({
|
||||||
value={column.type}
|
value={column.type}
|
||||||
onChange={(e) => updateColumn(column.id, 'type', e.target.value)}
|
onChange={(e) => updateColumn(column.id, 'type', e.target.value)}
|
||||||
label="Data Type"
|
label="Data Type"
|
||||||
|
disabled={loadingColumnTypes}
|
||||||
>
|
>
|
||||||
{columnTypes.map(type => (
|
{loadingColumnTypes ? (
|
||||||
|
<MenuItem value="">Loading...</MenuItem>
|
||||||
|
) : columnTypesFromAPI.length > 0 ? (
|
||||||
|
columnTypesFromAPI.map(type => (
|
||||||
|
<MenuItem key={type.cltp} value={type.name} title={type.description}>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="body2" component="div">
|
||||||
|
{type.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" color="text.secondary" component="div">
|
||||||
|
{type.description}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
</MenuItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
columnTypes.map(type => (
|
||||||
<MenuItem key={type} value={type}>
|
<MenuItem key={type} value={type}>
|
||||||
{type}
|
{type}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
))
|
||||||
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
@ -624,7 +773,7 @@ const AddTableModal = ({
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Keys Section */}
|
{/* Keys Section */}
|
||||||
<Paper elevation={1} sx={{ p: 3, mb: 3 }}>
|
<Paper elevation={1} sx={{ p: position === 'bottom-right' ? 2 : 3, mb: position === 'bottom-right' ? 2 : 3 }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||||
<Typography variant="h6" color="primary">
|
<Typography variant="h6" color="primary">
|
||||||
<KeyIcon style={{ marginRight: '8px', verticalAlign: 'middle' }} />
|
<KeyIcon style={{ marginRight: '8px', verticalAlign: 'middle' }} />
|
||||||
|
|
@ -711,10 +860,17 @@ const AddTableModal = ({
|
||||||
updateKey(key.id, 'keyType', e.target.value);
|
updateKey(key.id, 'keyType', e.target.value);
|
||||||
}}
|
}}
|
||||||
onClick={(e) => e.stopPropagation()}
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
disabled={loadingKeyTypes}
|
||||||
>
|
>
|
||||||
<MenuItem value="PRIMARY">Primary Key</MenuItem>
|
{loadingKeyTypes ? (
|
||||||
<MenuItem value="FOREIGN">Foreign Key</MenuItem>
|
<MenuItem value="">Loading...</MenuItem>
|
||||||
<MenuItem value="UNIQUE">Unique Key</MenuItem>
|
) : (
|
||||||
|
keyTypes.map((keyType) => (
|
||||||
|
<MenuItem key={keyType.kytp} value={keyType.kytp}>
|
||||||
|
{keyType.name} Key
|
||||||
|
</MenuItem>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|
@ -854,7 +1010,7 @@ const AddTableModal = ({
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
||||||
{/* Relations Section */}
|
{/* Relations Section */}
|
||||||
<Paper elevation={1} sx={{ p: 3 }}>
|
<Paper elevation={1} sx={{ p: position === 'bottom-right' ? 2 : 3 }}>
|
||||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 2 }}>
|
||||||
<Typography variant="h6" color="primary">
|
<Typography variant="h6" color="primary">
|
||||||
<LinkIcon style={{ marginRight: '8px', verticalAlign: 'middle' }} />
|
<LinkIcon style={{ marginRight: '8px', verticalAlign: 'middle' }} />
|
||||||
|
|
@ -965,7 +1121,7 @@ const AddTableModal = ({
|
||||||
</Paper>
|
</Paper>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
|
||||||
<DialogActions sx={{ p: 3, gap: 1 }}>
|
<DialogActions sx={{ p: position === 'bottom-right' ? 2 : 3, gap: 1 }}>
|
||||||
<Button onClick={onClose} variant="outlined">
|
<Button onClick={onClose} variant="outlined">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
|
|
@ -1755,6 +1755,7 @@ const ERDiagramCanvasContent = () => {
|
||||||
onAddTable={handleAddTable}
|
onAddTable={handleAddTable}
|
||||||
schemas={availableSchemas}
|
schemas={availableSchemas}
|
||||||
existingTables={existingTables}
|
existingTables={existingTables}
|
||||||
|
position="bottom-right"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
// Test script to verify both API calls
|
||||||
|
async function testKeyTypesAPI() {
|
||||||
|
console.log('🔍 Testing Key Types API...');
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://sandbox.kezel.io/api/qbt_table_key_type_list_get', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: "abdhsg",
|
||||||
|
org: "sN05Pjv11qvH"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.status === 200 && data.items) {
|
||||||
|
console.log('✅ Key Types API call successful');
|
||||||
|
console.log('Key types found:', data.items.length);
|
||||||
|
data.items.forEach(item => {
|
||||||
|
console.log(`- ${item.name} (ID: ${item.kytp})`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('❌ Key Types API call failed:', data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error calling Key Types API:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function testColumnTypesAPI() {
|
||||||
|
console.log('\n🔍 Testing Column Types API...');
|
||||||
|
try {
|
||||||
|
const response = await fetch('https://sandbox.kezel.io/api/qbt_column_type_list_get', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
token: "abdhsg",
|
||||||
|
org: "sN05Pjv11qvH"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.status === 200 && data.items) {
|
||||||
|
console.log('✅ Column Types API call successful');
|
||||||
|
console.log('Column types found:', data.items.length);
|
||||||
|
data.items.forEach(item => {
|
||||||
|
console.log(`- ${item.name} (ID: ${item.cltp}) - ${item.description}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('❌ Column Types API call failed:', data.message);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error calling Column Types API:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runTests() {
|
||||||
|
await testKeyTypesAPI();
|
||||||
|
await testColumnTypesAPI();
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests();
|
||||||
Loading…
Reference in New Issue