diff --git a/package-lock.json b/package-lock.json index aff7dd5..6a7b13f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -81,7 +81,7 @@ "storybook": "^10.1.11", "tailwindcss": "^4.1.17", "tsx": "^4.21.0", - "typescript": "^5.9.3", + "typescript": "5.9.3", "vite-tsconfig-paths": "^5.1.4", "vitest": "^4.0.15", "vitest-browser-react": "^2.0.2" diff --git a/package.json b/package.json index 1ce9e2b..d67df6e 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "storybook": "^10.1.11", "tailwindcss": "^4.1.17", "tsx": "^4.21.0", - "typescript": "^5.9.3", + "typescript": "5.9.3", "vite-tsconfig-paths": "^5.1.4", "vitest": "^4.0.15", "vitest-browser-react": "^2.0.2" diff --git a/src/app/admin/dashboard/page.tsx b/src/app/admin/dashboard/page.tsx index f246dd6..1f019fc 100644 --- a/src/app/admin/dashboard/page.tsx +++ b/src/app/admin/dashboard/page.tsx @@ -1,11 +1,10 @@ 'use client'; -import AddIcon from '@mui/icons-material/Add'; -import CodeIcon from '@mui/icons-material/Code'; -import DeleteIcon from '@mui/icons-material/Delete'; -import EditIcon from '@mui/icons-material/Edit'; import LogoutIcon from '@mui/icons-material/Logout'; +import AccountTreeIcon from '@mui/icons-material/AccountTree'; +import CodeIcon from '@mui/icons-material/Code'; import RuleIcon from '@mui/icons-material/Rule'; +import SpeedIcon from '@mui/icons-material/Speed'; import StorageIcon from '@mui/icons-material/Storage'; import TableChartIcon from '@mui/icons-material/TableChart'; import ViewColumnIcon from '@mui/icons-material/ViewColumn'; @@ -13,42 +12,50 @@ import { Alert, AppBar, Box, - Button, - Checkbox, CircularProgress, - Dialog, - DialogActions, - DialogContent, - DialogTitle, Drawer, - FormControlLabel, - IconButton, List, ListItem, ListItemButton, ListItemIcon, ListItemText, - MenuItem, Paper, - Select, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, - TextField, Toolbar, Typography, } from '@mui/material'; import { ThemeProvider } from '@mui/material/styles'; import { useRouter } from 'next/navigation'; import { useCallback, useEffect, useState } from 'react'; +import ColumnManagerTab from '@/components/admin/ColumnManagerTab'; import ConstraintManagerTab from '@/components/admin/ConstraintManagerTab'; +import IndexManagerTab from '@/components/admin/IndexManagerTab'; +import QueryBuilderTab from '@/components/admin/QueryBuilderTab'; +import SQLQueryTab from '@/components/admin/SQLQueryTab'; +import TableManagerTab from '@/components/admin/TableManagerTab'; +import TablesTab from '@/components/admin/TablesTab'; +import { getFeatureById, getNavItems } from '@/utils/featureConfig'; import { theme } from '@/utils/theme'; +import Button from '@mui/material/Button'; const DRAWER_WIDTH = 240; +// Icon map for dynamic icon rendering +const iconMap: Record> = { + Storage: StorageIcon, + Code: CodeIcon, + AccountTree: AccountTreeIcon, + TableChart: TableChartIcon, + ViewColumn: ViewColumnIcon, + Rule: RuleIcon, + Speed: SpeedIcon, +}; + type TabPanelProps = { children?: React.ReactNode; index: number; @@ -76,31 +83,13 @@ export default function AdminDashboard() { const [tabValue, setTabValue] = useState(0); const [tables, setTables] = useState([]); const [selectedTable, setSelectedTable] = useState(''); - const [queryText, setQueryText] = useState(''); const [queryResult, setQueryResult] = useState(null); - const [tableSchema, setTableSchema] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [successMessage, setSuccessMessage] = useState(''); - // Table Manager states - const [openCreateTableDialog, setOpenCreateTableDialog] = useState(false); - const [openDropTableDialog, setOpenDropTableDialog] = useState(false); - const [newTableName, setNewTableName] = useState(''); - const [tableColumns, setTableColumns] = useState([{ name: '', type: 'VARCHAR', length: 255, nullable: true, primaryKey: false }]); - const [tableToDelete, setTableToDelete] = useState(''); - - // Column Manager states - const [openAddColumnDialog, setOpenAddColumnDialog] = useState(false); - const [openModifyColumnDialog, setOpenModifyColumnDialog] = useState(false); - const [openDropColumnDialog, setOpenDropColumnDialog] = useState(false); - const [selectedTableForColumn, setSelectedTableForColumn] = useState(''); - const [newColumnName, setNewColumnName] = useState(''); - const [newColumnType, setNewColumnType] = useState('VARCHAR'); - const [newColumnNullable, setNewColumnNullable] = useState(true); - const [newColumnDefault, setNewColumnDefault] = useState(''); - const [columnToModify, setColumnToModify] = useState(''); - const [columnToDelete, setColumnToDelete] = useState(''); + // Get navigation items from features.json + const navItems = getNavItems(); const fetchTables = useCallback(async () => { try { @@ -123,31 +112,6 @@ export default function AdminDashboard() { fetchTables(); }, [fetchTables]); - useEffect(() => { - if (selectedTableForColumn && tabValue === 3) { - // Fetch schema when a table is selected in Column Manager - const fetchSchema = async () => { - try { - const schemaResponse = await fetch('/api/admin/table-schema', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ tableName: selectedTableForColumn }), - }); - - if (schemaResponse.ok) { - const schemaData = await schemaResponse.json(); - setTableSchema(schemaData); - } - } catch (err) { - console.error('Failed to fetch schema:', err); - } - }; - fetchSchema(); - } - }, [selectedTableForColumn, tabValue]); - const handleTableClick = async (tableName: string) => { setSelectedTable(tableName); setLoading(true); @@ -156,7 +120,6 @@ export default function AdminDashboard() { setQueryResult(null); try { - // Fetch table data const dataResponse = await fetch('/api/admin/table-data', { method: 'POST', headers: { @@ -172,20 +135,6 @@ export default function AdminDashboard() { const data = await dataResponse.json(); setQueryResult(data); - - // Fetch table schema - const schemaResponse = await fetch('/api/admin/table-schema', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ tableName }), - }); - - if (schemaResponse.ok) { - const schemaData = await schemaResponse.json(); - setTableSchema(schemaData); - } } catch (err: any) { setError(err.message); } finally { @@ -193,12 +142,7 @@ export default function AdminDashboard() { } }; - const handleQuerySubmit = async () => { - if (!queryText.trim()) { - setError('Please enter a query'); - return; - } - + const handleExecuteQuery = async (query: string) => { setLoading(true); setError(''); setQueryResult(null); @@ -209,7 +153,7 @@ export default function AdminDashboard() { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ query: queryText }), + body: JSON.stringify({ query }), }); const data = await response.json(); @@ -219,6 +163,7 @@ export default function AdminDashboard() { } setQueryResult(data); + setSuccessMessage('Query executed successfully'); } catch (err: any) { setError(err.message); } finally { @@ -239,17 +184,7 @@ export default function AdminDashboard() { }; // Table Management Handlers - const handleCreateTable = async () => { - if (!newTableName.trim()) { - setError('Table name is required'); - return; - } - - if (tableColumns.length === 0 || !tableColumns[0].name) { - setError('At least one column is required'); - return; - } - + const handleCreateTable = async (tableName: string, columns: any[]) => { setLoading(true); setError(''); setSuccessMessage(''); @@ -261,8 +196,8 @@ export default function AdminDashboard() { 'Content-Type': 'application/json', }, body: JSON.stringify({ - tableName: newTableName, - columns: tableColumns.filter(col => col.name.trim()), + tableName, + columns, }), }); @@ -273,23 +208,16 @@ export default function AdminDashboard() { } setSuccessMessage(data.message); - setOpenCreateTableDialog(false); - setNewTableName(''); - setTableColumns([{ name: '', type: 'VARCHAR', length: 255, nullable: true, primaryKey: false }]); await fetchTables(); } catch (err: any) { setError(err.message); + throw err; } finally { setLoading(false); } }; - const handleDropTable = async () => { - if (!tableToDelete) { - setError('Please select a table to drop'); - return; - } - + const handleDropTable = async (tableName: string) => { setLoading(true); setError(''); setSuccessMessage(''); @@ -300,7 +228,7 @@ export default function AdminDashboard() { headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ tableName: tableToDelete }), + body: JSON.stringify({ tableName }), }); const data = await response.json(); @@ -310,97 +238,53 @@ export default function AdminDashboard() { } setSuccessMessage(data.message); - setOpenDropTableDialog(false); - setTableToDelete(''); - if (selectedTable === tableToDelete) { + if (selectedTable === tableName) { setSelectedTable(''); setQueryResult(null); } await fetchTables(); } catch (err: any) { setError(err.message); + throw err; } finally { setLoading(false); } }; - const addColumnToTable = () => { - setTableColumns([...tableColumns, { name: '', type: 'VARCHAR', length: 255, nullable: true, primaryKey: false }]); - }; - - const updateColumnField = (index: number, field: string, value: any) => { - const updated = [...tableColumns]; - updated[index] = { ...updated[index], [field]: value }; - setTableColumns(updated); - }; - - const removeColumn = (index: number) => { - if (tableColumns.length > 1) { - setTableColumns(tableColumns.filter((_, i) => i !== index)); - } - }; - // Column Management Handlers - const handleAddColumn = async () => { - if (!selectedTableForColumn || !newColumnName.trim() || !newColumnType) { - setError('Table name, column name, and data type are required'); - return; - } - + const handleAddColumn = async (tableName: string, data: any) => { setLoading(true); setError(''); setSuccessMessage(''); try { - const payload: any = { - tableName: selectedTableForColumn, - columnName: newColumnName, - dataType: newColumnType, - nullable: newColumnNullable, - }; - - if (newColumnDefault) { - payload.defaultValue = newColumnDefault; - } - const response = await fetch('/api/admin/column-manage', { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify(payload), + body: JSON.stringify({ + tableName, + ...data, + }), }); - const data = await response.json(); + const result = await response.json(); if (!response.ok) { - throw new Error(data.error || 'Failed to add column'); + throw new Error(result.error || 'Failed to add column'); } - setSuccessMessage(data.message); - setOpenAddColumnDialog(false); - setNewColumnName(''); - setNewColumnType('VARCHAR'); - setNewColumnNullable(true); - setNewColumnDefault(''); - - // Refresh table schema if viewing the modified table - if (selectedTable === selectedTableForColumn) { - await handleTableClick(selectedTableForColumn); - } + setSuccessMessage(result.message); } catch (err: any) { setError(err.message); + throw err; } finally { setLoading(false); } }; - const handleModifyColumn = async () => { - if (!selectedTableForColumn || !columnToModify) { - setError('Table name and column name are required'); - return; - } - + const handleModifyColumn = async (tableName: string, data: any) => { setLoading(true); setError(''); setSuccessMessage(''); @@ -412,42 +296,27 @@ export default function AdminDashboard() { 'Content-Type': 'application/json', }, body: JSON.stringify({ - tableName: selectedTableForColumn, - columnName: columnToModify, - newType: newColumnType, - nullable: newColumnNullable, + tableName, + ...data, }), }); - const data = await response.json(); + const result = await response.json(); if (!response.ok) { - throw new Error(data.error || 'Failed to modify column'); + throw new Error(result.error || 'Failed to modify column'); } - setSuccessMessage(data.message); - setOpenModifyColumnDialog(false); - setColumnToModify(''); - setNewColumnType('VARCHAR'); - setNewColumnNullable(true); - - // Refresh table schema if viewing the modified table - if (selectedTable === selectedTableForColumn) { - await handleTableClick(selectedTableForColumn); - } + setSuccessMessage(result.message); } catch (err: any) { setError(err.message); + throw err; } finally { setLoading(false); } }; - const handleDropColumn = async () => { - if (!selectedTableForColumn || !columnToDelete) { - setError('Table name and column name are required'); - return; - } - + const handleDropColumn = async (tableName: string, data: any) => { setLoading(true); setError(''); setSuccessMessage(''); @@ -459,27 +328,21 @@ export default function AdminDashboard() { 'Content-Type': 'application/json', }, body: JSON.stringify({ - tableName: selectedTableForColumn, - columnName: columnToDelete, + tableName, + ...data, }), }); - const data = await response.json(); + const result = await response.json(); if (!response.ok) { - throw new Error(data.error || 'Failed to drop column'); + throw new Error(result.error || 'Failed to drop column'); } - setSuccessMessage(data.message); - setOpenDropColumnDialog(false); - setColumnToDelete(''); - - // Refresh table schema if viewing the modified table - if (selectedTable === selectedTableForColumn) { - await handleTableClick(selectedTableForColumn); - } + setSuccessMessage(result.message); } catch (err: any) { setError(err.message); + throw err; } finally { setLoading(false); } @@ -550,6 +413,41 @@ export default function AdminDashboard() { } }; + // Query Builder Handler + const handleExecuteBuiltQuery = async (params: any) => { + setLoading(true); + setError(''); + + try { + const response = await fetch('/api/admin/query-builder', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(params), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.error || 'Query failed'); + } + + return data; + } catch (err: any) { + setError(err.message); + throw err; + } finally { + setLoading(false); + } + }; + + // Get icon component for navigation item + const getIconComponent = (iconName: string) => { + const IconComponent = iconMap[iconName]; + return IconComponent ? : ; + }; + return ( @@ -582,46 +480,19 @@ export default function AdminDashboard() { - - setTabValue(0)}> - - - - - - - - setTabValue(1)}> - - - - - - - - setTabValue(2)}> - - - - - - - - setTabValue(3)}> - - - - - - - - setTabValue(4)}> - - - - - - + {navItems.map((item, index) => ( + + setTabValue(index)} + > + + {getIconComponent(item.icon)} + + + + + ))} @@ -636,207 +507,55 @@ export default function AdminDashboard() { > - - - Database Tables - - - - - {tables.map(table => ( - - handleTableClick(table.table_name)}> - - - - - - - ))} - - - - {selectedTable && ( - - Table: - {' '} - {selectedTable} - - )} - - - - - SQL Query Interface - - - - setQueryText(e.target.value)} - placeholder="SELECT * FROM your_table LIMIT 10;" - sx={{ mb: 2 }} - /> - - - - - - - Table Manager - - - - - - - - - - - Existing Tables - - - {tables.map(table => ( - - - - - - - ))} - {tables.length === 0 && ( - - - - )} - - - - - - - - Column Manager - - - - - Select a table to manage its columns: - - - - - {selectedTableForColumn && ( - <> - - - - - - - {tableSchema && ( - - - - Current Columns for {selectedTableForColumn} - - - - - - Column Name - Data Type - Nullable - Default - - - - {tableSchema.columns?.map((col: any) => ( - - {col.column_name} - {col.data_type} - {col.is_nullable} - {col.column_default || 'NULL'} - - ))} - -
-
-
-
- )} - - )} -
- - - - + {/* Render tabs dynamically based on navItems */} + {navItems.map((item, index) => ( + + {item.id === 'tables' && ( + + )} + {item.id === 'query' && ( + + )} + {item.id === 'query-builder' && ( + + )} + {item.id === 'table-manager' && ( + + )} + {item.id === 'column-manager' && ( + + )} + {item.id === 'constraints' && ( + + )} + {item.id === 'indexes' && ( + + )} + + ))} {successMessage && ( setSuccessMessage('')}> @@ -845,7 +564,7 @@ export default function AdminDashboard() { )} {error && ( - + setError('')}> {error} )} @@ -860,9 +579,7 @@ export default function AdminDashboard() { - Rows returned: - {' '} - {queryResult.rowCount} + Rows returned: {queryResult.rowCount} @@ -894,267 +611,6 @@ export default function AdminDashboard() { )}
- - {/* Create Table Dialog */} - setOpenCreateTableDialog(false)} maxWidth="md" fullWidth> - Create New Table - - setNewTableName(e.target.value)} - sx={{ mt: 2, mb: 2 }} - /> - - Columns: - - {tableColumns.map((col, index) => ( - - updateColumnField(index, 'name', e.target.value)} - sx={{ mr: 1, mb: 1 }} - /> - - {(col.type === 'VARCHAR') && ( - updateColumnField(index, 'length', e.target.value)} - sx={{ mr: 1, mb: 1, width: 100 }} - /> - )} - updateColumnField(index, 'nullable', e.target.checked)} - /> - } - label="Nullable" - sx={{ mr: 1 }} - /> - updateColumnField(index, 'primaryKey', e.target.checked)} - /> - } - label="Primary Key" - sx={{ mr: 1 }} - /> - {tableColumns.length > 1 && ( - removeColumn(index)} color="error" size="small"> - - - )} - - ))} - - - - - - - - - {/* Drop Table Dialog */} - setOpenDropTableDialog(false)}> - Drop Table - - - Warning: This will permanently delete the table and all its data! - - - - - - - - - - {/* Add Column Dialog */} - setOpenAddColumnDialog(false)}> - Add Column to {selectedTableForColumn} - - setNewColumnName(e.target.value)} - sx={{ mt: 2, mb: 2 }} - /> - - setNewColumnNullable(e.target.checked)} - /> - } - label="Nullable" - sx={{ mb: 2 }} - /> - setNewColumnDefault(e.target.value)} - /> - - - - - - - - {/* Modify Column Dialog */} - setOpenModifyColumnDialog(false)}> - Modify Column in {selectedTableForColumn} - - - {columnToModify && ( - <> - - setNewColumnNullable(e.target.checked)} - /> - } - label="Nullable" - /> - - )} - - - - - - - - {/* Drop Column Dialog */} - setOpenDropColumnDialog(false)}> - Drop Column from {selectedTableForColumn} - - - Warning: This will permanently delete the column and all its data! - - - - - - - -
); diff --git a/src/app/admin/dashboard/page.tsx.backup b/src/app/admin/dashboard/page.tsx.backup deleted file mode 100644 index 275d9ea..0000000 --- a/src/app/admin/dashboard/page.tsx.backup +++ /dev/null @@ -1,369 +0,0 @@ -'use client'; - -import AddIcon from '@mui/icons-material/Add'; -import CodeIcon from '@mui/icons-material/Code'; -import DeleteIcon from '@mui/icons-material/Delete'; -import EditIcon from '@mui/icons-material/Edit'; -import LogoutIcon from '@mui/icons-material/Logout'; -import StorageIcon from '@mui/icons-material/Storage'; -import TableChartIcon from '@mui/icons-material/TableChart'; -import { - Alert, - AppBar, - Box, - Button, - CircularProgress, - Dialog, - DialogActions, - DialogContent, - DialogTitle, - Drawer, - IconButton, - List, - ListItem, - ListItemButton, - ListItemIcon, - ListItemText, - Paper, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - TextField, - Toolbar, - Typography, -} from '@mui/material'; -import { ThemeProvider } from '@mui/material/styles'; -import { useRouter } from 'next/navigation'; -import { useCallback, useEffect, useState } from 'react'; -import { theme } from '@/utils/theme'; - -const DRAWER_WIDTH = 240; - -type TabPanelProps = { - children?: React.ReactNode; - index: number; - value: number; -}; - -function TabPanel(props: TabPanelProps) { - const { children, value, index, ...other } = props; - - return ( - - ); -} - -export default function AdminDashboard() { - const router = useRouter(); - const [tabValue, setTabValue] = useState(0); - const [tables, setTables] = useState([]); - const [selectedTable, setSelectedTable] = useState(''); - const [queryText, setQueryText] = useState(''); - const [queryResult, setQueryResult] = useState(null); - const [tableSchema, setTableSchema] = useState(null); - const [loading, setLoading] = useState(false); - const [error, setError] = useState(''); - const [successMessage, setSuccessMessage] = useState(''); - - // Dialog states - const [openCreateDialog, setOpenCreateDialog] = useState(false); - const [openEditDialog, setOpenEditDialog] = useState(false); - const [openDeleteDialog, setOpenDeleteDialog] = useState(false); - const [editingRecord, setEditingRecord] = useState(null); - const [deletingRecord, setDeletingRecord] = useState(null); - const [formData, setFormData] = useState({}); - - const fetchTables = useCallback(async () => { - try { - const response = await fetch('/api/admin/tables'); - if (!response.ok) { - if (response.status === 401) { - router.push('/admin/login'); - return; - } - throw new Error('Failed to fetch tables'); - } - const data = await response.json(); - setTables(data.tables); - } catch (err: any) { - setError(err.message); - } - }, [router]); - - useEffect(() => { - fetchTables(); - }, [fetchTables]); - - const handleTableClick = async (tableName: string) => { - setSelectedTable(tableName); - setLoading(true); - setError(''); - setSuccessMessage(''); - setQueryResult(null); - - try { - // Fetch table data - const dataResponse = await fetch('/api/admin/table-data', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ tableName }), - }); - - if (!response.ok) { - const data = await dataResponse.json(); - throw new Error(data.error || 'Query failed'); - } - - const data = await dataResponse.json(); - setQueryResult(data); - - // Fetch table schema - const schemaResponse = await fetch('/api/admin/table-schema', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ tableName }), - }); - - if (schemaResponse.ok) { - const schemaData = await schemaResponse.json(); - setTableSchema(schemaData); - } - } catch (err: any) { - setError(err.message); - } finally { - setLoading(false); - } - }; - - const handleQuerySubmit = async () => { - if (!queryText.trim()) { - setError('Please enter a query'); - return; - } - - setLoading(true); - setError(''); - setQueryResult(null); - - try { - const response = await fetch('/api/admin/query', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ query: queryText }), - }); - - const data = await response.json(); - - if (!response.ok) { - throw new Error(data.error || 'Query failed'); - } - - setQueryResult(data); - } catch (err: any) { - setError(err.message); - } finally { - setLoading(false); - } - }; - - const handleLogout = async () => { - try { - await fetch('/api/admin/logout', { - method: 'POST', - }); - router.push('/admin/login'); - router.refresh(); - } catch (err) { - console.error('Logout error:', err); - } - }; - - return ( - - - theme.zIndex.drawer + 1 }} - > - - - - Postgres Admin Panel - - - - - - - - - - - setTabValue(0)}> - - - - - - - - setTabValue(1)}> - - - - - - - - - - - - - - - - Database Tables - - - - - {tables.map(table => ( - - handleTableClick(table.table_name)}> - - - - - - - ))} - - - - {selectedTable && ( - - Table: - {' '} - {selectedTable} - - )} - - - - - SQL Query Interface - - - - setQueryText(e.target.value)} - placeholder="SELECT * FROM your_table LIMIT 10;" - sx={{ mb: 2 }} - /> - - - - - {error && ( - - {error} - - )} - - {loading && ( - - - - )} - - {queryResult && !loading && ( - - - - Rows returned: - {' '} - {queryResult.rowCount} - - - - - - - {queryResult.fields?.map((field: any) => ( - - {field.name} - - ))} - - - - {queryResult.rows?.map((row: any, idx: number) => ( - - {queryResult.fields?.map((field: any) => ( - - {row[field.name] !== null - ? String(row[field.name]) - : 'NULL'} - - ))} - - ))} - -
-
-
- )} -
-
-
- ); -} diff --git a/src/components/admin/SQLQueryTab.tsx b/src/components/admin/SQLQueryTab.tsx new file mode 100644 index 0000000..26c0ad9 --- /dev/null +++ b/src/components/admin/SQLQueryTab.tsx @@ -0,0 +1,72 @@ +'use client'; + +import { + Box, + Button, + CircularProgress, + Paper, + TextField, + Typography, +} from '@mui/material'; +import { useState } from 'react'; +import { getFeatureById } from '@/utils/featureConfig'; + +type SQLQueryTabProps = { + onExecuteQuery: (query: string) => Promise; +}; + +export default function SQLQueryTab({ onExecuteQuery }: SQLQueryTabProps) { + const [queryText, setQueryText] = useState(''); + const [loading, setLoading] = useState(false); + + // Get feature configuration from JSON + const feature = getFeatureById('sql-query'); + + const handleExecute = async () => { + if (!queryText.trim()) { + return; + } + + setLoading(true); + try { + await onExecuteQuery(queryText); + } finally { + setLoading(false); + } + }; + + return ( + + + {feature?.name || 'SQL Query Interface'} + + + {feature?.description && ( + + {feature.description} + + )} + + + setQueryText(e.target.value)} + placeholder="SELECT * FROM your_table LIMIT 10;" + sx={{ mb: 2 }} + /> + + + + ); +} diff --git a/src/components/admin/TablesTab.tsx b/src/components/admin/TablesTab.tsx new file mode 100644 index 0000000..9396b54 --- /dev/null +++ b/src/components/admin/TablesTab.tsx @@ -0,0 +1,72 @@ +'use client'; + +import StorageIcon from '@mui/icons-material/Storage'; +import { + Box, + List, + ListItem, + ListItemButton, + ListItemIcon, + ListItemText, + Paper, + Typography, +} from '@mui/material'; +import { getFeatureById } from '@/utils/featureConfig'; + +type TablesTabProps = { + tables: Array<{ table_name: string }>; + selectedTable: string; + onTableClick: (tableName: string) => void; +}; + +export default function TablesTab({ + tables, + selectedTable, + onTableClick, +}: TablesTabProps) { + // Get feature configuration from JSON + const feature = getFeatureById('database-crud'); + + return ( + + + {feature?.name || 'Database Tables'} + + + {feature?.description && ( + + {feature.description} + + )} + + + + {tables.map(table => ( + + onTableClick(table.table_name)} + > + + + + + + + ))} + {tables.length === 0 && ( + + + + )} + + + + {selectedTable && ( + + Table: {selectedTable} + + )} + + ); +} diff --git a/src/config/features.json b/src/config/features.json index 4d1d1d4..10f8928 100644 --- a/src/config/features.json +++ b/src/config/features.json @@ -2863,6 +2863,12 @@ "icon": "TableChart", "featureId": "table-management" }, + { + "id": "column-manager", + "label": "Column Manager", + "icon": "ViewColumn", + "featureId": "column-management" + }, { "id": "constraints", "label": "Constraints",