feat: implement dashboard UI and configure path aliases for renderer and locales

This commit is contained in:
Ümit Tunç
2026-03-28 20:48:05 +03:00
parent ab037b7bd9
commit f9c9bf117e
7 changed files with 124 additions and 78 deletions
+116 -73
View File
@@ -34,7 +34,9 @@ import {
Tab,
InputAdornment,
CircularProgress,
Stack
Stack,
Grid,
Avatar
} from '@mui/material'
import {
Dashboard as DashboardIcon,
@@ -55,11 +57,14 @@ import {
Save as SaveIcon,
LanguageOutlined as WebIcon,
Terminal as TerminalIcon,
ContentCopy as CopyIcon,
Close as CloseIcon,
ContentCopy as ContentCopyIcon,
OpenInNew as OpenIcon,
Edit as EditIcon
Edit as EditIcon,
Folder as FolderIcon,
Storage as MySQLIcon,
Language as HostIcon,
Launch as LaunchIcon
} from '@mui/icons-material'
import { useTranslation } from 'react-i18next'
@@ -810,33 +815,110 @@ function App(): JSX.Element {
<Typography variant="body1" color="text.secondary">Henüz bir proje eklenmemiş.</Typography>
</Paper>
) : (
<List>
<Grid container spacing={3}>
{projects.map((project) => (
<Paper key={project.id} sx={{ mb: 2, bgcolor: 'rgba(255, 255, 255, 0.05)' }}>
<ListItem
secondaryAction={
<Box>
<IconButton edge="end" aria-label="edit" onClick={() => {
setNewProject(project)
setIsProjectDialogOpen(true)
}} sx={{ mr: 1 }}>
<EditIcon color="primary" />
</IconButton>
<IconButton edge="end" aria-label="delete" onClick={() => handleRemoveProject(project.id)}>
<DeleteIcon color="error" />
</IconButton>
</Box>
}
<Grid item xs={12} md={6} key={project.id}>
<Paper
elevation={0}
sx={{
p: 2.5,
background: 'rgba(255, 255, 255, 0.03)',
backdropFilter: 'blur(10px)',
border: '1px solid rgba(255, 255, 255, 0.05)',
borderRadius: 3,
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
'&:hover': {
background: 'rgba(255, 255, 255, 0.06)',
transform: 'translateY(-4px)',
borderColor: 'rgba(57, 167, 255, 0.3)',
boxShadow: '0 12px 24px rgba(0,0,0,0.3)'
}
}}
>
<ListItemIcon><ProjectIcon color="primary" /></ListItemIcon>
<ListItemText
primary={project.name}
secondary={`${project.path} - PHP: ${project.phpVersion} | MySQL: ${project.mySqlVersion || 'Yok'} - Host: ${project.host}`}
/>
</ListItem>
</Paper>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
<Avatar sx={{ bgcolor: 'rgba(57, 167, 255, 0.1)', color: '#39A7FF' }}>
<FolderIcon />
</Avatar>
<Box>
<Typography variant="h6" sx={{ fontWeight: 700, color: '#fff', lineHeight: 1.2 }}>
{project.name}
</Typography>
<Typography variant="caption" sx={{ color: 'rgba(255, 255, 255, 0.4)', fontFamily: 'monospace' }}>
{project.path.length > 40 ? '...' + project.path.slice(-37) : project.path}
</Typography>
</Box>
</Box>
<Box sx={{ display: 'flex', gap: 0.5 }}>
<Tooltip title="Düzenle">
<IconButton size="small" onClick={() => {
setNewProject(project)
setIsProjectDialogOpen(true)
}} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}>
<EditIcon fontSize="small" />
</IconButton>
</Tooltip>
<Tooltip title="Sil">
<IconButton size="small" onClick={() => handleRemoveProject(project.id)} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#ff4444' } }}>
<DeleteIcon fontSize="small" />
</IconButton>
</Tooltip>
</Box>
</Box>
<Stack spacing={1.5}>
<Box sx={{ display: 'flex', gap: 1 }}>
<Chip
size="small"
icon={<PhpIcon sx={{ fontSize: '1rem !important' }} />}
label={`PHP ${project.phpVersion}`}
sx={{ bgcolor: 'rgba(71, 74, 255, 0.1)', color: '#777bb4', fontWeight: 600, border: '1px solid rgba(71, 74, 255, 0.2)' }}
/>
{project.mySqlVersion && (
<Chip
size="small"
icon={<MySQLIcon sx={{ fontSize: '1rem !important' }} />}
label={`MySQL ${project.mySqlVersion}`}
sx={{ bgcolor: 'rgba(242, 145, 17, 0.1)', color: '#f29111', fontWeight: 600, border: '1px solid rgba(242, 145, 17, 0.2)' }}
/>
)}
</Box>
<Paper variant="outlined" sx={{
p: 1.5,
bgcolor: 'rgba(0,0,0,0.2)',
borderColor: 'rgba(255,255,255,0.05)',
borderRadius: 2,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between'
}}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<HostIcon sx={{ fontSize: '1rem', color: 'rgba(255,255,255,0.3)' }} />
<Typography variant="body2" sx={{ color: '#fff', fontWeight: 500, fontFamily: 'monospace' }}>
{project.host}
</Typography>
</Box>
<Button
size="small"
endIcon={<LaunchIcon />}
onClick={() => window.open(`http://${project.host}`, '_blank')}
sx={{
color: '#39A7FF',
textTransform: 'none',
fontSize: '0.75rem',
p: '2px 8px',
'&:hover': { bgcolor: 'rgba(57, 167, 255, 0.1)' }
}}
>
Gözat
</Button>
</Paper>
</Stack>
</Paper>
</Grid>
))}
</List>
</Grid>
)}
</Box>
)}
@@ -1046,51 +1128,12 @@ function App(): JSX.Element {
variant="contained"
disabled={!newProject.name || !newProject.path || installedPhpVersions.length === 0 || mySqlVersions.filter(v => v.status === 'installed').length === 0}
>
</Dialog>
{/* Log Dialog */}
<Dialog open={logDialogOpen} onClose={() => setLogDialogOpen(false)} fullWidth maxWidth="md">
<DialogTitle sx={{ bgcolor: '#1e1e1e', color: '#fff', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<TerminalIcon />
{selectedService.toUpperCase()} Konsol Çıktısı
</Box>
<Box>
<IconButton size="small" sx={{ color: '#fff' }} onClick={() => navigator.clipboard.writeText(logs.join('\n'))}>
<CopyIcon fontSize="small" />
</IconButton>
</Box>
</DialogTitle>
<DialogContent sx={{ bgcolor: '#000', p: 0 }}>
<Box
sx={{
p: 2,
fontFamily: 'Consolas, monospace',
fontSize: '0.85rem',
color: '#00ff00',
height: '400px',
overflowY: 'auto',
display: 'flex',
flexDirection: 'column'
}}
>
{logs.length === 0 ? (
<Typography variant="caption" sx={{ color: '#666' }}>Henüz log kaydı yok...</Typography>
) : (
logs.map((line, i) => (
<Box key={i} sx={{ mb: 0.5, borderLeft: '2px solid #333', pl: 1 }}>
{line}
</Box>
))
)}
</Box>
</DialogContent>
<DialogActions sx={{ bgcolor: '#1e1e1e' }}>
<Button onClick={() => setLogDialogOpen(false)} variant="contained" size="small">Kapat</Button>
</DialogActions>
</Dialog>
</Box>
)
{newProject.id ? 'Güncelle' : 'Ekle'}
</Button>
</DialogActions>
</Dialog>
</Box>
)
}
export default App
export default App
+3 -3
View File
@@ -1,8 +1,8 @@
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import tr from '@locales/tr.json'
import en from '@locales/en.json'
import de from '@locales/de.json'
import tr from './locales/tr.json'
import en from './locales/en.json'
import de from './locales/de.json'
i18n
.use(initReactI18next)
+16
View File
@@ -0,0 +1,16 @@
{
"common": {
"start": "Starten",
"stop": "Stoppen",
"restart": "Neustart",
"settings": "Einstellungen",
"close": "Schließen",
"quit": "Beenden"
},
"dashboard": {
"title": "Trunçgil Multi-PHP Server",
"status": "Serverstatus",
"services": "Dienste",
"active_version": "Aktive PHP-Version"
}
}
+16
View File
@@ -0,0 +1,16 @@
{
"common": {
"start": "Start",
"stop": "Stop",
"restart": "Restart",
"settings": "Settings",
"close": "Close",
"quit": "Quit"
},
"dashboard": {
"title": "Trunçgil Multi-PHP Server",
"status": "Server Status",
"services": "Services",
"active_version": "Active PHP Version"
}
}
+16
View File
@@ -0,0 +1,16 @@
{
"common": {
"start": "Başlat",
"stop": "Durdur",
"restart": "Yeniden Başlat",
"settings": "Ayarlar",
"close": "Kapat",
"quit": "Çıkış"
},
"dashboard": {
"title": "Trunçgil Multi-PHP Server",
"status": "Sunucu Durumu",
"services": "Servisler",
"active_version": "Aktif PHP Sürümü"
}
}