import React, { useEffect, useState, useCallback } from 'react'; import { Box, Paper, Typography, CircularProgress, Button, Divider, IconButton, Tooltip, Snackbar, Alert, AlertTitle } from '@mui/material'; import { PlayArrow, History, Save, CleaningServices, Close } from '@mui/icons-material'; import { DataGrid } from '@mui/x-data-grid'; import type { GridColDef, GridPaginationModel } from '@mui/x-data-grid'; import Editor from '@monaco-editor/react'; import { useAppStore } from '../store/useAppStore'; import { SchemaService } from '../services/api'; const MainContent: React.FC = () => { const { activeTable, activeDatabase, darkMode } = useAppStore(); const [columns, setColumns] = useState([]); const [rows, setRows] = useState([]); const [rowCount, setRowCount] = useState(0); const [loadingSchema, setLoadingSchema] = useState(false); const [loadingData, setLoadingData] = useState(false); const [sqlQuery, setSqlQuery] = useState(''); const [isCustomQuery, setIsCustomQuery] = useState(false); const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 100, }); // Custom Alert State const [errorInfo, setErrorInfo] = useState<{ open: boolean; message: string; title: string }>({ open: false, message: '', title: '' }); const handleCloseError = () => setErrorInfo({ ...errorInfo, open: false }); // Helper to map SQL types to MUI X Data Grid types const mapSqlTypeToMuiType = (sqlType: string): 'string' | 'number' | 'date' | 'dateTime' | 'boolean' => { sqlType = sqlType.toLowerCase(); if (sqlType.includes('int') || sqlType.includes('decimal') || sqlType.includes('float') || sqlType.includes('double')) return 'number'; if (sqlType.includes('datetime') || sqlType.includes('timestamp')) return 'dateTime'; if (sqlType.includes('date')) return 'date'; if (sqlType.includes('bool') || sqlType.includes('tinyint(1)')) return 'boolean'; return 'string'; }; // Set initial SQL query when table changes useEffect(() => { if (activeTable && activeDatabase) { setSqlQuery(`SELECT * FROM ${activeDatabase}.${activeTable} LIMIT 1000;`); setIsCustomQuery(false); } }, [activeTable, activeDatabase]); // Fetch Schema useEffect(() => { const fetchSchema = async () => { if (!activeTable || !activeDatabase || isCustomQuery) return; setLoadingSchema(true); try { const schemaRes = await SchemaService.getTableSchema(activeTable, activeDatabase); const cols: GridColDef[] = schemaRes.data.map((col: any) => { const type = mapSqlTypeToMuiType(col.Type); return { field: col.Field, headerName: col.Field, type: type, width: 200, minWidth: 100, valueGetter: (value: any) => { if ((type === 'date' || type === 'dateTime') && value && typeof value === 'string') { return new Date(value); } return value; }, }; }); setColumns(cols); setPaginationModel({ page: 0, pageSize: 100 }); } catch (error) { console.error('Failed to fetch table schema', error); } finally { setLoadingSchema(false); } }; fetchSchema(); }, [activeTable, activeDatabase, isCustomQuery]); // Fetch Data const fetchData = useCallback(async () => { if (!activeTable || !activeDatabase || isCustomQuery) return; setLoadingData(true); try { const params = { skip: paginationModel.page * paginationModel.pageSize, take: paginationModel.pageSize, requireTotalCount: true, database: activeDatabase, }; const response = await SchemaService.getTableData(activeTable, params); const dataWithIds = response.data.data.map((row: any, index: number) => ({ id: row.id || row.ID || `row-${index}-${paginationModel.page}`, ...row, })); setRows(dataWithIds); setRowCount(response.data.totalCount || 0); } catch (error) { console.error('Data loading error', error); } finally { setLoadingData(false); } }, [activeTable, activeDatabase, paginationModel, isCustomQuery]); useEffect(() => { fetchData(); }, [fetchData]); const handleExecute = async () => { if (!sqlQuery) return; setLoadingData(true); setIsCustomQuery(true); try { const response = await SchemaService.executeQuery(sqlQuery); const rawData = response.data.data; if (rawData && rawData.length > 0) { const fields = Object.keys(rawData[0]); const newCols: GridColDef[] = fields.map(field => ({ field, headerName: field, width: 150, flex: fields.length < 6 ? 1 : 0 })); setColumns(newCols); const dataWithIds = rawData.map((row: any, index: number) => ({ id: row.id || row.ID || `row-${index}`, ...row, })); setRows(dataWithIds); setRowCount(response.data.count); } else { setRows([]); setRowCount(0); } } catch (error: any) { console.error('Execution error', error); const msg = error.response?.data?.error || 'An unexpected error occurred during SQL execution.'; setErrorInfo({ open: true, title: 'SQL Execution Error', message: msg }); } finally { setLoadingData(false); } }; if (!activeTable) { return ( Select a table to view data ); } return ( {/* SQL Editor Section */} SQL EDITOR {isCustomQuery ? 'Custom Query' : `${activeDatabase}.${activeTable}`} setSqlQuery('')}> setSqlQuery(value || '')} options={{ minimap: { enabled: false }, fontSize: 14, fontFamily: "'Fira Code', 'Cascadia Code', Consolas, monospace", lineNumbers: 'on', scrollBeyondLastLine: false, automaticLayout: true, padding: { top: 16, bottom: 16 }, cursorSmoothCaretAnimation: 'on', smoothScrolling: true }} /> {/* Data Section */} {isCustomQuery ? 'Query Results' : 'Table Results'} {rowCount} rows found {isCustomQuery && ( )} {(loadingSchema && !isCustomQuery) ? ( ) : ( theme.palette.mode === 'light' ? 'rgba(0, 0, 0, 0.02)' : 'rgba(255, 255, 255, 0.02)', }, '& .MuiDataGrid-row:hover': { bgcolor: (theme) => theme.palette.mode === 'light' ? 'rgba(0, 97, 255, 0.04)' : 'rgba(0, 97, 255, 0.08)', }, '& .MuiDataGrid-columnHeaderTitle': { fontWeight: 700, opacity: 0.8 } }} slotProps={{ loadingOverlay: { variant: 'linear-progress', noRowsVariant: 'linear-progress', } }} /> )} {/* Custom Alert (Snackbar) */} } > {errorInfo.title} {errorInfo.message} ); }; export default MainContent;