feat: add MainContent component with SQL editor and interactive table data viewer

This commit is contained in:
Ümit Tunç
2026-04-24 08:15:54 +03:00
parent d0d0f193a1
commit 43d864f2bd
+21 -10
View File
@@ -15,6 +15,7 @@ const MainContent: React.FC = () => {
const [loadingSchema, setLoadingSchema] = useState(false); const [loadingSchema, setLoadingSchema] = useState(false);
const [loadingData, setLoadingData] = useState(false); const [loadingData, setLoadingData] = useState(false);
const [sqlQuery, setSqlQuery] = useState(''); const [sqlQuery, setSqlQuery] = useState('');
const [isCustomQuery, setIsCustomQuery] = useState(false);
const [paginationModel, setPaginationModel] = useState<GridPaginationModel>({ const [paginationModel, setPaginationModel] = useState<GridPaginationModel>({
page: 0, page: 0,
pageSize: 100, pageSize: 100,
@@ -34,13 +35,14 @@ const MainContent: React.FC = () => {
useEffect(() => { useEffect(() => {
if (activeTable && activeDatabase) { if (activeTable && activeDatabase) {
setSqlQuery(`SELECT * FROM ${activeDatabase}.${activeTable} LIMIT 1000;`); setSqlQuery(`SELECT * FROM ${activeDatabase}.${activeTable} LIMIT 1000;`);
setIsCustomQuery(false); // Reset to table mode when a new table is selected
} }
}, [activeTable, activeDatabase]); }, [activeTable, activeDatabase]);
// Fetch Schema // Fetch Schema
useEffect(() => { useEffect(() => {
const fetchSchema = async () => { const fetchSchema = async () => {
if (!activeTable || !activeDatabase) return; if (!activeTable || !activeDatabase || isCustomQuery) return;
setLoadingSchema(true); setLoadingSchema(true);
try { try {
@@ -71,11 +73,11 @@ const MainContent: React.FC = () => {
}; };
fetchSchema(); fetchSchema();
}, [activeTable, activeDatabase]); }, [activeTable, activeDatabase, isCustomQuery]);
// Fetch Data // Fetch Data
const fetchData = useCallback(async () => { const fetchData = useCallback(async () => {
if (!activeTable || !activeDatabase) return; if (!activeTable || !activeDatabase || isCustomQuery) return;
setLoadingData(true); setLoadingData(true);
try { try {
@@ -100,7 +102,7 @@ const MainContent: React.FC = () => {
} finally { } finally {
setLoadingData(false); setLoadingData(false);
} }
}, [activeTable, activeDatabase, paginationModel]); }, [activeTable, activeDatabase, paginationModel, isCustomQuery]);
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
@@ -109,12 +111,12 @@ const MainContent: React.FC = () => {
const handleExecute = async () => { const handleExecute = async () => {
if (!sqlQuery) return; if (!sqlQuery) return;
setLoadingData(true); setLoadingData(true);
setIsCustomQuery(true); // Switch to custom query mode
try { try {
const response = await SchemaService.executeQuery(sqlQuery); const response = await SchemaService.executeQuery(sqlQuery);
const rawData = response.data.data; const rawData = response.data.data;
if (rawData && rawData.length > 0) { if (rawData && rawData.length > 0) {
// Generate dynamic columns from the result set
const fields = Object.keys(rawData[0]); const fields = Object.keys(rawData[0]);
const newCols: GridColDef[] = fields.map(field => ({ const newCols: GridColDef[] = fields.map(field => ({
field, field,
@@ -136,7 +138,6 @@ const MainContent: React.FC = () => {
} }
} catch (error: any) { } catch (error: any) {
console.error('Execution error', error); console.error('Execution error', error);
// In a real app, we'd use a nicer toast or error panel
alert(error.response?.data?.error || 'Execution failed'); alert(error.response?.data?.error || 'Execution failed');
} finally { } finally {
setLoadingData(false); setLoadingData(false);
@@ -155,6 +156,7 @@ const MainContent: React.FC = () => {
<Box sx={{ flexGrow: 1, p: 3, bgcolor: 'background.default', display: 'flex', flexDirection: 'column', width: '100%', minWidth: 0, overflow: 'hidden', gap: 2 }}> <Box sx={{ flexGrow: 1, p: 3, bgcolor: 'background.default', display: 'flex', flexDirection: 'column', width: '100%', minWidth: 0, overflow: 'hidden', gap: 2 }}>
{/* SQL Editor Section */} {/* SQL Editor Section */}
<Paper elevation={0} sx={{ <Paper elevation={0} sx={{
flexShrink: 0, // Prevent editor from collapsing
borderRadius: 2, borderRadius: 2,
overflow: 'hidden', overflow: 'hidden',
border: 1, border: 1,
@@ -166,7 +168,9 @@ const MainContent: React.FC = () => {
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 700, opacity: 0.7 }}>SQL EDITOR</Typography> <Typography variant="subtitle2" sx={{ fontWeight: 700, opacity: 0.7 }}>SQL EDITOR</Typography>
<Divider orientation="vertical" flexItem sx={{ height: 16, my: 'auto' }} /> <Divider orientation="vertical" flexItem sx={{ height: 16, my: 'auto' }} />
<Typography variant="caption" sx={{ opacity: 0.5 }}>{activeDatabase}.sql</Typography> <Typography variant="caption" sx={{ opacity: 0.5 }}>
{isCustomQuery ? 'Custom Query' : `${activeDatabase}.${activeTable}`}
</Typography>
</Box> </Box>
<Box sx={{ display: 'flex', gap: 1 }}> <Box sx={{ display: 'flex', gap: 1 }}>
<Tooltip title="Clear Editor"> <Tooltip title="Clear Editor">
@@ -212,13 +216,20 @@ const MainContent: React.FC = () => {
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', minWidth: 0, overflow: 'hidden' }}> <Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', minWidth: 0, overflow: 'hidden' }}>
<Box sx={{ mb: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <Box sx={{ mb: 1, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Typography variant="h6" sx={{ fontWeight: 700 }}>Results</Typography> <Typography variant="h6" sx={{ fontWeight: 700 }}>
{isCustomQuery ? 'Query Results' : 'Table Results'}
</Typography>
<Typography variant="caption" sx={{ opacity: 0.5, mt: 0.5 }}>{rowCount} rows found</Typography> <Typography variant="caption" sx={{ opacity: 0.5, mt: 0.5 }}>{rowCount} rows found</Typography>
</Box> </Box>
{isCustomQuery && (
<Button size="small" onClick={() => setIsCustomQuery(false)} sx={{ textTransform: 'none' }}>
Back to Table View
</Button>
)}
</Box> </Box>
<Paper sx={{ flexGrow: 1, borderRadius: 2, overflow: 'hidden', display: 'flex', flexDirection: 'column', width: '100%', border: 1, borderColor: 'divider' }}> <Paper sx={{ flexGrow: 1, borderRadius: 2, overflow: 'hidden', display: 'flex', flexDirection: 'column', width: '100%', border: 1, borderColor: 'divider' }}>
{loadingSchema ? ( {(loadingSchema && !isCustomQuery) ? (
<Box sx={{ display: 'flex', height: '100%', alignItems: 'center', justifyContent: 'center' }}> <Box sx={{ display: 'flex', height: '100%', alignItems: 'center', justifyContent: 'center' }}>
<CircularProgress /> <CircularProgress />
</Box> </Box>
@@ -228,7 +239,7 @@ const MainContent: React.FC = () => {
columns={columns} columns={columns}
rowCount={rowCount} rowCount={rowCount}
loading={loadingData} loading={loadingData}
paginationMode="server" paginationMode={isCustomQuery ? 'client' : 'server'} // Client pagination for SQL results
paginationModel={paginationModel} paginationModel={paginationModel}
onPaginationModelChange={setPaginationModel} onPaginationModelChange={setPaginationModel}
pageSizeOptions={[25, 50, 100]} pageSizeOptions={[25, 50, 100]}