feat: add MainContent component with SQL editor and interactive table data viewer
This commit is contained in:
@@ -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]}
|
||||||
|
|||||||
Reference in New Issue
Block a user