feat: implement database export/import functionality and table management UI components

This commit is contained in:
Ümit Tunç
2026-04-24 13:33:02 +03:00
parent 6594f639c3
commit 5192d950f0
3 changed files with 198 additions and 178 deletions
@@ -170,7 +170,7 @@ const DatabaseTablesGrid: React.FC<DatabaseTablesGridProps> = ({ database, setNo
)} )}
<Paper sx={{ <Paper sx={{
flexGrow: 1, flexGrow: 1,
borderRadius: 3, borderRadius: 1,
overflow: 'hidden', overflow: 'hidden',
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
+36 -19
View File
@@ -5,7 +5,13 @@ import {
Typography, Typography,
Button, Button,
CircularProgress, CircularProgress,
Alert Alert,
Table,
TableBody,
TableCell,
TableContainer,
TableRow,
Avatar
} from '@mui/material'; } from '@mui/material';
import { import {
Terminal, Terminal,
@@ -120,28 +126,39 @@ const TechnicalOverview: React.FC<TechnicalOverviewProps> = ({ database, table,
confirmLabel="Truncate Now" confirmLabel="Truncate Now"
loading={truncating} loading={truncating}
/> />
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(240px, 1fr))', gap: 3 }}> <TableContainer component={Paper} sx={{
{stats.map((stat, i) => ( borderRadius: 1,
<Paper key={i} sx={{
p: 3,
borderRadius: 4,
border: 1, border: 1,
borderColor: 'divider', borderColor: 'divider',
display: 'flex', bgcolor: 'background.paper',
flexDirection: 'column', overflow: 'hidden',
gap: 1, boxShadow: 'none'
bgcolor: (theme) => theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.01)',
transition: 'all 0.2s',
'&:hover': { borderColor: 'primary.main', transform: 'translateY(-2px)' }
}}> }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> <Table>
<Typography variant="caption" sx={{ fontWeight: 700, color: 'text.secondary', textTransform: 'uppercase', letterSpacing: 1 }}>{stat.label}</Typography> <TableBody>
<Box sx={{ color: 'primary.main', opacity: 0.5 }}>{stat.icon}</Box> {stats.map((stat, i) => (
</Box> <TableRow key={i} sx={{ '&:last-child td, &:last-child th': { border: 0 }, '&:hover': { bgcolor: 'rgba(255,255,255,0.02)' } }}>
<Typography variant="h5" sx={{ fontWeight: 800 }}>{stat.value || 'N/A'}</Typography> <TableCell sx={{ width: 64 }}>
</Paper> <Avatar sx={{
bgcolor: 'rgba(0,123,255,0.1)',
color: 'primary.main',
width: 40,
height: 40
}}>
{stat.icon}
</Avatar>
</TableCell>
<TableCell sx={{ fontWeight: 700, color: 'text.secondary', textTransform: 'uppercase', letterSpacing: 1, fontSize: '0.75rem' }}>
{stat.label}
</TableCell>
<TableCell align="right" sx={{ fontWeight: 800, fontSize: '1.1rem' }}>
{stat.value || 'N/A'}
</TableCell>
</TableRow>
))} ))}
</Box> </TableBody>
</Table>
</TableContainer>
</Box> </Box>
); );
}; };
+60 -57
View File
@@ -8,7 +8,13 @@ import {
Divider, Divider,
Alert, Alert,
LinearProgress, LinearProgress,
IconButton IconButton,
Table,
TableBody,
TableCell,
TableContainer,
TableRow,
Avatar
} from '@mui/material'; } from '@mui/material';
import { import {
CloudDownload, CloudDownload,
@@ -111,106 +117,103 @@ const TransferContent: React.FC<TransferContentProps> = ({ mode = 'both' }) => {
</Alert> </Alert>
)} )}
<Stack direction={{ xs: 'column', md: 'row' }} spacing={3}> <TableContainer component={Paper} sx={{
{/* Export Card */}
{(mode === 'export' || mode === 'both') && (
<Paper sx={{
flex: 1,
p: 4,
borderRadius: 4, borderRadius: 4,
border: 1, border: 1,
borderColor: 'divider', borderColor: 'divider',
display: 'flex', bgcolor: 'background.paper',
flexDirection: 'column', overflow: 'hidden',
alignItems: 'center', boxShadow: 'none'
textAlign: 'center',
gap: 2,
transition: 'all 0.3s ease',
'&:hover': { transform: 'translateY(-4px)', boxShadow: '0 12px 24px rgba(0,0,0,0.1)' }
}}> }}>
<Box sx={{ p: 2, borderRadius: '50%', bgcolor: 'primary.main', color: 'white', mb: 1 }}> <Table>
<CloudDownload sx={{ fontSize: 40 }} /> <TableBody>
</Box> {/* Export Row */}
<Typography variant="h6" sx={{ fontWeight: 700 }}>Export {activeTable ? 'Table' : 'Database'}</Typography> {(mode === 'export' || mode === 'both') && (
<TableRow sx={{ '&:hover': { bgcolor: 'rgba(255,255,255,0.02)' } }}>
<TableCell sx={{ width: 80, verticalAlign: 'top', pt: 3 }}>
<Avatar sx={{ bgcolor: 'primary.main', color: 'white', width: 48, height: 48 }}>
<CloudDownload />
</Avatar>
</TableCell>
<TableCell sx={{ py: 3 }}>
<Typography variant="h6" sx={{ fontWeight: 700, mb: 0.5 }}>Export {activeTable ? 'Table' : 'Database'}</Typography>
<Typography variant="body2" color="text.secondary"> <Typography variant="body2" color="text.secondary">
Create a full backup of the current {activeTable ? 'table' : 'database'}: <strong>{activeTable ? `${activeDatabase}.${activeTable}` : (activeDatabase || 'All Databases')}</strong> Create a full backup of the current {activeTable ? 'table' : 'database'}: <br/>
<Box component="span" sx={{ color: 'primary.main', fontWeight: 600 }}>
{activeTable ? `${activeDatabase}.${activeTable}` : (activeDatabase || 'All Databases')}
</Box>
</Typography> </Typography>
<Box sx={{ flexGrow: 1 }} /> </TableCell>
<TableCell align="right" sx={{ py: 3 }}>
<Button <Button
variant="contained" variant="contained"
fullWidth
size="large"
startIcon={<CloudDownload />} startIcon={<CloudDownload />}
onClick={handleExport} onClick={handleExport}
disabled={loading} disabled={loading}
sx={{ borderRadius: 2, py: 1.5, fontWeight: 700 }} sx={{ borderRadius: 2, px: 3, py: 1, fontWeight: 700, minWidth: 160 }}
> >
Start Export Start Export
</Button> </Button>
</Paper> </TableCell>
</TableRow>
)} )}
{/* Import Card */} {/* Import Row */}
{(mode === 'import' || mode === 'both') && ( {(mode === 'import' || mode === 'both') && (
<Paper sx={{ <TableRow sx={{ '&:hover': { bgcolor: 'rgba(255,255,255,0.02)' } }}>
flex: 1, <TableCell sx={{ width: 80, verticalAlign: 'top', pt: 3 }}>
p: 4, <Avatar sx={{ bgcolor: 'secondary.main', color: 'white', width: 48, height: 48 }}>
borderRadius: 4, <CloudUpload />
border: 1, </Avatar>
borderColor: 'divider', </TableCell>
display: 'flex', <TableCell sx={{ py: 3 }}>
flexDirection: 'column', <Typography variant="h6" sx={{ fontWeight: 700, mb: 0.5 }}>Import {activeTable ? 'Table' : 'Database'}</Typography>
alignItems: 'center', <Typography variant="body2" color="text.secondary" sx={{ mb: 2 }}>
textAlign: 'center',
gap: 2,
transition: 'all 0.3s ease',
'&:hover': { transform: 'translateY(-4px)', boxShadow: '0 12px 24px rgba(0,0,0,0.1)' }
}}>
<Box sx={{ p: 2, borderRadius: '50%', bgcolor: 'secondary.main', color: 'white', mb: 1 }}>
<CloudUpload sx={{ fontSize: 40 }} />
</Box>
<Typography variant="h6" sx={{ fontWeight: 700 }}>Import {activeTable ? 'Table' : 'Database'}</Typography>
<Typography variant="body2" color="text.secondary">
Upload a .sql file to restore or migrate your {activeTable ? 'table' : 'database'}. Upload a .sql file to restore or migrate your {activeTable ? 'table' : 'database'}.
</Typography> </Typography>
<Box sx={{ <Box sx={{
width: '100%', maxWidth: 400,
p: 2, p: 1.5,
border: '2px dashed', border: '2px dashed',
borderColor: file ? 'secondary.main' : 'divider', borderColor: file ? 'secondary.main' : 'divider',
borderRadius: 2, borderRadius: 2,
bgcolor: 'rgba(0,0,0,0.02)', bgcolor: 'rgba(0,0,0,0.02)',
cursor: 'pointer', cursor: 'pointer',
position: 'relative' position: 'relative',
display: 'flex',
alignItems: 'center',
gap: 1
}}> }}>
<input <input
type="file" type="file"
accept=".sql" accept=".sql"
onChange={handleFileChange} onChange={handleFileChange}
style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', opacity: 0, cursor: 'pointer' }} style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', opacity: 0, cursor: 'pointer', zIndex: 1 }}
/> />
<Typography variant="caption" color="text.secondary"> <CloudUpload fontSize="small" color={file ? "secondary" : "disabled"} />
{file ? file.name : "Click or drag .sql file here"} <Typography variant="caption" sx={{ fontWeight: 600 }}>
{file ? file.name : "Select or drag .sql file here"}
</Typography> </Typography>
</Box> </Box>
</TableCell>
<Box sx={{ flexGrow: 1 }} /> <TableCell align="right" sx={{ py: 3 }}>
<Button <Button
variant="outlined" variant="outlined"
fullWidth
size="large"
color="secondary" color="secondary"
startIcon={<CloudUpload />} startIcon={<CloudUpload />}
onClick={handleImport} onClick={handleImport}
disabled={loading || !file} disabled={loading || !file}
sx={{ borderRadius: 2, py: 1.5, fontWeight: 700 }} sx={{ borderRadius: 2, px: 3, py: 1, fontWeight: 700, minWidth: 160 }}
> >
Start Import Start Import
</Button> </Button>
</Paper> </TableCell>
</TableRow>
)} )}
</Stack> </TableBody>
</Table>
</TableContainer>
{loading && ( {loading && (
<Box sx={{ width: '100%' }}> <Box sx={{ width: '100%' }}>