feat: add Turkish and English localization files and initialize main application component

This commit is contained in:
Ümit Tunç
2026-04-02 00:42:20 +03:00
parent d4d3d6cbb3
commit 3627feac1c
3 changed files with 173 additions and 120 deletions
+169 -119
View File
@@ -68,7 +68,8 @@ import {
Language as HostIcon, Language as HostIcon,
Launch as LaunchIcon, Launch as LaunchIcon,
Handyman as ToolsIcon, Handyman as ToolsIcon,
Restore as ResetIcon Restore as ResetIcon,
Search as SearchIcon
} from '@mui/icons-material' } from '@mui/icons-material'
import Swal from 'sweetalert2' import Swal from 'sweetalert2'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@@ -117,6 +118,7 @@ function App(): JSX.Element {
mariadb: { status: 'stopped' } mariadb: { status: 'stopped' }
}) })
const [activeTab, setActiveTab] = useState('dashboard') const [activeTab, setActiveTab] = useState('dashboard')
const [projectSearch, setProjectSearch] = useState('')
const [settingsTab, setSettingsTab] = useState(0) const [settingsTab, setSettingsTab] = useState(0)
const [dashboardTab, setDashboardTab] = useState(0) const [dashboardTab, setDashboardTab] = useState(0)
const [projects, setProjects] = useState<any[]>([]) const [projects, setProjects] = useState<any[]>([])
@@ -1536,10 +1538,40 @@ function App(): JSX.Element {
{activeTab === 'projects' && ( {activeTab === 'projects' && (
<Box> <Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}> <Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, gap: 2, alignItems: { xs: 'stretch', sm: 'center' }, mb: 3 }}>
<Typography variant="h5" sx={{ fontWeight: 'medium' }}> <Typography variant="h5" sx={{ fontWeight: 'medium', flexGrow: 1 }}>
{t('projects.title')} {t('projects.title')}
</Typography> </Typography>
<TextField
size="small"
placeholder={t('projects.search')}
value={projectSearch}
onChange={(e) => setProjectSearch(e.target.value)}
sx={{
minWidth: { sm: 300 },
bgcolor: 'rgba(255, 255, 255, 0.03)',
borderRadius: 1,
'& .MuiOutlinedInput-root': {
color: '#fff',
'& fieldset': { borderColor: 'rgba(255, 255, 255, 0.1)' },
'&:hover fieldset': { borderColor: 'rgba(255, 255, 255, 0.2)' },
}
}}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon sx={{ color: 'rgba(255, 255, 255, 0.3)' }} />
</InputAdornment>
),
endAdornment: projectSearch && (
<InputAdornment position="end">
<IconButton size="small" onClick={() => setProjectSearch('')} sx={{ color: 'rgba(255, 255, 255, 0.3)' }}>
<CloseIcon fontSize="small" />
</IconButton>
</InputAdornment>
)
}}
/>
<Button variant="contained" startIcon={<ProjectIcon />} onClick={() => setIsProjectDialogOpen(true)}> <Button variant="contained" startIcon={<ProjectIcon />} onClick={() => setIsProjectDialogOpen(true)}>
{t('projects.add_new')} {t('projects.add_new')}
</Button> </Button>
@@ -1549,126 +1581,144 @@ function App(): JSX.Element {
<Paper sx={{ p: 4, textAlign: 'center', bgcolor: 'rgba(255, 255, 255, 0.03)' }}> <Paper sx={{ p: 4, textAlign: 'center', bgcolor: 'rgba(255, 255, 255, 0.03)' }}>
<Typography variant="body1" color="text.secondary">{t('projects.empty')}</Typography> <Typography variant="body1" color="text.secondary">{t('projects.empty')}</Typography>
</Paper> </Paper>
) : ( ) : (() => {
<Grid container spacing={3}> const filteredProjects = projects.filter(p =>
{projects.map((project) => ( p.name.toLowerCase().includes(projectSearch.toLowerCase()) ||
<Grid item xs={12} md={6} key={project.id}> p.host.toLowerCase().includes(projectSearch.toLowerCase())
<Paper );
elevation={0}
sx={{ if (filteredProjects.length === 0) {
p: 2.5, return (
background: 'rgba(255, 255, 255, 0.03)', <Paper sx={{ p: 6, textAlign: 'center', bgcolor: 'rgba(255, 255, 255, 0.02)', border: '1px dashed rgba(255, 255, 255, 0.1)' }}>
backdropFilter: 'blur(10px)', <SearchIcon sx={{ fontSize: 48, color: 'rgba(255, 255, 255, 0.1)', mb: 2 }} />
border: '1px solid rgba(255, 255, 255, 0.05)', <Typography variant="h6" sx={{ color: 'rgba(255, 255, 255, 0.4)' }}>
borderRadius: 3, {t('common.not_found', 'Eşleşen proje bulunamadı')}
transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', </Typography>
'&:hover': { </Paper>
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)' return (
} <Grid container spacing={3}>
}} {filteredProjects.map((project) => (
> <Grid item xs={12} md={6} key={project.id}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2, gap: 1 }}> <Paper
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, minWidth: 0 }}> elevation={0}
<Avatar sx={{ bgcolor: 'rgba(57, 167, 255, 0.1)', color: '#39A7FF', flexShrink: 0 }}> sx={{
<FolderIcon /> p: 2.5,
</Avatar> background: 'rgba(255, 255, 255, 0.03)',
<Box sx={{ minWidth: 0, flexGrow: 1 }}> backdropFilter: 'blur(10px)',
<Typography variant="h6" noWrap sx={{ fontWeight: 700, color: '#fff', lineHeight: 1.2, textOverflow: 'ellipsis' }}> border: '1px solid rgba(255, 255, 255, 0.05)',
{project.name} borderRadius: 3,
</Typography> transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
<Typography variant="caption" noWrap sx={{ color: 'rgba(255, 255, 255, 0.4)', fontFamily: 'monospace', display: 'block', textOverflow: 'ellipsis' }}> '&:hover': {
{project.path.length > 40 ? '...' + project.path.slice(-37) : project.path} background: 'rgba(255, 255, 255, 0.06)',
</Typography> transform: 'translateY(-4px)',
borderColor: 'rgba(57, 167, 255, 0.3)',
boxShadow: '0 12px 24px rgba(0,0,0,0.3)'
}
}}
>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2, gap: 1 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, minWidth: 0 }}>
<Avatar sx={{ bgcolor: 'rgba(57, 167, 255, 0.1)', color: '#39A7FF', flexShrink: 0 }}>
<FolderIcon />
</Avatar>
<Box sx={{ minWidth: 0, flexGrow: 1 }}>
<Typography variant="h6" noWrap sx={{ fontWeight: 700, color: '#fff', lineHeight: 1.2, textOverflow: 'ellipsis' }}>
{project.name}
</Typography>
<Typography variant="caption" noWrap sx={{ color: 'rgba(255, 255, 255, 0.4)', fontFamily: 'monospace', display: 'block', textOverflow: 'ellipsis' }}>
{project.path.length > 40 ? '...' + project.path.slice(-37) : project.path}
</Typography>
</Box>
</Box> </Box>
</Box> <Box sx={{ display: 'flex', gap: 0.5, flexShrink: 0 }}>
<Box sx={{ display: 'flex', gap: 0.5, flexShrink: 0 }}> <Tooltip title={t('common.edit')}>
<Tooltip title={t('common.edit')}> <IconButton size="small" onClick={() => {
<IconButton size="small" onClick={() => { setNewProject(project)
setNewProject(project) setIsProjectDialogOpen(true)
setIsProjectDialogOpen(true) }} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}>
}} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}> <EditIcon fontSize="small" />
<EditIcon fontSize="small" /> </IconButton>
</IconButton> </Tooltip>
</Tooltip> {project.serverType !== 'apache' && (
{project.serverType !== 'apache' && ( <Tooltip title={t('server.nginx.config')}>
<Tooltip title={t('server.nginx.config')}> <IconButton size="small" onClick={() => handleOpenProjectNginxConfig(project.host)} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}>
<IconButton size="small" onClick={() => handleOpenProjectNginxConfig(project.host)} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}> <TerminalIcon fontSize="small" />
<TerminalIcon 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> </IconButton>
</Tooltip> </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)' }}
/>
<Chip
size="small"
icon={project.serverType === 'apache' ? <ServerIcon sx={{ fontSize: '1rem !important' }} /> : <WebIcon sx={{ fontSize: '1rem !important' }} />}
label={project.serverType === 'apache' ? 'Apache' : 'Nginx'}
sx={{ bgcolor: 'rgba(255, 255, 255, 0.05)', color: '#fff', fontWeight: 600 }}
/>
</Box>
<ProjectResourceMonitor
serverType={project.serverType === 'apache' ? 'Apache' : 'Nginx'}
serverStats={project.serverType === 'apache' ? status.apache : status.nginx}
phpStats={status[`php:${project.phpVersion}`]}
/>
<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> </Box>
<Button </Box>
size="small"
endIcon={<LaunchIcon />} <Stack spacing={1.5}>
onClick={() => { <Box sx={{ display: 'flex', gap: 1 }}>
const port = project.serverType === 'apache' ? settings.apachePort : settings.nginxPort <Chip
window.open(`http://localhost:${port}/${project.host}/`, '_blank') size="small"
}} icon={<PhpIcon sx={{ fontSize: '1rem !important' }} />}
sx={{ label={`PHP ${project.phpVersion}`}
color: '#39A7FF', sx={{ bgcolor: 'rgba(71, 74, 255, 0.1)', color: '#777bb4', fontWeight: 600, border: '1px solid rgba(71, 74, 255, 0.2)' }}
textTransform: 'none', />
fontSize: '0.75rem', <Chip
p: '2px 8px', size="small"
'&:hover': { bgcolor: 'rgba(57, 167, 255, 0.1)' } icon={project.serverType === 'apache' ? <ServerIcon sx={{ fontSize: '1rem !important' }} /> : <WebIcon sx={{ fontSize: '1rem !important' }} />}
}} label={project.serverType === 'apache' ? 'Apache' : 'Nginx'}
> sx={{ bgcolor: 'rgba(255, 255, 255, 0.05)', color: '#fff', fontWeight: 600 }}
{t('projects.browse')} />
</Button> </Box>
</Paper>
</Stack> <ProjectResourceMonitor
</Paper> serverType={project.serverType === 'apache' ? 'Apache' : 'Nginx'}
</Grid> serverStats={project.serverType === 'apache' ? status.apache : status.nginx}
))} phpStats={status[`php:${project.phpVersion}`]}
</Grid> />
)}
<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={() => {
const port = project.serverType === 'apache' ? settings.apachePort : settings.nginxPort
window.open(`http://localhost:${port}/${project.host}/`, '_blank')
}}
sx={{
color: '#39A7FF',
textTransform: 'none',
fontSize: '0.75rem',
p: '2px 8px',
'&:hover': { bgcolor: 'rgba(57, 167, 255, 0.1)' }
}}
>
{t('projects.browse')}
</Button>
</Paper>
</Stack>
</Paper>
</Grid>
))}
</Grid>
)
})()}
</Box> </Box>
)} )}
</Container> </Container>
+1
View File
@@ -62,6 +62,7 @@
"projects": { "projects": {
"title": "My Projects", "title": "My Projects",
"add_new": "Add New Project", "add_new": "Add New Project",
"search": "Search for project or host...",
"edit": "Edit Project", "edit": "Edit Project",
"empty": "No projects added yet.", "empty": "No projects added yet.",
"name": "Project Name", "name": "Project Name",
+3 -1
View File
@@ -62,6 +62,7 @@
"projects": { "projects": {
"title": "Projelerim", "title": "Projelerim",
"add_new": "Yeni Proje Ekle", "add_new": "Yeni Proje Ekle",
"search": "Proje veya host ara...",
"edit": "Proje Düzenle", "edit": "Proje Düzenle",
"empty": "Henüz bir proje eklenmemiş.", "empty": "Henüz bir proje eklenmemiş.",
"name": "Proje Adı", "name": "Proje Adı",
@@ -77,7 +78,8 @@
"added": "Proje başarıyla eklendi.", "added": "Proje başarıyla eklendi.",
"updated": "Proje başarıyla güncellendi.", "updated": "Proje başarıyla güncellendi.",
"deleted": "Proje silindi.", "deleted": "Proje silindi.",
"delete_confirm": "Bu projeyi silmek istediğinize emin misiniz? Dosyalarınız silinmez, sadece listeden kaldırılır." "delete_confirm": "Bu projeyi silmek istediğinize emin misiniz? Dosyalarınız silinmez, sadece listeden kaldırılır.",
"browse" : "Gözat"
}, },
"settings": { "settings": {
"general": "Genel Ayarlar", "general": "Genel Ayarlar",