feat: implement MariaDB export/import wizards, server tuning, and global styling components

This commit is contained in:
Ümit Tunç
2026-04-03 15:55:09 +03:00
parent 4932e18553
commit fe25ec5b38
10 changed files with 530 additions and 135 deletions
+16 -5
View File
@@ -56,12 +56,22 @@ header img.logo {
} }
/* For automatic logo switching in dark mode */ /* For automatic logo switching in dark mode */
.logo-light { display: none; } .logo-light {
.logo-dark { display: block; } display: none;
}
.logo-dark {
display: block;
}
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
.logo-dark { display: none; } .logo-dark {
.logo-light { display: block; } display: none;
}
.logo-light {
display: block;
}
} }
h1 { h1 {
@@ -77,7 +87,8 @@ table {
margin-top: 1rem; margin-top: 1rem;
} }
th, td { th,
td {
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
text-align: left; text-align: left;
border-bottom: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
+44 -65
View File
@@ -389,11 +389,19 @@ function App(): JSX.Element {
} }
}, [logs, logDialogOpen]) }, [logs, logDialogOpen])
const handleOpenNginxConfig = async () => {
if (window.api && window.api.invoke) {
const config = await window.api.invoke('nginx:config:get')
setNginxConfig(config)
setIsNginxConfigOpen(true)
}
}
const handleSaveNginxConfig = async () => { const handleSaveNginxConfig = async () => {
if (window.api && window.api.invoke) { if (window.api && window.api.invoke) {
const result = await window.api.invoke('nginx:config:save', nginxConfig) const result = await window.api.invoke('nginx:config:save', nginxConfig)
if (result.success) { if (result.success) {
Swal.fire({ title: t('common.success'), text: t('server.nginx.config_saved'), icon: 'success', background: '#1e1e1e', color: '#fff' }) Swal.fire({ title: t('common.success'), text: t('server.nginx.config_saved'), icon: 'success' })
setIsNginxConfigOpen(false) setIsNginxConfigOpen(false)
} }
} }
@@ -411,7 +419,7 @@ function App(): JSX.Element {
if (window.api && window.api.invoke) { if (window.api && window.api.invoke) {
const result = await window.api.invoke('apache:config:save', apacheConfig) const result = await window.api.invoke('apache:config:save', apacheConfig)
if (result.success) { if (result.success) {
Swal.fire({ title: t('common.success'), text: t('server.apache.config_saved'), icon: 'success', background: '#1e1e1e', color: '#fff' }) Swal.fire({ title: t('common.success'), text: t('server.apache.config_saved'), icon: 'success' })
setIsApacheConfigOpen(false) setIsApacheConfigOpen(false)
} }
} }
@@ -420,7 +428,7 @@ function App(): JSX.Element {
const handleTestApacheConfig = async () => { const handleTestApacheConfig = async () => {
if (window.api && window.api.invoke) { if (window.api && window.api.invoke) {
const result = await window.api.invoke('apache:config:test') const result = await window.api.invoke('apache:config:test')
Swal.fire({ title: result.success ? t('common.success') : t('common.error'), text: result.message, icon: result.success ? 'success' : 'error', background: '#1e1e1e', color: '#fff' }) Swal.fire({ title: result.success ? t('common.success') : t('common.error'), text: result.message, icon: result.success ? 'success' : 'error' })
} }
} }
@@ -462,32 +470,36 @@ function App(): JSX.Element {
const updateSwal = (currentSrv?: string) => { const updateSwal = (currentSrv?: string) => {
const html = ` const html = `
<div style="text-align: left; font-family: 'Inter', sans-serif; padding-top: 10px;"> <div class="swal-service-list">
${services.map(srv => { ${services.map(srv => {
const label = getDisplayLabel(srv); const label = getDisplayLabel(srv);
const res = results[srv]; const res = results[srv];
let icon = '<span style="color: rgba(255,255,255,0.1); margin-right: 12px; font-size: 0.8rem;">○</span>'; let iconClass = 'swal-status-icon';
let statusColor = 'rgba(255,255,255,0.4)'; let noteClass = 'swal-status-note';
let note = operation === 'start' ? t('common.ready') : t('common.running'); let note = operation === 'start' ? t('common.ready') : t('common.running');
if (res?.success) { if (res?.success) {
icon = '<span style="color: #33d9b2; margin-right: 12px; font-size: 0.8rem;">●</span>'; iconClass += ' success';
statusColor = '#33d9b2'; noteClass += ' success';
note = operation === 'start' ? t('common.started') : t('common.stopped'); note = operation === 'start' ? t('common.started') : t('common.stopped');
} else if (res?.error) { } else if (res?.error) {
icon = '<span style="color: #ff5252; margin-right: 12px; font-size: 0.8rem;">●</span>'; iconClass += ' error';
statusColor = '#ff5252'; noteClass += ' error';
note = t('common.error'); note = t('common.error');
} else if (srv === currentSrv) { } else if (srv === currentSrv) {
icon = '<span style="color: #39A7FF; margin-right: 12px; font-size: 0.8rem;" class="swal2-loading-dots">●</span>'; iconClass += ' loading swal2-loading-dots';
statusColor = '#39A7FF'; noteClass += ' loading';
note = operation === 'start' ? t('common.starting') : t('common.stopping'); note = operation === 'start' ? t('common.starting') : t('common.stopping');
} }
return '<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; padding: 8px 14px; background: rgba(0,0,0,0.2); border: 1px solid rgba(255,255,255,0.03); border-radius: 8px; font-size: 0.8rem;">' + return `
'<span style="font-weight: 600; display: flex; align-items: center;">' + icon + ' ' + label + '</span>' + <div class="swal-service-item">
'<span style="font-size: 0.65rem; color: ' + statusColor + '; font-weight: 800; letter-spacing: 0.5px;">' + note.toUpperCase() + '</span>' + <span class="swal-service-label">
'</div>'; <span class="${iconClass}"></span> ${label}
</span>
<span class="${noteClass}">${note.toUpperCase()}</span>
</div>
`;
}).join('')} }).join('')}
</div> </div>
`; `;
@@ -499,8 +511,6 @@ function App(): JSX.Element {
html: '<div></div>', html: '<div></div>',
allowOutsideClick: false, allowOutsideClick: false,
showConfirmButton: false, showConfirmButton: false,
background: '#1a1d21',
color: '#fff',
customClass: { customClass: {
title: 'swal-title-small' title: 'swal-title-small'
}, },
@@ -598,9 +608,7 @@ function App(): JSX.Element {
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonText: t('common.yes_delete'), confirmButtonText: t('common.yes_delete'),
cancelButtonText: t('common.cancel'), cancelButtonText: t('common.cancel')
background: '#1e1e1e',
color: '#fff'
}) })
if (result.isConfirmed) { if (result.isConfirmed) {
@@ -639,8 +647,6 @@ function App(): JSX.Element {
showCancelButton: !result.success && !!errorInfo?.host, showCancelButton: !result.success && !!errorInfo?.host,
confirmButtonText: !result.success && errorInfo?.host ? t('common.go_to_fix') : t('common.ok'), confirmButtonText: !result.success && errorInfo?.host ? t('common.go_to_fix') : t('common.ok'),
cancelButtonText: t('common.close'), cancelButtonText: t('common.close'),
background: '#1e1e1e',
color: '#fff',
width: '600px' width: '600px'
}).then((alertResult) => { }).then((alertResult) => {
if (alertResult.isConfirmed && !result.success && errorInfo?.host) { if (alertResult.isConfirmed && !result.success && errorInfo?.host) {
@@ -667,10 +673,7 @@ function App(): JSX.Element {
Swal.fire({ Swal.fire({
title: t('common.success'), title: t('common.success'),
text: t('server.nginx.config_saved'), text: t('server.nginx.config_saved'),
icon: 'success', icon: 'success'
background: '#1e1e1e',
color: '#fff',
customClass: { popup: 'swal2-dark-popup' }
}) })
setIsProjectNginxConfigOpen(false) setIsProjectNginxConfigOpen(false)
} }
@@ -762,9 +765,7 @@ function App(): JSX.Element {
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true,
confirmButtonText: t('mariadb_process_monitor.kill_btn'), confirmButtonText: t('mariadb_process_monitor.kill_btn'),
cancelButtonText: t('common.cancel'), cancelButtonText: t('common.cancel')
background: '#1a1d21',
color: '#fff'
}) })
if (result.isConfirmed) { if (result.isConfirmed) {
@@ -789,13 +790,7 @@ function App(): JSX.Element {
maxWidth="lg" maxWidth="lg"
fullWidth fullWidth
PaperProps={{ PaperProps={{
sx: { className: 'mui-custom-dialog-paper'
bgcolor: '#1a1d21',
backgroundImage: 'none',
color: '#fff',
borderRadius: 2,
border: '1px solid rgba(255,255,255,0.1)'
}
}} }}
> >
<DialogTitle sx={{ borderBottom: '1px solid rgba(255,255,255,0.1)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <DialogTitle sx={{ borderBottom: '1px solid rgba(255,255,255,0.1)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
@@ -1167,7 +1162,12 @@ function App(): JSX.Element {
</Box> </Box>
)} )}
{v.status === 'installed' && ( {v.status === 'installed' && (
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
<Button variant="outlined" size="small" startIcon={<SettingsIcon />} onClick={handleOpenNginxConfig}>
{t('common.settings').toUpperCase()}
</Button>
<Chip label={t('common.ok').toUpperCase()} color="success" variant="outlined" size="small" /> <Chip label={t('common.ok').toUpperCase()} color="success" variant="outlined" size="small" />
</Box>
)} )}
</Box> </Box>
</ListItem> </ListItem>
@@ -1920,11 +1920,7 @@ function App(): JSX.Element {
maxWidth="lg" maxWidth="lg"
fullWidth fullWidth
PaperProps={{ PaperProps={{
sx: { className: 'mui-custom-dialog-paper'
bgcolor: '#1a1d21',
color: '#fff',
borderRadius: 2
}
}} }}
> >
<DialogTitle sx={{ borderBottom: '1px solid rgba(255,255,255,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}> <DialogTitle sx={{ borderBottom: '1px solid rgba(255,255,255,0.05)', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
@@ -2016,12 +2012,7 @@ function App(): JSX.Element {
maxWidth="md" maxWidth="md"
fullWidth fullWidth
PaperProps={{ PaperProps={{
sx: { className: 'mui-custom-dialog-paper'
bgcolor: '#1e1e1e',
color: '#fff',
borderRadius: 3,
border: '1px solid rgba(255,255,255,0.1)'
}
}} }}
> >
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
@@ -2067,12 +2058,7 @@ function App(): JSX.Element {
maxWidth="md" maxWidth="md"
fullWidth fullWidth
PaperProps={{ PaperProps={{
sx: { className: 'mui-custom-dialog-paper'
bgcolor: '#1e1e1e',
color: '#fff',
borderRadius: 3,
border: '1px solid rgba(255,255,255,0.1)'
}
}} }}
> >
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
@@ -2112,16 +2098,14 @@ function App(): JSX.Element {
${formattedMessage} ${formattedMessage}
</div> </div>
${!result.success && errorInfo ? ` ${!result.success && errorInfo ? `
<div style="margin-top: 15px; text-align: center;"> <div class="swal-error-footer">
<p style="font-size: 0.9rem; color: #ffb142;"> <p class="swal-error-line">
<b>{t('server.project_nginx.error_line', { line: errorInfo.line })}</b> <b>${t('server.project_nginx.error_line', { line: errorInfo.line })}</b>
</p> </p>
</div> </div>
` : ''} ` : ''}
`, `,
icon: result.success ? 'success' : 'error', icon: result.success ? 'success' : 'error',
background: '#1e1e1e',
color: '#fff',
width: '600px' width: '600px'
}) })
} }
@@ -2150,12 +2134,7 @@ function App(): JSX.Element {
maxWidth="md" maxWidth="md"
fullWidth fullWidth
PaperProps={{ PaperProps={{
sx: { className: 'mui-custom-dialog-paper'
bgcolor: '#1e1e1e',
color: '#fff',
borderRadius: 3,
border: '1px solid rgba(255,255,255,0.1)'
}
}} }}
> >
<DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <DialogTitle sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
@@ -88,8 +88,6 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
input: 'text', input: 'text',
inputLabel: t('mariadb_manager.db_name_label'), inputLabel: t('mariadb_manager.db_name_label'),
showCancelButton: true, showCancelButton: true,
background: '#1e1e1e',
color: '#fff',
inputValidator: (value) => { inputValidator: (value) => {
if (!value) return t('common.error_required') if (!value) return t('common.error_required')
return null return null
@@ -99,10 +97,10 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
if (dbName) { if (dbName) {
const result = await window.api.invoke('mariadb:create-db', { versionId: selectedVersion, dbName }) const result = await window.api.invoke('mariadb:create-db', { versionId: selectedVersion, dbName })
if (result.success) { if (result.success) {
Swal.fire({ icon: 'success', title: t('common.ok'), background: '#1e1e1e', color: '#fff' }) Swal.fire({ icon: 'success', title: t('common.ok') })
fetchData() fetchData()
} else { } else {
Swal.fire({ icon: 'error', title: t('common.error'), text: result.message, background: '#1e1e1e', color: '#fff' }) Swal.fire({ icon: 'error', title: t('common.error'), text: result.message })
} }
} }
} }
@@ -112,9 +110,7 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
title: t('common.are_you_sure'), title: t('common.are_you_sure'),
text: t('mariadb_manager.delete_db_confirm', { db: dbName }), text: t('mariadb_manager.delete_db_confirm', { db: dbName }),
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true
background: '#1e1e1e',
color: '#fff'
}) })
if (result.isConfirmed) { if (result.isConfirmed) {
@@ -122,7 +118,7 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
if (res.success) { if (res.success) {
fetchData() fetchData()
} else { } else {
Swal.fire({ icon: 'error', title: t('common.error'), text: res.message, background: '#1e1e1e', color: '#fff' }) Swal.fire({ icon: 'error', title: t('common.error'), text: res.message })
} }
} }
} }
@@ -135,8 +131,6 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
`<input id="swal-host" class="swal2-input" placeholder="Host" value="%">` + `<input id="swal-host" class="swal2-input" placeholder="Host" value="%">` +
`<input id="swal-pass" type="password" class="swal2-input" placeholder="${t('mariadb_manager.password_label')}">`, `<input id="swal-pass" type="password" class="swal2-input" placeholder="${t('mariadb_manager.password_label')}">`,
focusConfirm: false, focusConfirm: false,
background: '#1e1e1e',
color: '#fff',
preConfirm: () => { preConfirm: () => {
const user = (document.getElementById('swal-user') as HTMLInputElement).value const user = (document.getElementById('swal-user') as HTMLInputElement).value
const host = (document.getElementById('swal-host') as HTMLInputElement).value const host = (document.getElementById('swal-host') as HTMLInputElement).value
@@ -152,10 +146,10 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
if (formValues) { if (formValues) {
const result = await window.api.invoke('mariadb:create-user', { versionId: selectedVersion, ...formValues }) const result = await window.api.invoke('mariadb:create-user', { versionId: selectedVersion, ...formValues })
if (result.success) { if (result.success) {
Swal.fire({ icon: 'success', title: t('common.ok'), background: '#1e1e1e', color: '#fff' }) Swal.fire({ icon: 'success', title: t('common.ok') })
fetchData() fetchData()
} else { } else {
Swal.fire({ icon: 'error', title: t('common.error'), text: result.message, background: '#1e1e1e', color: '#fff' }) Swal.fire({ icon: 'error', title: t('common.error'), text: result.message })
} }
} }
} }
@@ -165,9 +159,7 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
title: t('common.are_you_sure'), title: t('common.are_you_sure'),
text: t('mariadb_manager.delete_user_confirm', { user: `${user}@${host}` }), text: t('mariadb_manager.delete_user_confirm', { user: `${user}@${host}` }),
icon: 'warning', icon: 'warning',
showCancelButton: true, showCancelButton: true
background: '#1e1e1e',
color: '#fff'
}) })
if (result.isConfirmed) { if (result.isConfirmed) {
@@ -175,7 +167,7 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
if (res.success) { if (res.success) {
fetchData() fetchData()
} else { } else {
Swal.fire({ icon: 'error', title: t('common.error'), text: res.message, background: '#1e1e1e', color: '#fff' }) Swal.fire({ icon: 'error', title: t('common.error'), text: res.message })
} }
} }
} }
@@ -272,7 +272,15 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
} }
return ( return (
<Dialog open={open} onClose={loading ? undefined : onClose} maxWidth="sm" fullWidth> <Dialog
open={open}
onClose={loading ? undefined : onClose}
maxWidth="sm"
fullWidth
PaperProps={{
className: 'mui-custom-dialog-paper'
}}
>
<DialogTitle>{t('mariadb.export.wizard_title')}</DialogTitle> <DialogTitle>{t('mariadb.export.wizard_title')}</DialogTitle>
<DialogContent> <DialogContent>
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}> <Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
@@ -316,7 +316,15 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
} }
return ( return (
<Dialog open={open} onClose={loading ? undefined : onClose} maxWidth="sm" fullWidth> <Dialog
open={open}
onClose={loading ? undefined : onClose}
maxWidth="sm"
fullWidth
PaperProps={{
className: 'mui-custom-dialog-paper'
}}
>
<DialogTitle>{t('mariadb.import.wizard_title')}</DialogTitle> <DialogTitle>{t('mariadb.import.wizard_title')}</DialogTitle>
<DialogContent> <DialogContent>
<Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}> <Stepper activeStep={activeStep} orientation="horizontal" sx={{ pt: 2, pb: 4 }}>
@@ -79,9 +79,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
allowOutsideClick: false, allowOutsideClick: false,
didOpen: () => { didOpen: () => {
Swal.showLoading() Swal.showLoading()
}, }
background: '#1e1e1e',
color: '#fff'
}) })
try { try {
@@ -95,15 +93,13 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
text: t('php.extensions.success_msg'), text: t('php.extensions.success_msg'),
icon: 'success', icon: 'success',
timer: 2000, timer: 2000,
showConfirmButton: false, showConfirmButton: false
background: '#1e1e1e',
color: '#fff'
}) })
setIsRawEditorOpen(false) setIsRawEditorOpen(false)
fetchExtensions() fetchExtensions()
} }
} catch (error: any) { } catch (error: any) {
Swal.fire({ title: 'Hata', text: error.message, icon: 'error', background: '#1e1e1e', color: '#fff' }) Swal.fire({ title: 'Hata', text: error.message, icon: 'error' })
} finally { } finally {
setIsSavingRaw(false) setIsSavingRaw(false)
} }
@@ -131,9 +127,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
allowOutsideClick: false, allowOutsideClick: false,
didOpen: () => { didOpen: () => {
Swal.showLoading() Swal.showLoading()
}, }
background: '#1e1e1e',
color: '#fff'
}) })
} }
@@ -147,10 +141,10 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
Swal.close() Swal.close()
} }
} else { } else {
Swal.fire({ title: t('common.error'), text: result.message, icon: 'error', background: '#1e1e1e', color: '#fff' }) Swal.fire({ title: t('common.error'), text: result.message, icon: 'error' })
} }
} catch (error: any) { } catch (error: any) {
Swal.fire({ title: t('common.error'), text: error.message, icon: 'error', background: '#1e1e1e', color: '#fff' }) Swal.fire({ title: t('common.error'), text: error.message, icon: 'error' })
} finally { } finally {
setSaving(null) setSaving(null)
} }
@@ -169,13 +163,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
maxWidth="sm" maxWidth="sm"
fullWidth fullWidth
PaperProps={{ PaperProps={{
sx: { className: 'mui-custom-dialog-paper'
bgcolor: '#1e1e1e',
color: '#fff',
backgroundImage: 'none',
borderRadius: 2,
border: '1px solid rgba(255,255,255,0.1)'
}
}} }}
> >
<DialogTitle sx={{ m: 0, p: 1.5, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <DialogTitle sx={{ m: 0, p: 1.5, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
@@ -280,13 +268,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
maxWidth="md" maxWidth="md"
fullWidth fullWidth
PaperProps={{ PaperProps={{
sx: { className: 'mui-custom-dialog-paper'
bgcolor: '#1a1a1a',
color: '#fff',
borderRadius: 2,
backgroundImage: 'none',
border: '1px solid rgba(255,255,255,0.1)'
}
}} }}
> >
<DialogTitle sx={{ display: 'flex', flexDirection: 'column', gap: 0.5, p: 1.5 }}> <DialogTitle sx={{ display: 'flex', flexDirection: 'column', gap: 0.5, p: 1.5 }}>
@@ -150,9 +150,7 @@ export default function ProjectDetails({
toast: true, toast: true,
position: 'top-end', position: 'top-end',
showConfirmButton: false, showConfirmButton: false,
timer: 3000, timer: 3000
background: '#1e1e1e',
color: '#fff'
}); });
onRefreshProjects(); onRefreshProjects();
} }
@@ -160,9 +158,7 @@ export default function ProjectDetails({
Swal.fire({ Swal.fire({
icon: 'error', icon: 'error',
title: t('common.error'), title: t('common.error'),
text: e.message, text: e.message
background: '#1e1e1e',
color: '#fff'
}); });
} finally { } finally {
setIsConfigSaving(false); setIsConfigSaving(false);
@@ -186,9 +182,7 @@ export default function ProjectDetails({
toast: true, toast: true,
position: 'top-end', position: 'top-end',
showConfirmButton: false, showConfirmButton: false,
timer: 3000, timer: 3000
background: '#1e1e1e',
color: '#fff'
}); });
if (onRefreshProjects) onRefreshProjects(); if (onRefreshProjects) onRefreshProjects();
} }
@@ -196,9 +190,7 @@ export default function ProjectDetails({
Swal.fire({ Swal.fire({
icon: 'error', icon: 'error',
title: t('common.error'), title: t('common.error'),
text: error.message, text: error.message
background: '#1e1e1e',
color: '#fff'
}); });
} finally { } finally {
setIsSaving(false); setIsSaving(false);
+1 -7
View File
@@ -337,13 +337,7 @@ export default function ServerTuning() {
maxWidth="sm" maxWidth="sm"
fullWidth fullWidth
PaperProps={{ PaperProps={{
sx: { className: 'mui-custom-dialog-paper'
bgcolor: '#1a1d21',
backgroundImage: 'none',
color: '#fff',
borderRadius: 4,
border: '1px solid rgba(255,255,255,0.1)'
}
}} }}
> >
<DialogTitle sx={{ borderBottom: '1px solid rgba(255,255,255,0.1)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <DialogTitle sx={{ borderBottom: '1px solid rgba(255,255,255,0.1)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
+360
View File
@@ -0,0 +1,360 @@
{
"common": {
"actions": "Действие",
"start": "Запустить",
"stop": "Остановить",
"started": "Запущено",
"restart": "Перезапустить",
"settings": "Настройки",
"tools": "Инструменты",
"close": "Закрыть",
"quit": "Выход",
"save": "Сохранить",
"user_guide": "Руководство пользователя",
"port": "Порт",
"all": "Все",
"export": "Экспорт",
"import": "Импорт",
"ok": "ОК",
"cancel": "Отмена",
"error": "Ошибка!",
"success": "Успешно!",
"warning": "Предупреждение",
"info": "Информация",
"delete": "Удалить",
"edit": "Редактировать",
"refresh": "Обновить",
"search": "Поиск",
"loading": "Загрузка...",
"starting": "Запуск...",
"running": "Работает",
"stopped": "Остановлено",
"stopping": "Остановка...",
"ready": "Готово.",
"waiting_process": "Ожидание процесса...",
"executable_not_found": "Файл не найден",
"are_you_sure": "Вы уверены?",
"yes_delete": "Да, удалить",
"go_to_fix": "Перейти к исправлению",
"select": "ВЫБРАТЬ",
"next": "ДАЛЕЕ",
"back": "НАЗАД",
"finish": "ЗАВЕРШИТЬ",
"download": "СКАЧАТЬ",
"available": "Доступно",
"reset": "Сбросить",
"copy": "Копировать",
"browse": "Обзор",
"delete_confirm": "Вы уверены, что хотите удалить этот проект?",
"stop_status": "Остановка",
"reset_confirm_title": "Вы уверены?",
"reset_confirm_text": "Вы хотите сбросить данные {{service}}? Это действие удалит все данные и выполнит чистую установку.",
"reset_btn": "Да, сбросить",
"operating": "Операция выполняется...",
"please_wait": "Пожалуйста, подождите...",
"status": "Статус"
},
"dashboard": {
"title": "Панель управления",
"status": "Статус сервера",
"services": "Сервисы",
"active_version": "Активная версия PHP",
"quick_look": "Быстрый обзор",
"start_all": "Запустить все",
"stop_all": "Остановить все",
"starting_all": "Запуск всех сервисов",
"stopping_all": "Остановка всех сервисов",
"all_started": "Все сервисы запущены.",
"all_stopped": "Все сервисы остановлены.",
"overview": "Обзор",
"service_settings": "Настройки сервиса",
"project_settings": "Настройки проекта",
"select_service": "Пожалуйста, выберите сервис",
"system_stats": "СИСТЕМНАЯ СТАТИСТИКА",
"service_info": "ИНФОРМАЦИЯ О СЕРВИСЕ"
},
"projects": {
"title": "Мои проекты",
"add_new": "Добавить новый проект",
"search": "Поиск проекта или хоста...",
"edit": "Редактировать проект",
"empty": "Проекты пока не добавлены.",
"name": "Название проекта",
"path": "Путь к папке",
"host": "Хост / Домен",
"hostname_placeholder": "Имя хоста (напр: local.test)",
"php_version": "Версия PHP",
"mariadb_version": "Версия MariaDB",
"server_type": "Веб-сервер",
"apache_htaccess": "Apache (с поддержкой htaccess)",
"actions": "Действия",
"no_php_warning": "Сначала необходимо скачать версию PHP",
"added": "Проект успешно добавлен.",
"updated": "Проект успешно обновлен.",
"deleted": "Проект удален.",
"delete_confirm": "Вы уверены, что хотите удалить этот проект? Ваши файлы не будут удалены, проект просто исчезнет из списка.",
"browse": "Обзор",
"overview": "Обзор",
"database": "База данных",
"site_domain": "Адрес сайта",
"web_server": "Веб-сервер",
"site_path": "Путь к файлам",
"open_site": "ОТКРЫТЬ САЙТ",
"stop_site": "ОСТАНОВИТЬ САЙТ",
"start_site": "ЗАПУСТИТЬ САЙТ",
"no_site_selected": "Пожалуйста, сначала выберите сайт.",
"server_settings": "НАСТРОЙКИ СЕРВЕРА",
"manual_edit_warning": "При ручном редактировании этих настроек в начало добавляется # MANUAL_EDIT, и система прекращает автоматические обновления.",
"no_project_selected": "Пожалуйста, выберите проект из списка или создайте новый."
},
"settings": {
"general": "Общие настройки",
"saved": "Настройки сохранены.",
"save_failed": "Не удалось сохранить настройки.",
"language": "Язык",
"nginx_port": "Порт Nginx",
"apache_port": "Порт Apache",
"php_port": "Порт PHP-FPM",
"mariadb_port": "Порт MariaDB",
"remote_access": "Разрешить удаленный доступ",
"server_type_select": "Тип сервера по умолчанию",
"php_versions": "Версии PHP",
"mariadb_versions": "Версии MariaDB",
"nginx_versions": "Версии Nginx",
"apache_versions": "Версии Apache",
"php_versions_desc": "Здесь вы можете скачивать и управлять необходимыми версиями PHP.",
"mariadb_versions_desc": "Управление версиями MariaDB.",
"nginx_versions_desc": "Управление версиями Nginx.",
"apache_versions_desc": "Управление версиями Apache.",
"nginx_port_desc": "Порт, на котором будет работать Nginx (по умолчанию: 80)",
"cli_env": "CLI и переменные окружения",
"cli_env_desc": "Управляйте настройками PATH, чтобы запускать команды PHP и MariaDB напрямую из любого терминала (CMD, PowerShell).",
"cli_enabled": "Интеграция с системным PATH активна",
"cli_enabled_desc": "Если включено, папка 'bin' Multi-PHP будет добавлена в переменную PATH пользователя.",
"cli_default_php": "Версия PHP по умолчанию (php)",
"cli_default_php_desc": "Выберите, какая версия будет использоваться при выполнении команды 'php'.",
"cli_default_mariadb": "Версия MariaDB по умолчанию (mysql)",
"cli_default_mariadb_desc": "Версия по умолчанию для команд 'mysql' или 'mariadb'.",
"cli_sync_btn": "Синхронизировать команды",
"cli_path_found": "Настройка PATH найдена:",
"cli_path_not_found": "Настройка PATH еще не выполнена",
"cli_sync_success": "Команды CLI успешно обновлены.",
"cli_aliases_title": "Созданные команды (псевдонимы)",
"cli_restart_warning": "Внимание: Чтобы изменения PATH вступили в силу, необходимо закрыть и снова открыть окна терминала/CMD.",
"cli_table_command": "Команда",
"cli_table_version": "Вызываемая версия",
"cli_table_path": "Путь к файлу"
},
"server": {
"nginx": {
"title": "Nginx",
"config": "Настройки Nginx",
"config_title": "Конфигурация Nginx (nginx.conf)",
"config_warning": "Внимание: Неправильная конфигурация может остановить работу Nginx. Протестируйте перед сохранением.",
"test_config": "Проверить конфигурацию",
"save_reload": "Сохранить и перезагрузить",
"config_saved": "Конфигурация Nginx успешно сохранена.",
"test_failed": "Ошибка конфигурации"
},
"apache": {
"title": "Apache",
"config": "Настройки Apache",
"config_title": "Конфигурация Apache (httpd.conf)",
"config_desc": "Этот файл является основным (глобальным) конфигурационным файлом сервера Apache.",
"test_config": "Проверить конфигурацию",
"save_reload": "Сохранить и перезагрузить",
"config_saved": "Конфигурация Apache сохранена."
},
"project_nginx": {
"title": "Конфигурация Nginx для {{host}}",
"tip": "Совет: Эта конфигурация применима только к проекту {{host}}.",
"error_line": "Строка ошибки: {{line}}"
}
},
"pma": {
"ready_desc": "phpMyAdmin готов к управлению базами данных.",
"install_desc": "Установите phpMyAdmin для управления базами данных через веб-интерфейс.",
"installing": "УСТАНОВКА...",
"install_button": "НАЧАТЬ УСТАНОВКУ",
"open": "ОТКРЫТЬ",
"progress": "Загрузка... %{{progress}}"
},
"mariadb": {
"import": {
"title": "Импорт данных MariaDB",
"desc": "Быстро загрузите выбранный .sql файл или архив в базу данных.",
"button": "ИМПОРТ",
"wizard_title": "Мастер быстрого дампа/импорта MariaDB",
"step1": "Выбор данных",
"step2": "Выбор сервера",
"step3": "Конфигурация базы данных",
"step4": "Итог и выполнение",
"step1_desc": "Пожалуйста, выберите файл .sql или архив (.zip, .tar.gz) для загрузки.",
"step2_desc": "Выберите сервер MariaDB, на который будет загружен файл.",
"step3_desc": "Настройте параметры базы данных и пользователя.",
"no_file": "Файл не выбран...",
"root_pass": "Пароль Root (если есть)",
"root_pass_helper": "Введите текущий пароль пользователя 'root' в MariaDB. Оставьте пустым, если пароля нет.",
"db_name": "Имя базы данных",
"db_user": "Пользователь БД",
"db_pass": "Пароль пользователя",
"summary": "Итоговая информация",
"file": "Файл",
"server": "Сервер",
"db": "База данных",
"user": "Пользователь",
"success_title": "Успешно!",
"error_title": "Произошла ошибка!",
"access_info": "Информация для доступа",
"host": "Хост",
"starting": "[Система] Процесс запущен...",
"overwrite_title": "База данных уже существует!",
"overwrite_desc": "База данных '{{dbName}}' уже существует. Хотите перезаписать её? Все текущие данные будут удалены!",
"btn_summary": "ПОСМОТРЕТЬ ИТОГ",
"btn_start": "НАЧАТЬ ИМПОРТ"
},
"export": {
"title": "Экспорт данных MariaDB",
"desc": "Безопасно создавайте резервные копии баз данных в форматах .sql или .sql.gz.",
"button": "ЭКСПОРТ",
"wizard_title": "Мастер экспорта базы данных MariaDB",
"step1": "Выбор сервера",
"step2": "Выбор базы данных",
"step3": "Настройки экспорта",
"step4": "Итог и выполнение",
"step1_desc": "Выберите сервер MariaDB, на котором находится база данных.",
"step2_desc": "Выберите базу данных для экспорта.",
"step3_desc": "Укажите место сохранения и формат файла.",
"placeholder": "Файл для сохранения...",
"compress": "Сжать с помощью Gzip (.sql.gz)",
"target": "Цель",
"compression": "Сжатие",
"on": "Включено (.gz)",
"off": "Выключено (.sql)",
"btn_show_file": "ПОКАЗАТЬ ФАЙЛ",
"btn_start": "НАЧАТЬ ЭКСПОРТ",
"starting": "[Система] Процесс экспорта запущен...",
"no_db_found": "На выбранном сервере не найдено доступных баз данных.",
"list_error": "Не удалось получить список баз данных: "
},
"wizard": {
"import_title": "Мастер импорта данных MariaDB",
"export_title": "Мастер экспорта данных MariaDB",
"select_version": "Выберите версию MariaDB",
"select_db": "Выберите базу данных",
"select_file": "Выберите файл",
"import_success": "Данные успешно импортированы.",
"export_success": "Данные успешно экспортированы."
}
},
"diagnostics": {
"logs": "Логи ошибок",
"noLogs": "Записей в логах пока нет."
},
"php": {
"extensions": {
"title": "Расширения PHP {{version}}",
"desc": "Управляйте расширениями через php.ini",
"search_placeholder": "Поиск расширения... (mysql, gd, mbstring...)",
"search_file_placeholder": "Поиск в файле...",
"raw_editor": "RAW РЕДАКТОР",
"none": "Расширения отсутствуют.",
"not_found": "Расширение не найдено.",
"active": "Активно",
"ini_edit": "Редактировать php.ini ({{version}})",
"file_open_tip": "Открыть файл в системе",
"saving": "Сохранение конфигурации",
"restarting": "Перезапуск сервиса PHP...",
"adjusting": "Настройка расширения",
"adjusting_desc": "{{extName}}: режим {{mode}}, выполняется перезапуск PHP...",
"save_restart_btn": "СОХРАНИТЬ И ПЕРЕЗАПУСТИТЬ",
"saving_btn": "СОХРАНЕНИЕ...",
"save_error": "Ошибка при сохранении расширений.",
"success_msg": "Конфигурация сохранена, сервис перезапущен.",
"prev_match": "Пред. (Shift+Enter)",
"next_match": "След. (Enter)"
}
},
"mariadb_process_monitor": {
"title": "Список процессов БД",
"auto_refresh_desc": "Список процессов обновляется автоматически каждые 3 секунды.",
"kill_btn": "KILL",
"kill_confirm": "Вы уверены, что хотите завершить этот процесс?",
"refresh_btn": "Обновить",
"click_to_view_sql": "Нажмите, чтобы просмотреть SQL-запрос",
"query_details": "Детали запроса",
"no_processes": "Активных процессов не найдено.",
"columns": {
"id": "ID",
"user": "Пользователь",
"host": "Хост",
"db": "База данных",
"command": "Команда",
"time": "Время",
"state": "Статус",
"info": "Информация",
"progress": "Прогресс"
}
},
"tuning": {
"title": "Оптимизация",
"desc": "Оптимизируйте производительность PHP и MariaDB с помощью критических лимитов ресурсов.",
"php_total": "Экземпляр PHP",
"mariadb_total": "Экземпляр MariaDB",
"tune_btn": "НАСТРОИТЬ",
"saving": "Применение изменений...",
"restart_notice": "Для вступления в силу некоторых изменений требуется перезапуск сервисов.",
"parameters": {
"memory_limit": "Лимит памяти",
"memory_limit_desc": "Максимальный объем памяти, который может использовать скрипт.",
"max_execution_time": "Макс. время выполнения",
"max_execution_time_desc": "Максимальное время работы скрипта в секундах.",
"post_max_size": "Макс. размер POST",
"post_max_size_desc": "Максимальный объем данных POST, принимаемый PHP.",
"upload_max_filesize": "Лимит загрузки файлов",
"upload_max_filesize_desc": "Максимальный размер загружаемого файла.",
"max_input_vars": "Макс. входных переменных",
"max_input_vars_desc": "Количество входных переменных (GET/POST/Cookie), которые могут быть приняты.",
"max_connections": "Макс. соединений",
"max_connections_desc": "Максимальное количество одновременных клиентских подключений.",
"innodb_buffer_pool_size": "Буферный пул InnoDB",
"innodb_buffer_pool_size_desc": "Критично для производительности. Хранит индексы и данные в памяти.",
"key_buffer_size": "Размер буфера ключей",
"key_buffer_size_desc": "Индексные блоки для таблиц MyISAM (общие для всех потоков).",
"max_allowed_packet": "Макс. размер пакета",
"max_allowed_packet_desc": "Максимальный размер одного SQL-запроса или результата."
}
},
"service_monitor": {
"title": "Логи сервисов",
"desc": "Отслеживайте историю запуска, остановки и ошибок сервисов.",
"event_log": "Журнал событий",
"clear_confirm": "Вы уверены, что хотите очистить всю историю событий?",
"chart_title": "График активности сервисов",
"no_events": "Событий пока не зарегистрировано.",
"columns": {
"service": "Сервис",
"action": "Действие",
"status": "Статус",
"timestamp": "Время",
"details": "Детали"
}
},
"mariadb_manager": {
"title": "Управление БД",
"databases_title": "Базы данных",
"users_title": "Пользователи",
"create_db_btn": "Новая база данных",
"create_db_title": "Создать новую базу данных",
"db_name_label": "Имя базы данных",
"db_name": "База данных",
"db_size": "Размер",
"create_user_btn": "Новый пользователь",
"create_user_title": "Создать нового пользователя",
"user_label": "Имя пользователя",
"password_label": "Пароль",
"delete_db_confirm": "Вы уверены, что хотите удалить базу данных '{{db}}'? Это действие необратимо!",
"delete_user_confirm": "Вы уверены, что хотите удалить пользователя '{{user}}'?",
"no_active_version": "Пожалуйста, сначала выберите установленную версию MariaDB или скачайте её."
}
}
+69
View File
@@ -104,6 +104,75 @@ const styles = {
padding: '10px !important', padding: '10px !important',
borderRadius: '5px !important', borderRadius: '5px !important',
border: '1px solid rgba(255,255,255,0.1) !important' border: '1px solid rgba(255,255,255,0.1) !important'
},
// Service List Alert Classes
'.swal-service-list': {
textAlign: 'left',
fontFamily: "'Inter', sans-serif",
paddingTop: '10px'
},
'.swal-service-item': {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '6px',
padding: '8px 14px',
background: 'rgba(0,0,0,0.2)',
border: '1px solid rgba(255,255,255,0.03)',
borderRadius: '8px',
fontSize: '0.8rem'
},
'.swal-service-label': {
fontWeight: 600,
display: 'flex',
alignItems: 'center'
},
'.swal-status-icon': {
marginRight: '12px',
fontSize: '0.8rem',
color: 'rgba(255,255,255,0.1)'
},
'.swal-status-icon.success': {
color: '#33d9b2'
},
'.swal-status-icon.error': {
color: '#ff5252'
},
'.swal-status-icon.loading': {
color: '#39A7FF'
},
'.swal-status-note': {
fontSize: '0.65rem',
fontWeight: 800,
letterSpacing: '0.5px',
textTransform: 'uppercase',
color: 'rgba(255,255,255,0.4)'
},
'.swal-status-note.success': {
color: '#33d9b2'
},
'.swal-status-note.error': {
color: '#ff5252'
},
'.swal-status-note.loading': {
color: '#39A7FF'
},
// Global Dialog Styling (for MUI PaperProps removal)
'.mui-custom-dialog-paper': {
background: '#1a1d21 !important',
backgroundImage: 'none !important',
color: '#fff !important',
borderRadius: '16px !important',
border: '1px solid rgba(255,255,255,0.1) !important',
boxShadow: '0 24px 48px rgba(0,0,0,0.5) !important'
},
'.swal-error-footer': {
marginTop: '15px !important',
textAlign: 'center !important'
},
'.swal-error-line': {
fontSize: '0.9rem !important',
color: '#ffb142 !important'
} }
} }