feat: add phpMyAdmin service management and UI integration

This commit is contained in:
Ümit Tunç
2026-03-28 21:38:07 +03:00
parent 9837b0abd7
commit a411138732
5 changed files with 112 additions and 93 deletions
+2 -2
View File
@@ -21,9 +21,9 @@ export class PhpMyAdminService {
} }
try { try {
await downloadService.downloadAndExtract(this.downloadUrl, this.pmaDir, (p) => { await downloadService.downloadAndExtract(this.downloadUrl, 'phpmyadmin', (p) => {
if (onProgress) onProgress(p) if (onProgress) onProgress(p)
}) }, this.pmaDir)
// After extraction, the zip usually contains a folder like phpMyAdmin-x.y.z-all-languages // After extraction, the zip usually contains a folder like phpMyAdmin-x.y.z-all-languages
// We should move contents to the root of this.pmaDir if they are nested // We should move contents to the root of this.pmaDir if they are nested
+107 -91
View File
@@ -65,7 +65,8 @@ import {
Folder as FolderIcon, Folder as FolderIcon,
Storage as MySQLIcon, Storage as MySQLIcon,
Language as HostIcon, Language as HostIcon,
Launch as LaunchIcon Launch as LaunchIcon,
Handyman as ToolsIcon
} from '@mui/icons-material' } from '@mui/icons-material'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@@ -497,6 +498,12 @@ function App(): JSX.Element {
<ListItemText primary="Projeler" /> <ListItemText primary="Projeler" />
</ListItemButton> </ListItemButton>
</ListItem> </ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'tools'} onClick={() => setActiveTab('tools')}>
<ListItemIcon><ToolsIcon color={activeTab === 'tools' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary="Araçlar" />
</ListItemButton>
</ListItem>
</List> </List>
<Divider /> <Divider />
<List> <List>
@@ -577,97 +584,51 @@ function App(): JSX.Element {
)} )}
{settingsTab === 1 && ( {settingsTab === 1 && (
<> <Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
<Paper sx={{ bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}> <Box sx={{ p: 2, borderBottom: '1px solid rgba(255, 255, 255, 0.05)' }}>
<Box sx={{ p: 2, borderBottom: '1px solid rgba(255, 255, 255, 0.05)' }}> <Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>MySQL Sürümleri</Typography>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>MySQL Sürümleri</Typography> <Typography variant="body2" color="text.secondary">Farklı MySQL sürümlerini indirebilir ve sunucunuzda kullanabilirsiniz.</Typography>
<Typography variant="body2" color="text.secondary">Farklı MySQL sürümlerini indirebilir ve sunucunuzda kullanabilirsiniz.</Typography> </Box>
</Box> <List sx={{ maxHeight: 500, overflow: 'auto' }}>
<List sx={{ maxHeight: 500, overflow: 'auto' }}> {mySqlVersions.length === 0 && !loadingVersions && (
{mySqlVersions.length === 0 && !loadingVersions && ( <ListItem><ListItemText primary="Yükleniyor veya liste boş..." /></ListItem>
<ListItem><ListItemText primary="Yükleniyor veya liste boş..." /></ListItem>
)}
{mySqlVersions.map((v) => (
<ListItem key={v.id} sx={{ py: 2 }}>
<ListItemIcon>
{v.status === 'installed' ? <InstalledIcon color="success" /> : <AvailableIcon color="action" />}
</ListItemIcon>
<ListItemText
primary={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{`MySQL ${v.version} (${v.cycle})`}
{v.description && (
<Chip label={v.description} size="small" variant="outlined" sx={{ fontSize: '0.7rem', height: 20 }} />
)}
</Box>
}
secondary={v.status === 'downloading' ? `İndiriliyor... %${Math.round(v.progress)}` : v.status === 'installed' ? 'Kurulu' : 'İndirilebilir'}
sx={{ flexGrow: 1 }}
/>
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
{v.status === 'available' && (
<Button variant="outlined" size="small" startIcon={<DownloadIcon />} onClick={() => handleDownloadMySql(v.id)}>
İNDİR
</Button>
)}
{v.status === 'downloading' && (
<Box sx={{ width: '100%', mt: 1 }}>
<LinearProgress variant="determinate" value={v.progress} sx={{ height: 8, borderRadius: 5 }} />
</Box>
)}
{v.status === 'installed' && (
<Chip label="HAZIR" color="success" variant="outlined" size="small" />
)}
</Box>
</ListItem>
))}
</List>
</Paper>
<Paper sx={{ mt: 3, p: 2, bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Box sx={{ p: 1.5, bgcolor: 'rgba(255, 255, 255, 0.05)', borderRadius: 2 }}>
<DbIcon color="primary" />
</Box>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>phpMyAdmin</Typography>
<Typography variant="body2" color="text.secondary">
{pmaInstalled ? 'Veritabanı yönetimi için phpMyAdmin hazır.' : 'Veritabanlarını web üzerinden yönetmek için phpMyAdmin kurun.'}
</Typography>
</Box>
<Box>
{!pmaInstalled ? (
<Button
variant="contained"
size="small"
onClick={handleSetupPma}
disabled={pmaLoading}
startIcon={pmaLoading ? <CircularProgress size={20} /> : <DownloadIcon />}
>
{pmaLoading ? 'KURULUYOR...' : 'KURULUMU BAŞLAT'}
</Button>
) : (
<Button
variant="outlined"
size="small"
onClick={handleOpenPma}
startIcon={<OpenIcon />}
>
</Button>
)}
</Box>
</Box>
{pmaLoading && (
<Box sx={{ mt: 2 }}>
<LinearProgress variant="determinate" value={pmaProgress} sx={{ height: 6, borderRadius: 3 }} />
<Typography variant="caption" sx={{ mt: 0.5, display: 'block', textAlign: 'center' }}>
İndiriliyor... %{Math.round(pmaProgress)}
</Typography>
</Box>
)} )}
</Paper> {mySqlVersions.map((v) => (
</> <ListItem key={v.id} sx={{ py: 2 }}>
<ListItemIcon>
{v.status === 'installed' ? <InstalledIcon color="success" /> : <AvailableIcon color="action" />}
</ListItemIcon>
<ListItemText
primary={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
{`MySQL ${v.version} (${v.cycle})`}
{v.description && (
<Chip label={v.description} size="small" variant="outlined" sx={{ fontSize: '0.7rem', height: 20 }} />
)}
</Box>
}
secondary={v.status === 'downloading' ? `İndiriliyor... %${Math.round(v.progress)}` : v.status === 'installed' ? 'Kurulu' : 'İndirilebilir'}
sx={{ flexGrow: 1 }}
/>
<Box sx={{ minWidth: 120, textAlign: 'right' }}>
{v.status === 'available' && (
<Button variant="outlined" size="small" startIcon={<DownloadIcon />} onClick={() => handleDownloadMySql(v.id)}>
İNDİR
</Button>
)}
{v.status === 'downloading' && (
<Box sx={{ width: '100%', mt: 1 }}>
<LinearProgress variant="determinate" value={v.progress} sx={{ height: 8, borderRadius: 5 }} />
</Box>
)}
{v.status === 'installed' && (
<Chip label="HAZIR" color="success" variant="outlined" size="small" />
)}
</Box>
</ListItem>
))}
</List>
</Paper>
)} )}
{settingsTab === 2 && ( {settingsTab === 2 && (
@@ -748,6 +709,7 @@ function App(): JSX.Element {
)} )}
</Box> </Box>
)} )}
{activeTab === 'dashboard' && ( {activeTab === 'dashboard' && (
<> <>
<Typography variant="h5" gutterBottom sx={{ mb: 3, fontWeight: 'medium' }}> <Typography variant="h5" gutterBottom sx={{ mb: 3, fontWeight: 'medium' }}>
@@ -823,6 +785,59 @@ function App(): JSX.Element {
</> </>
)} )}
{activeTab === 'tools' && (
<Box>
<Typography variant="h5" sx={{ mb: 3, fontWeight: 'medium' }}>
{t('common.tools')}
</Typography>
<Paper sx={{ mb: 3, p: 3, bgcolor: 'rgba(255, 255, 255, 0.03)', borderRadius: 2, border: '1px solid rgba(255, 255, 255, 0.05)' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 3 }}>
<Box sx={{ p: 2, bgcolor: 'rgba(255, 255, 255, 0.05)', borderRadius: 3 }}>
<DbIcon color="primary" sx={{ fontSize: 32 }} />
</Box>
<Box sx={{ flexGrow: 1 }}>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>phpMyAdmin</Typography>
<Typography variant="body2" color="text.secondary">
{pmaInstalled ? 'Veritabanı yönetimi için phpMyAdmin hazır.' : 'Veritabanlarını web üzerinden yönetmek için phpMyAdmin kurun.'}
</Typography>
</Box>
<Box>
{!pmaInstalled ? (
<Button
variant="contained"
size="large"
onClick={handleSetupPma}
disabled={pmaLoading}
startIcon={pmaLoading ? <CircularProgress size={20} /> : <DownloadIcon />}
sx={{ borderRadius: 2 }}
>
{pmaLoading ? 'KURULUYOR...' : 'KURULUMU BAŞLAT'}
</Button>
) : (
<Button
variant="contained"
size="large"
onClick={handleOpenPma}
startIcon={<OpenIcon />}
sx={{ borderRadius: 2 }}
>
</Button>
)}
</Box>
</Box>
{pmaLoading && (
<Box sx={{ mt: 3 }}>
<LinearProgress variant="determinate" value={pmaProgress} sx={{ height: 8, borderRadius: 4 }} />
<Typography variant="caption" sx={{ mt: 1, display: 'block', textAlign: 'center', color: 'primary.main', fontWeight: 'bold' }}>
İndiriliyor... %{Math.round(pmaProgress)}
</Typography>
</Box>
)}
</Paper>
</Box>
)}
{activeTab === 'projects' && ( {activeTab === 'projects' && (
<Box> <Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}> <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
@@ -958,6 +973,7 @@ function App(): JSX.Element {
{notification.message} {notification.message}
</Alert> </Alert>
</Snackbar> </Snackbar>
{/* Log Viewer Dialog */} {/* Log Viewer Dialog */}
<Dialog <Dialog
open={logDialogOpen} open={logDialogOpen}
+1
View File
@@ -4,6 +4,7 @@
"stop": "Stoppen", "stop": "Stoppen",
"restart": "Neustart", "restart": "Neustart",
"settings": "Einstellungen", "settings": "Einstellungen",
"tools": "Werkzeuge",
"close": "Schließen", "close": "Schließen",
"quit": "Beenden" "quit": "Beenden"
}, },
+1
View File
@@ -4,6 +4,7 @@
"stop": "Stop", "stop": "Stop",
"restart": "Restart", "restart": "Restart",
"settings": "Settings", "settings": "Settings",
"tools": "Tools",
"close": "Close", "close": "Close",
"quit": "Quit" "quit": "Quit"
}, },
+1
View File
@@ -4,6 +4,7 @@
"stop": "Durdur", "stop": "Durdur",
"restart": "Yeniden Başlat", "restart": "Yeniden Başlat",
"settings": "Ayarlar", "settings": "Ayarlar",
"tools": "Araçlar",
"close": "Kapat", "close": "Kapat",
"quit": "Çıkış" "quit": "Çıkış"
}, },