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,
Launch as LaunchIcon,
Handyman as ToolsIcon,
Restore as ResetIcon
Restore as ResetIcon,
Search as SearchIcon
} from '@mui/icons-material'
import Swal from 'sweetalert2'
import { useTranslation } from 'react-i18next'
@@ -117,6 +118,7 @@ function App(): JSX.Element {
mariadb: { status: 'stopped' }
})
const [activeTab, setActiveTab] = useState('dashboard')
const [projectSearch, setProjectSearch] = useState('')
const [settingsTab, setSettingsTab] = useState(0)
const [dashboardTab, setDashboardTab] = useState(0)
const [projects, setProjects] = useState<any[]>([])
@@ -1536,10 +1538,40 @@ function App(): JSX.Element {
{activeTab === 'projects' && (
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Typography variant="h5" sx={{ fontWeight: 'medium' }}>
<Box sx={{ display: 'flex', flexDirection: { xs: 'column', sm: 'row' }, gap: 2, alignItems: { xs: 'stretch', sm: 'center' }, mb: 3 }}>
<Typography variant="h5" sx={{ fontWeight: 'medium', flexGrow: 1 }}>
{t('projects.title')}
</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)}>
{t('projects.add_new')}
</Button>
@@ -1549,126 +1581,144 @@ function App(): JSX.Element {
<Paper sx={{ p: 4, textAlign: 'center', bgcolor: 'rgba(255, 255, 255, 0.03)' }}>
<Typography variant="body1" color="text.secondary">{t('projects.empty')}</Typography>
</Paper>
) : (
<Grid container spacing={3}>
{projects.map((project) => (
<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)'
}
}}
>
<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>
) : (() => {
const filteredProjects = projects.filter(p =>
p.name.toLowerCase().includes(projectSearch.toLowerCase()) ||
p.host.toLowerCase().includes(projectSearch.toLowerCase())
);
if (filteredProjects.length === 0) {
return (
<Paper sx={{ p: 6, textAlign: 'center', bgcolor: 'rgba(255, 255, 255, 0.02)', border: '1px dashed rgba(255, 255, 255, 0.1)' }}>
<SearchIcon sx={{ fontSize: 48, color: 'rgba(255, 255, 255, 0.1)', mb: 2 }} />
<Typography variant="h6" sx={{ color: 'rgba(255, 255, 255, 0.4)' }}>
{t('common.not_found', 'Eşleşen proje bulunamadı')}
</Typography>
</Paper>
);
}
return (
<Grid container spacing={3}>
{filteredProjects.map((project) => (
<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)'
}
}}
>
<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 sx={{ display: 'flex', gap: 0.5, flexShrink: 0 }}>
<Tooltip title={t('common.edit')}>
<IconButton size="small" onClick={() => {
setNewProject(project)
setIsProjectDialogOpen(true)
}} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}>
<EditIcon fontSize="small" />
</IconButton>
</Tooltip>
{project.serverType !== 'apache' && (
<Tooltip title={t('server.nginx.config')}>
<IconButton size="small" onClick={() => handleOpenProjectNginxConfig(project.host)} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}>
<TerminalIcon fontSize="small" />
<Box sx={{ display: 'flex', gap: 0.5, flexShrink: 0 }}>
<Tooltip title={t('common.edit')}>
<IconButton size="small" onClick={() => {
setNewProject(project)
setIsProjectDialogOpen(true)
}} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}>
<EditIcon fontSize="small" />
</IconButton>
</Tooltip>
{project.serverType !== 'apache' && (
<Tooltip title={t('server.nginx.config')}>
<IconButton size="small" onClick={() => handleOpenProjectNginxConfig(project.host)} sx={{ color: 'rgba(255,255,255,0.4)', '&:hover': { color: '#39A7FF' } }}>
<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>
</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>
<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>
<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>
<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>
)}
</Container>
+1
View File
@@ -62,6 +62,7 @@
"projects": {
"title": "My Projects",
"add_new": "Add New Project",
"search": "Search for project or host...",
"edit": "Edit Project",
"empty": "No projects added yet.",
"name": "Project Name",
+3 -1
View File
@@ -62,6 +62,7 @@
"projects": {
"title": "Projelerim",
"add_new": "Yeni Proje Ekle",
"search": "Proje veya host ara...",
"edit": "Proje Düzenle",
"empty": "Henüz bir proje eklenmemiş.",
"name": "Proje Adı",
@@ -77,7 +78,8 @@
"added": "Proje başarıyla eklendi.",
"updated": "Proje başarıyla güncellendi.",
"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": {
"general": "Genel Ayarlar",