Refactor admin dashboard to be config-driven with features.json

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-01-08 16:18:04 +00:00
parent ce17c4940d
commit 7a868a2cb7
7 changed files with 1470 additions and 703 deletions

2
package-lock.json generated
View File

@@ -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"

View File

@@ -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"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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<void>;
};
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 (
<Box>
<Typography variant="h5" gutterBottom>
{feature?.name || 'SQL Query Interface'}
</Typography>
{feature?.description && (
<Typography variant="body2" color="text.secondary" gutterBottom>
{feature.description}
</Typography>
)}
<Paper sx={{ p: 2, mt: 2 }}>
<TextField
fullWidth
multiline
rows={6}
label="SQL Query (SELECT only)"
variant="outlined"
value={queryText}
onChange={e => setQueryText(e.target.value)}
placeholder="SELECT * FROM your_table LIMIT 10;"
sx={{ mb: 2 }}
/>
<Button
variant="contained"
onClick={handleExecute}
disabled={loading || !queryText.trim()}
>
{loading ? <CircularProgress size={24} /> : 'Execute Query'}
</Button>
</Paper>
</Box>
);
}

View File

@@ -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 (
<Box>
<Typography variant="h5" gutterBottom>
{feature?.name || 'Database Tables'}
</Typography>
{feature?.description && (
<Typography variant="body2" color="text.secondary" gutterBottom>
{feature.description}
</Typography>
)}
<Paper sx={{ mt: 2, mb: 2 }}>
<List>
{tables.map(table => (
<ListItem key={table.table_name} disablePadding>
<ListItemButton
selected={selectedTable === table.table_name}
onClick={() => onTableClick(table.table_name)}
>
<ListItemIcon>
<StorageIcon />
</ListItemIcon>
<ListItemText primary={table.table_name} />
</ListItemButton>
</ListItem>
))}
{tables.length === 0 && (
<ListItem>
<ListItemText primary="No tables found" />
</ListItem>
)}
</List>
</Paper>
{selectedTable && (
<Typography variant="h6" gutterBottom>
Table: {selectedTable}
</Typography>
)}
</Box>
);
}

View File

@@ -2863,6 +2863,12 @@
"icon": "TableChart",
"featureId": "table-management"
},
{
"id": "column-manager",
"label": "Column Manager",
"icon": "ViewColumn",
"featureId": "column-management"
},
{
"id": "constraints",
"label": "Constraints",