feat: implement MariaDB management, PHP extension control, and project details UI components

This commit is contained in:
Ümit Tunç
2026-04-03 16:02:14 +03:00
parent fe25ec5b38
commit 815e01004e
8 changed files with 220 additions and 162 deletions
+90 -85
View File
@@ -472,27 +472,27 @@ function App(): JSX.Element {
const html = `
<div class="swal-service-list">
${services.map(srv => {
const label = getDisplayLabel(srv);
const res = results[srv];
let iconClass = 'swal-status-icon';
let noteClass = 'swal-status-note';
let note = operation === 'start' ? t('common.ready') : t('common.running');
const label = getDisplayLabel(srv);
const res = results[srv];
let iconClass = 'swal-status-icon';
let noteClass = 'swal-status-note';
let note = operation === 'start' ? t('common.ready') : t('common.running');
if (res?.success) {
iconClass += ' success';
noteClass += ' success';
note = operation === 'start' ? t('common.started') : t('common.stopped');
} else if (res?.error) {
iconClass += ' error';
noteClass += ' error';
note = t('common.error');
} else if (srv === currentSrv) {
iconClass += ' loading swal2-loading-dots';
noteClass += ' loading';
note = operation === 'start' ? t('common.starting') : t('common.stopping');
}
if (res?.success) {
iconClass += ' success';
noteClass += ' success';
note = operation === 'start' ? t('common.started') : t('common.stopped');
} else if (res?.error) {
iconClass += ' error';
noteClass += ' error';
note = t('common.error');
} else if (srv === currentSrv) {
iconClass += ' loading swal2-loading-dots';
noteClass += ' loading';
note = operation === 'start' ? t('common.starting') : t('common.stopping');
}
return `
return `
<div class="swal-service-item">
<span class="swal-service-label">
<span class="${iconClass}"></span> ${label}
@@ -500,7 +500,7 @@ function App(): JSX.Element {
<span class="${noteClass}">${note.toUpperCase()}</span>
</div>
`;
}).join('')}
}).join('')}
</div>
`;
Swal.update({ html });
@@ -523,17 +523,17 @@ function App(): JSX.Element {
updateSwal(srv);
try {
const res = operation === 'start' ? await window.api.startService(srv) : await window.api.stopService(srv);
if (res.success) {
// Poll for status confirmation
let attempts = 0;
const maxAttempts = 15; // ~7.5 seconds
let confirmed = false;
while (attempts < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, 500));
const statusRes = await window.api.invoke('service:status', srv);
if (operation === 'start' && statusRes === 'running') {
confirmed = true;
break;
@@ -545,7 +545,7 @@ function App(): JSX.Element {
attempts++;
}
results[srv] = { success: confirmed, error: !confirmed ? t('common.timeout') : null };
} else {
results[srv] = { success: false, error: res.message };
@@ -558,7 +558,7 @@ function App(): JSX.Element {
const anyError = Object.values(results).some(r => r.error);
Swal.update({
icon: anyError ? 'warning' : 'success',
showConfirmButton: true,
@@ -571,7 +571,7 @@ function App(): JSX.Element {
if (Swal.isVisible()) Swal.close();
}, 3000);
}
fetchStatus();
};
@@ -784,10 +784,10 @@ function App(): JSX.Element {
return (
<>
<Dialog
open={!!selectedQuery}
onClose={() => setSelectedQuery(null)}
maxWidth="lg"
<Dialog
open={!!selectedQuery}
onClose={() => setSelectedQuery(null)}
maxWidth="lg"
fullWidth
PaperProps={{
className: 'mui-custom-dialog-paper'
@@ -802,24 +802,24 @@ function App(): JSX.Element {
<DialogContent sx={{ p: 0 }}>
<Box sx={{ p: 3, maxHeight: '70vh', overflow: 'auto' }}>
<pre style={{ margin: 0, whiteSpace: 'pre-wrap', wordBreak: 'break-all' }}>
<code
<code
className="language-sql"
dangerouslySetInnerHTML={{
dangerouslySetInnerHTML={{
__html: Prism.highlight(
(selectedQuery || '')
.replace(/\\r\\n/g, '\n')
.replace(/\\n/g, '\n')
.replace(/\\t/g, ' '),
Prism.languages.sql,
Prism.languages.sql,
'sql'
)
}}
)
}}
/>
</pre>
</Box>
</DialogContent>
<DialogActions sx={{ borderTop: '1px solid rgba(255,255,255,0.1)', p: 2 }}>
<Button
<Button
startIcon={<ContentCopyIcon />}
onClick={() => {
navigator.clipboard.writeText(selectedQuery || '')
@@ -854,17 +854,17 @@ function App(): JSX.Element {
>
<Toolbar sx={{ gap: 1.5, minHeight: '56px !important' }}>
<Box sx={{ flexGrow: 1 }} />
<Button
variant="outlined"
size="small"
<Button
variant="outlined"
size="small"
color="success"
onClick={handleStartAll}
startIcon={<StartIcon sx={{ fontSize: '1rem !important' }} />}
sx={{
borderRadius: 1.5,
fontSize: '0.7rem',
fontWeight: 800,
startIcon={<StartIcon sx={{ fontSize: 'var(--font-size-base) !important' }} />}
sx={{
borderRadius: 'var(--radius-md)',
fontSize: 'var(--font-size-xs)',
fontWeight: 'var(--font-weight-black)',
textTransform: 'none',
borderColor: 'rgba(76, 175, 80, 0.3)',
px: 1.5,
@@ -874,16 +874,16 @@ function App(): JSX.Element {
{t('dashboard.start_all')}
</Button>
<Button
variant="outlined"
size="small"
<Button
variant="outlined"
size="small"
color="error"
onClick={handleStopAll}
startIcon={<StopIcon sx={{ fontSize: '1rem !important' }} />}
sx={{
borderRadius: 1.5,
fontSize: '0.7rem',
fontWeight: 800,
startIcon={<StopIcon sx={{ fontSize: 'var(--font-size-base) !important' }} />}
sx={{
borderRadius: 'var(--radius-md)',
fontSize: 'var(--font-size-xs)',
fontWeight: 'var(--font-weight-black)',
textTransform: 'none',
borderColor: 'rgba(244, 67, 54, 0.3)',
px: 1.5,
@@ -894,7 +894,7 @@ function App(): JSX.Element {
</Button>
<Divider orientation="vertical" flexItem sx={{ mx: 0.5, borderColor: 'rgba(255,255,255,0.1)', height: 20, mt: 1.5 }} />
<FormControl size="small" variant="outlined" sx={{ minWidth: 70 }}>
<Select
value={i18n.language}
@@ -912,10 +912,10 @@ function App(): JSX.Element {
}}
>
{supportedLngs.map(lang => (
<MenuItem key={lang} value={lang} sx={{ fontSize: '0.75rem' }}>
<MenuItem key={lang} value={lang} className="text-sm">
{lang.toUpperCase()}
</MenuItem>
))}
))}
</Select>
</FormControl>
@@ -924,7 +924,7 @@ function App(): JSX.Element {
<TerminalIcon sx={{ fontSize: '1.2rem' }} />
</IconButton>
</Tooltip>
<IconButton size="small" color="inherit" onClick={() => setActiveTab('settings')} sx={{ p: 0.5, opacity: 0.7, '&:hover': { opacity: 1 } }}>
<SettingsIcon sx={{ fontSize: '1.2rem' }} />
</IconButton>
@@ -937,7 +937,12 @@ function App(): JSX.Element {
sx={{
width: drawerWidth,
flexShrink: 0,
[`& .MuiDrawer-paper`]: { width: drawerWidth, boxSizing: 'border-box', bgcolor: 'rgba(18, 18, 18, 0.95)', borderRight: '1px solid rgba(255, 255, 255, 0.05)' },
[`& .MuiDrawer-paper`]: {
width: drawerWidth,
boxSizing: 'border-box',
bgcolor: 'rgba(18, 18, 18, 0.95)',
borderRight: '1px solid var(--color-border-soft)'
},
}}
>
<Box sx={{ p: 2, display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '120px' }}>
@@ -949,37 +954,37 @@ function App(): JSX.Element {
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'dashboard'} onClick={() => setActiveTab('dashboard')} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 40 }}><DashboardIcon fontSize="small" color={activeTab === 'dashboard' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary={t('dashboard.title')} primaryTypographyProps={{ fontSize: '0.75rem', fontWeight: 500 }} />
<ListItemText primary={t('dashboard.title')} primaryTypographyProps={{ className: 'text-sm font-medium' }} />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'projects'} onClick={() => setActiveTab('projects')} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 40 }}><ProjectIcon fontSize="small" color={activeTab === 'projects' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary={t('projects.title')} primaryTypographyProps={{ fontSize: '0.75rem', fontWeight: 500 }} />
<ListItemText primary={t('projects.title')} primaryTypographyProps={{ className: 'text-sm font-medium' }} />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'tuning'} onClick={() => setActiveTab('tuning')} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 40 }}><TuningIcon fontSize="small" color={activeTab === 'tuning' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary={t('tuning.title')} primaryTypographyProps={{ fontSize: '0.75rem', fontWeight: 500 }} />
<ListItemText primary={t('tuning.title')} primaryTypographyProps={{ className: 'text-sm font-medium' }} />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'tools'} onClick={() => setActiveTab('tools')} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 40 }}><ToolsIcon fontSize="small" color={activeTab === 'tools' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary={t('common.tools')} primaryTypographyProps={{ fontSize: '0.75rem', fontWeight: 500 }} />
<ListItemText primary={t('common.tools')} primaryTypographyProps={{ className: 'text-sm font-medium' }} />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'db_manager'} onClick={() => setActiveTab('db_manager')} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 40 }}><DbIcon fontSize="small" color={activeTab === 'db_manager' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary={t('mariadb_manager.title')} primaryTypographyProps={{ fontSize: '0.75rem', fontWeight: 500 }} />
<ListItemText primary={t('mariadb_manager.title')} primaryTypographyProps={{ className: 'text-sm font-medium' }} />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'db_monitor'} onClick={() => setActiveTab('db_monitor')} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 40 }}><TerminalIcon fontSize="small" color={activeTab === 'db_monitor' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary={t('mariadb_process_monitor.title')} primaryTypographyProps={{ fontSize: '0.75rem', fontWeight: 500 }} />
<ListItemText primary={t('mariadb_process_monitor.title')} primaryTypographyProps={{ className: 'text-sm font-medium' }} />
</ListItemButton>
</ListItem>
</List>
@@ -988,19 +993,19 @@ function App(): JSX.Element {
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'user_guide'} onClick={() => setActiveTab('user_guide')} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 40 }}><GuideIcon fontSize="small" color={activeTab === 'user_guide' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary={t('common.user_guide')} primaryTypographyProps={{ fontSize: '0.75rem', fontWeight: 500 }} />
<ListItemText primary={t('common.user_guide')} primaryTypographyProps={{ className: 'text-sm font-medium' }} />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'settings'} onClick={() => setActiveTab('settings')} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 40 }}><SettingsIcon fontSize="small" color={activeTab === 'settings' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary={t('settings.general')} primaryTypographyProps={{ fontSize: '0.75rem', fontWeight: 500 }} />
<ListItemText primary={t('settings.general')} primaryTypographyProps={{ className: 'text-sm font-medium' }} />
</ListItemButton>
</ListItem>
<ListItem disablePadding>
<ListItemButton selected={activeTab === 'event_monitor'} onClick={() => setActiveTab('event_monitor')} sx={{ py: 0.5 }}>
<ListItemIcon sx={{ minWidth: 40 }}><HistoryIcon fontSize="small" color={activeTab === 'event_monitor' ? 'primary' : 'inherit'} /></ListItemIcon>
<ListItemText primary={t('service_monitor.title')} primaryTypographyProps={{ fontSize: '0.75rem', fontWeight: 500 }} />
<ListItemText primary={t('service_monitor.title')} primaryTypographyProps={{ className: 'text-sm font-medium' }} />
</ListItemButton>
</ListItem>
</List>
@@ -1284,13 +1289,13 @@ function App(): JSX.Element {
<Box>
<FormControlLabel
control={
<Switch
checked={cliConfig.enabled}
<Switch
checked={cliConfig.enabled}
onChange={async (e) => {
const newConf = { ...cliConfig, enabled: e.target.checked }
setCliConfig(newConf)
await window.api.invoke('cli:set-config', newConf)
}}
}}
/>
}
label={
@@ -1353,9 +1358,9 @@ function App(): JSX.Element {
{cliConfig.binDir || '...'}
</Typography>
</Box>
<Button
variant="outlined"
size="small"
<Button
variant="outlined"
size="small"
startIcon={<RefreshIcon />}
onClick={async () => {
await window.api.invoke('cli:sync')
@@ -1420,7 +1425,7 @@ function App(): JSX.Element {
)}
{activeTab === 'dashboard' && (
<DashboardManager
<DashboardManager
status={status}
settings={settings}
projects={projects}
@@ -1453,7 +1458,7 @@ function App(): JSX.Element {
value={selectedMariaDbId}
onChange={(e) => setSelectedMariaDbId(e.target.value)}
label={t('settings.mariadb_versions')}
sx={{
sx={{
color: '#fff',
'.MuiOutlinedInput-notchedOutline': { borderColor: 'rgba(255,255,255,0.1)' }
}}
@@ -1502,9 +1507,9 @@ function App(): JSX.Element {
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: proc.State === 'Query' ? '#39A7FF' : 'rgba(255,255,255,0.5)' }}>{proc.State || '-'}</TableCell>
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', color: 'rgba(255,255,255,0.7)', maxWidth: 300, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
<Tooltip title={t('mariadb_process_monitor.click_to_view_sql')}>
<Box
<Box
onClick={() => setSelectedQuery(proc.Info)}
sx={{
sx={{
cursor: proc.Info ? 'pointer' : 'default',
'&:hover': { color: proc.Info ? '#39A7FF' : 'inherit' },
transition: 'color 0.2s'
@@ -1635,7 +1640,7 @@ function App(): JSX.Element {
)}
{activeTab === 'projects' && (
<ProjectManager
<ProjectManager
projects={projects}
status={status}
settings={settings}
@@ -1931,9 +1936,9 @@ function App(): JSX.Element {
</Typography>
</Box>
<Box sx={{ display: 'flex', gap: 1 }}>
<Button
size="small"
startIcon={isProcessListLoading ? <CircularProgress size={16} /> : <RefreshIcon />}
<Button
size="small"
startIcon={isProcessListLoading ? <CircularProgress size={16} /> : <RefreshIcon />}
onClick={() => selectedMariaDbId && fetchProcessList(selectedMariaDbId)}
disabled={isProcessListLoading}
>
@@ -1987,10 +1992,10 @@ function App(): JSX.Element {
{p.Progress ? `${parseFloat(p.Progress).toFixed(2)}%` : '-'}
</TableCell>
<TableCell sx={{ borderColor: 'rgba(255,255,255,0.05)', textAlign: 'right' }}>
<Button
size="small"
color="error"
variant="text"
<Button
size="small"
color="error"
variant="text"
onClick={() => selectedMariaDbId && handleKillProcess(selectedMariaDbId, p.Id)}
sx={{ fontSize: '0.65rem', p: 0, minWidth: 'auto' }}
>
@@ -178,31 +178,32 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
<Stack direction="row" spacing={2} alignItems="center" justifyContent="space-between" sx={{ mb: 3 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<DbIcon color="primary" />
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>{t('mariadb_manager.title')}</Typography>
<Typography variant="h6" className="font-bold">{t('mariadb_manager.title')}</Typography>
</Box>
<Stack direction="row" spacing={1.5}>
<FormControl size="small" sx={{ minWidth: 160 }}>
<InputLabel id="version-select-label" sx={{ fontSize: '0.75rem' }}>{t('mariadb.version')}</InputLabel>
<InputLabel id="version-select-label" className="text-sm">{t('mariadb.version')}</InputLabel>
<Select
labelId="version-select-label"
value={selectedVersion}
label={t('mariadb.version')}
onChange={(e) => setSelectedVersion(e.target.value)}
sx={{ fontSize: '0.75rem', height: 32 }}
sx={{ height: 32 }}
className="text-sm"
>
{installedVersions.map(v => (
<MenuItem key={v.id} value={v.id} sx={{ fontSize: '0.75rem' }}>MariaDB {v.version}</MenuItem>
<MenuItem key={v.id} value={v.id} className="text-sm">MariaDB {v.version}</MenuItem>
))}
</Select>
</FormControl>
<Button variant="outlined" size="small" startIcon={<RefreshIcon sx={{ fontSize: '1rem' }} />} onClick={fetchData} disabled={loading} sx={{ fontSize: '0.7rem', height: 32, borderRadius: 2 }}>
<Button variant="outlined" size="small" startIcon={<RefreshIcon sx={{ fontSize: 'var(--font-size-base)' }} />} onClick={fetchData} disabled={loading} sx={{ height: 32 }} className="text-xs rounded-md">
{t('common.refresh')}
</Button>
</Stack>
</Stack>
{!selectedVersion && (
<Alert severity="info" sx={{ mt: 2, fontSize: '0.75rem' }}>{t('mariadb_manager.no_active_version')}</Alert>
<Alert severity="info" sx={{ mt: 2 }} className="text-sm">{t('mariadb_manager.no_active_version')}</Alert>
)}
{selectedVersion && (
@@ -210,10 +211,10 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
{/* Databases Section */}
<Box>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 1.5 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 800, color: 'primary.light', fontSize: '0.8rem' }}>
<Typography variant="subtitle2" sx={{ color: 'primary.light' }} className="font-black text-md">
{t('mariadb_manager.databases_title')}
</Typography>
<Button variant="outlined" size="small" startIcon={<AddIcon sx={{ fontSize: '1rem' }} />} onClick={handleCreateDb} sx={{ fontSize: '0.7rem', height: 28, borderRadius: 1.5 }}>
<Button variant="outlined" size="small" startIcon={<AddIcon sx={{ fontSize: 'var(--font-size-base)' }} />} onClick={handleCreateDb} sx={{ height: 28 }} className="text-xs rounded-md">
{t('mariadb_manager.create_db_btn')}
</Button>
</Box>
@@ -221,20 +222,20 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
<Table stickyHeader size="small">
<TableHead>
<TableRow>
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, fontSize: '0.7rem', py: 1 }}>{t('mariadb_manager.db_name')}</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, fontSize: '0.7rem', py: 1 }}>{t('mariadb_manager.db_size')}</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, fontSize: '0.7rem', py: 1, textAlign: 'right' }}>{t('common.actions')}</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', py: 1 }} className="font-black text-xs">{t('mariadb_manager.db_name')}</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', py: 1 }} className="font-black text-xs">{t('mariadb_manager.db_size')}</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', py: 1, textAlign: 'right' }} className="font-black text-xs">{t('common.actions')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{loading ? (
<TableRow><TableCell colSpan={3} align="center"><CircularProgress size={20} sx={{ my: 1 }} /></TableCell></TableRow>
) : databases.length === 0 ? (
<TableRow><TableCell colSpan={3} align="center" sx={{ py: 2, color: 'text.secondary', fontSize: '0.7rem' }}>{t('common.no_data')}</TableCell></TableRow>
<TableRow><TableCell colSpan={3} align="center" sx={{ py: 2 }} className="text-secondary text-xs">{t('common.no_data')}</TableCell></TableRow>
) : databases.map(db => (
<TableRow key={db.name}>
<TableCell sx={{ fontWeight: 600, fontSize: '0.75rem' }}>{db.name}</TableCell>
<TableCell sx={{ fontSize: '0.7rem', opacity: 0.8 }}>{db.size.toFixed(2)} MB</TableCell>
<TableCell className="font-semibold text-sm">{db.name}</TableCell>
<TableCell className="text-xs" sx={{ opacity: 0.8 }}>{db.size.toFixed(2)} MB</TableCell>
<TableCell align="right" sx={{ py: 0.5 }}>
<Tooltip title={t('common.delete')}>
<IconButton color="error" size="small" onClick={() => handleDeleteDb(db.name)} sx={{ p: 0.5 }}>
@@ -257,7 +258,7 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
<Typography variant="subtitle2" sx={{ fontWeight: 800, color: 'secondary.light', fontSize: '0.8rem' }}>
{t('mariadb_manager.users_title')}
</Typography>
<Button variant="outlined" color="secondary" size="small" startIcon={<AddIcon sx={{ fontSize: '1rem' }} />} onClick={handleCreateUser} sx={{ fontSize: '0.7rem', height: 28, borderRadius: 1.5 }}>
<Button variant="outlined" color="secondary" size="small" startIcon={<AddIcon sx={{ fontSize: 'var(--font-size-base)' }} />} onClick={handleCreateUser} sx={{ height: 28 }} className="text-xs rounded-md">
{t('mariadb_manager.create_user_btn')}
</Button>
</Box>
@@ -265,27 +266,27 @@ const MariaDbDatabaseManager: React.FC<MariaDbDatabaseManagerProps> = ({ version
<Table stickyHeader size="small">
<TableHead>
<TableRow>
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, fontSize: '0.7rem', py: 1 }}>{t('mariadb_manager.user_label')}</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, fontSize: '0.7rem', py: 1 }}>Host</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', fontWeight: 800, fontSize: '0.7rem', py: 1, textAlign: 'right' }}>{t('common.actions')}</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', py: 1 }} className="font-black text-xs">{t('mariadb_manager.user_label')}</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', py: 1 }} className="font-black text-xs">Host</TableCell>
<TableCell sx={{ bgcolor: '#1a1d21', py: 1, textAlign: 'right' }} className="font-black text-xs">{t('common.actions')}</TableCell>
</TableRow>
</TableHead>
<TableBody>
{loading ? (
<TableRow><TableCell colSpan={3} align="center"><CircularProgress size={20} sx={{ my: 1 }} /></TableCell></TableRow>
) : users.length === 0 ? (
<TableRow><TableCell colSpan={3} align="center" sx={{ py: 2, color: 'text.secondary', fontSize: '0.7rem' }}>{t('common.no_data')}</TableCell></TableRow>
<TableRow><TableCell colSpan={3} align="center" sx={{ py: 2 }} className="text-secondary text-xs">{t('common.no_data')}</TableCell></TableRow>
) : users.map(user => (
<TableRow key={`${user.user}-${user.host}`}>
<TableCell sx={{ fontWeight: 600, fontSize: '0.75rem' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<UserIcon sx={{ opacity: 0.5, fontSize: '0.9rem' }} />
<TableCell className="font-semibold text-sm">
<Box className="flex-align-center" sx={{ gap: 1 }}>
<UserIcon sx={{ fontSize: 'var(--font-size-md)' }} className="text-secondary" />
{user.user}
</Box>
</TableCell>
<TableCell sx={{ fontSize: '0.7rem', opacity: 0.8 }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<HostIcon sx={{ opacity: 0.5, fontSize: '0.9rem' }} />
<TableCell className="text-xs" sx={{ opacity: 0.8 }}>
<Box className="flex-align-center" sx={{ gap: 1 }}>
<HostIcon sx={{ fontSize: 'var(--font-size-md)' }} className="text-secondary" />
{user.host}
</Box>
</TableCell>
@@ -219,9 +219,9 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
<Box sx={{ mt: 2 }}>
{!exportResult ? (
<>
<Paper sx={{ p: 2, bgcolor: 'rgba(255,255,255,0.05)', borderRadius: 2 }}>
<Typography variant="subtitle2" color="primary" sx={{ fontWeight: 'bold' }}>{t('mariadb.import.summary')}</Typography>
<Divider sx={{ my: 1 }} />
<Paper sx={{ p: 2, bgcolor: 'var(--color-bg-soft)', border: '1px solid var(--color-border-soft)' }} className="rounded-md">
<Typography variant="subtitle2" color="primary" className="font-bold">{t('mariadb.import.summary')}</Typography>
<Divider sx={{ my: 1, opacity: 0.05 }} />
<Stack spacing={1}>
<Typography variant="body2"><strong>{t('mariadb.import.server')}:</strong> {selectedVersion}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.db')}:</strong> {selectedDb}</Typography>
@@ -230,7 +230,7 @@ const MariaDbExportWizard: React.FC<MariaDbExportWizardProps> = ({ open, onClose
</Stack>
</Paper>
{loading && (
<Box sx={{ mt: 3, p: 2, bgcolor: '#000', borderRadius: 2, maxHeight: '200px', overflowY: 'auto', fontFamily: 'monospace' }}>
<Box sx={{ mt: 3, p: 2, bgcolor: 'var(--color-bg-medium)', maxHeight: '200px', overflowY: 'auto', fontFamily: 'monospace' }} className="rounded-md border-soft">
{logs.map((log, i) => (
<Typography key={i} variant="caption" sx={{ display: 'block', color: log.includes('error') ? 'error.main' : 'success.main' }}>
{log}
@@ -260,9 +260,9 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
<Box sx={{ mt: 2 }}>
{!importResult ? (
<>
<Paper sx={{ p: 2, bgcolor: 'rgba(255,255,255,0.05)', borderRadius: 2 }}>
<Typography variant="subtitle2" color="primary" sx={{ fontWeight: 'bold' }}>{t('mariadb.import.summary')}</Typography>
<Divider sx={{ my: 1 }} />
<Paper sx={{ p: 2, bgcolor: 'var(--color-bg-soft)', border: '1px solid var(--color-border-soft)' }} className="rounded-md">
<Typography variant="subtitle2" color="primary" className="font-bold">{t('mariadb.import.summary')}</Typography>
<Divider sx={{ my: 1, opacity: 0.05 }} />
<Stack spacing={1}>
<Typography variant="body2"><strong>{t('mariadb.import.file')}:</strong> {filePath.split(/[/\\]/).pop()}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.server')}:</strong> {selectedVersion}</Typography>
@@ -271,7 +271,7 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
</Stack>
</Paper>
{loading && (
<Box sx={{ mt: 3, p: 2, bgcolor: '#000', borderRadius: 2, maxHeight: '200px', overflowY: 'auto', fontFamily: 'monospace' }}>
<Box sx={{ mt: 3, p: 2, bgcolor: 'var(--color-bg-medium)', maxHeight: '200px', overflowY: 'auto', fontFamily: 'monospace' }} className="rounded-md border-soft">
{logs.map((log, i) => (
<Typography key={i} variant="caption" sx={{ display: 'block', color: log.includes('fail') ? 'error.main' : 'success.main' }}>
{log}
@@ -288,8 +288,8 @@ const MariaDbImportWizard: React.FC<MariaDbImportWizardProps> = ({ open, onClose
<SuccessIcon color="success" sx={{ fontSize: 60 }} />
<Typography variant="h6" sx={{ mt: 2 }}>{t('mariadb.import.success_title')}</Typography>
<Typography variant="body2" color="text.secondary">{importResult.message}</Typography>
<Paper sx={{ p: 2, mt: 3, textAlign: 'left', bgcolor: 'rgba(0,180,0,0.1)' }}>
<Typography variant="caption" sx={{ fontWeight: 'bold' }}>{t('mariadb.import.access_info')}:</Typography>
<Paper sx={{ p: 2, mt: 3, textAlign: 'left', bgcolor: 'rgba(0,180,0,0.1)', border: '1px solid rgba(0,180,0,0.2)' }} className="rounded-md">
<Typography variant="caption" className="font-bold">{t('mariadb.import.access_info')}:</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.host')}:</strong> localhost</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.db')}:</strong> {dbConfig.dbName}</Typography>
<Typography variant="body2"><strong>{t('mariadb.import.user')}:</strong> {dbConfig.user}</Typography>
@@ -168,8 +168,8 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
>
<DialogTitle sx={{ m: 0, p: 1.5, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Box>
<Typography variant="subtitle2" sx={{ fontWeight: 800 }}>{t('php.extensions.title', { version: phpVersion })}</Typography>
<Typography variant="caption" color="text.secondary" sx={{ fontSize: '0.65rem' }}>{t('php.extensions.desc')}</Typography>
<Typography variant="subtitle2" className="font-black">{t('php.extensions.title', { version: phpVersion })}</Typography>
<Typography variant="caption" color="text.secondary" className="text-xs">{t('php.extensions.desc')}</Typography>
</Box>
<IconButton onClick={onClose} size="small" sx={{ color: 'text.secondary' }}>
<CloseIcon fontSize="small" />
@@ -186,18 +186,20 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon sx={{ color: 'text.secondary', fontSize: '1rem' }} />
<SearchIcon sx={{ color: 'text.secondary', fontSize: 'var(--font-size-base)' }} />
</InputAdornment>
),
sx: { color: '#fff', bgcolor: 'rgba(0,0,0,0.2)', fontSize: '0.75rem', height: 32 }
className: 'text-sm',
sx: { color: '#fff', bgcolor: 'rgba(0,0,0,0.2)', height: 32 }
}}
/>
<Button
variant="outlined"
size="small"
startIcon={<RawIcon sx={{ fontSize: '1rem' }} />}
startIcon={<RawIcon sx={{ fontSize: 'var(--font-size-base)' }} />}
onClick={fetchRawConfig}
sx={{ whiteSpace: 'nowrap', borderColor: 'rgba(255,255,255,0.1)', color: 'rgba(255,255,255,0.7)', fontSize: '0.7rem', height: 32, borderRadius: 1.5 }}
sx={{ whiteSpace: 'nowrap', borderColor: 'var(--color-border-medium)', color: 'var(--color-text-secondary)', height: 32 }}
className="text-xs rounded-md"
>
{t('php.extensions.raw_editor')}
</Button>
@@ -213,7 +215,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
<ListItem>
<ListItemText
primary={searchTerm ? t('php.extensions.not_found') : t('php.extensions.none')}
primaryTypographyProps={{ sx: { textAlign: 'center', color: 'text.secondary', fontSize: '0.75rem' } }}
primaryTypographyProps={{ className: 'text-sm text-secondary', sx: { textAlign: 'center' } }}
/>
</ListItem>
) : (
@@ -238,13 +240,13 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
>
<ListItemText
primary={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, fontSize: '0.75rem', fontWeight: 600 }}>
<Box className="flex-align-center font-semibold text-sm" sx={{ gap: 1 }}>
{ext.name}
{ext.enabled && <Chip label={t('php.extensions.active')} size="small" color="success" sx={{ height: 16, fontSize: '0.55rem', fontWeight: 900 }} />}
{ext.enabled && <Chip label={t('php.extensions.active')} size="small" color="success" className="font-black text-xs" sx={{ height: 16 }} />}
</Box>
}
secondary={ext.dll}
secondaryTypographyProps={{ sx: { color: 'text.secondary', fontSize: '0.65rem', opacity: 0.5 } }}
secondaryTypographyProps={{ className: 'text-xs text-secondary', sx: { opacity: 0.5 } }}
/>
</ListItem>
))
@@ -253,10 +255,10 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
)}
</DialogContent>
<DialogActions sx={{ p: 1.5, bgcolor: 'rgba(255,255,255,0.01)' }}>
<Button onClick={fetchExtensions} size="small" startIcon={<RefreshIcon sx={{ fontSize: '0.9rem' }} />} sx={{ mr: 'auto', fontSize: '0.7rem' }}>
<Button onClick={fetchExtensions} size="small" startIcon={<RefreshIcon sx={{ fontSize: 'var(--font-size-md)' }} />} className="text-xs" sx={{ mr: 'auto' }}>
{t('common.refresh').toUpperCase()}
</Button>
<Button onClick={onClose} variant="outlined" size="small" sx={{ fontSize: '0.7rem', borderRadius: 1.5 }}>
<Button onClick={onClose} variant="outlined" size="small" className="text-xs rounded-md">
{t('common.close').toUpperCase()}
</Button>
</DialogActions>
@@ -273,18 +275,18 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
>
<DialogTitle sx={{ display: 'flex', flexDirection: 'column', gap: 0.5, p: 1.5 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Typography variant="subtitle2" sx={{ fontWeight: 800 }}>{t('php.extensions.ini_edit', { version: phpVersion })}</Typography>
<Typography variant="subtitle2" className="font-black">{t('php.extensions.ini_edit', { version: phpVersion })}</Typography>
<IconButton onClick={() => setIsRawEditorOpen(false)} size="small" sx={{ color: 'rgba(255,255,255,0.5)' }}>
<CloseIcon fontSize="small" />
</IconButton>
</Box>
<Typography
variant="caption"
className="text-xs"
sx={{
color: 'primary.main',
wordBreak: 'break-all',
cursor: 'pointer',
fontSize: '0.6rem',
opacity: 0.7,
'&:hover': { textDecoration: 'underline', color: 'primary.light' }
}}
@@ -304,15 +306,15 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon sx={{ color: 'text.secondary', fontSize: '1rem' }} />
<SearchIcon sx={{ color: 'text.secondary', fontSize: 'var(--font-size-base)' }} />
</InputAdornment>
),
className: 'text-sm',
sx: {
color: '#fff',
bgcolor: 'rgba(255,255,255,0.03)',
fontSize: '0.75rem',
height: 32,
'& fieldset': { borderColor: 'rgba(255,255,255,0.05)' }
'& fieldset': { borderColor: 'var(--color-border-soft)' }
}
}}
/>
@@ -324,7 +326,7 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
/>
</DialogContent>
<DialogActions sx={{ p: 1.5, bgcolor: 'rgba(0,0,0,0.1)' }}>
<Button onClick={() => setIsRawEditorOpen(false)} size="small" sx={{ color: 'rgba(255,255,255,0.4)', fontSize: '0.7rem' }}>
<Button onClick={() => setIsRawEditorOpen(false)} size="small" className="text-secondary text-xs">
{t('common.cancel').toUpperCase()}
</Button>
<Button
@@ -332,9 +334,10 @@ const PhpExtensionManager: React.FC<PhpExtensionManagerProps> = ({ open, onClose
variant="outlined"
color="primary"
size="small"
startIcon={isSavingRaw ? <CircularProgress size={14} color="inherit" /> : <SaveIcon sx={{ fontSize: '1rem' }} />}
startIcon={isSavingRaw ? <CircularProgress size={14} color="inherit" /> : <SaveIcon sx={{ fontSize: 'var(--font-size-base)' }} />}
disabled={isSavingRaw}
sx={{ fontSize: '0.7rem', borderRadius: 1.5, px: 2 }}
className="text-xs rounded-md"
sx={{ px: 2 }}
>
{isSavingRaw ? t('php.extensions.saving_btn') : t('php.extensions.save_restart_btn')}
</Button>
@@ -201,7 +201,7 @@ export default function ProjectDetails({
return (
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', opacity: 0.3 }}>
<ProjectIcon sx={{ fontSize: 80, mb: 2 }} />
<Typography variant="h6">{t('projects.no_project_selected')}</Typography>
<Typography variant="h6" className="font-semibold">{t('projects.no_project_selected')}</Typography>
</Box>
);
}
@@ -218,14 +218,14 @@ export default function ProjectDetails({
<Box sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', bgcolor: 'rgba(255,255,255,0.01)', height: '100%', overflow: 'hidden', position: 'relative' }}>
{/* Header */}
<Box sx={{ p: 4, borderBottom: '1px solid rgba(255,255,255,0.05)', display: 'flex', alignItems: 'flex-start', gap: 3, position: 'relative' }}>
<Avatar sx={{ bgcolor: 'rgba(57, 167, 255, 0.1)', color: 'primary.main', width: 64, height: 64, borderRadius: 3, border: '1px solid rgba(57, 167, 255, 0.2)' }}>
<Avatar sx={{ bgcolor: 'rgba(57, 167, 255, 0.1)', color: 'primary.main', width: 64, height: 64, border: '1px solid var(--color-border-soft)' }} className="rounded-lg">
<ProjectIcon sx={{ fontSize: 32 }} />
</Avatar>
<Box sx={{ flexGrow: 1 }}>
<Box sx={{ mb: 2.5 }}>
<Typography variant="h4" sx={{ fontWeight: 800, letterSpacing: '-0.02em', mb: 0.5 }}>{p.name}</Typography>
<Typography variant="h4" sx={{ letterSpacing: '-0.02em', mb: 0.5 }} className="font-black">{p.name}</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Typography variant="body2" sx={{ color: 'rgba(255,255,255,0.4)', fontFamily: 'monospace' }}>
<Typography variant="body2" sx={{ fontFamily: 'monospace' }} className="text-secondary">
{p.path}
</Typography>
<Tooltip title={t('projects.open_folder')}>
@@ -263,7 +263,8 @@ export default function ProjectDetails({
size="small"
startIcon={<LaunchIcon sx={{ fontSize: '1rem !important' }} />}
onClick={() => onOpenSite(isHostMapped)}
sx={{ px: 2, height: 32, borderRadius: 1.5, fontWeight: 600, fontSize: '0.75rem', borderColor: 'rgba(255,255,255,0.1)', '&:hover': { borderColor: 'primary.main', bgcolor: 'rgba(57, 167, 255, 0.05)' } }}
sx={{ px: 2, height: 32, borderColor: 'var(--color-border-medium)', '&:hover': { borderColor: 'primary.main', bgcolor: 'rgba(57, 167, 255, 0.05)' } }}
className="rounded-md font-semibold text-sm"
>
{t('projects.open_site').toUpperCase()}
</Button>
@@ -320,9 +321,9 @@ export default function ProjectDetails({
{/* Tabs */}
<Box sx={{ borderBottom: 1, borderColor: 'rgba(255,255,255,0.05)', px: 4, bgcolor: 'rgba(255,255,255,0.01)' }}>
<Tabs value={projectDetailTab} onChange={(_e, v) => setProjectDetailTab(v)} textColor="primary" indicatorColor="primary">
<Tab label={t('projects.overview')} sx={{ fontWeight: 700, py: 2.5, minWidth: 120, fontSize: '0.8rem' }} />
<Tab label={t('projects.database')} sx={{ fontWeight: 700, py: 2.5, minWidth: 120, fontSize: '0.8rem' }} />
<Tab label={t('projects.server_settings')} sx={{ fontWeight: 700, py: 2.5, minWidth: 120, fontSize: '0.8rem' }} />
<Tab label={t('projects.overview').toUpperCase()} className="font-bold text-md" sx={{ py: 2.5, minWidth: 120 }} />
<Tab label={t('projects.database').toUpperCase()} className="font-bold text-md" sx={{ py: 2.5, minWidth: 120 }} />
<Tab label={t('projects.server_settings').toUpperCase()} className="font-bold text-md" sx={{ py: 2.5, minWidth: 120 }} />
</Tabs>
</Box>
+14 -14
View File
@@ -258,10 +258,10 @@ export default function ServerTuning() {
return (
<Box>
<Box sx={{ mb: 4 }}>
<Typography variant="h4" sx={{ fontWeight: 800, mb: 1, color: 'primary.light' }}>
<Typography variant="h4" sx={{ mb: 1, color: 'primary.light' }} className="font-black">
{t('tuning.title')}
</Typography>
<Typography variant="body1" sx={{ color: 'rgba(255,255,255,0.6)' }}>
<Typography variant="body1" className="text-secondary">
{t('tuning.desc')}
</Typography>
</Box>
@@ -269,22 +269,22 @@ export default function ServerTuning() {
<Grid container spacing={4}>
{/* PHP Section */}
<Grid item xs={12} md={6}>
<Paper sx={{ p: 3, height: '100%', bgcolor: 'rgba(255,255,255,0.02)', borderRadius: 4, border: '1px solid rgba(255,255,255,0.05)' }}>
<Paper sx={{ p: 3, height: '100%', bgcolor: 'var(--color-bg-soft)', border: '1px solid var(--color-border-soft)' }} className="rounded-xl">
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3, gap: 2 }}>
<Box sx={{ p: 1.5, borderRadius: 2, bgcolor: 'primary.main', display: 'flex' }}>
<PhpIcon sx={{ color: 'white' }} />
</Box>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>PHP Tuning</Typography>
<Typography variant="h6" className="font-bold">PHP Tuning</Typography>
<Chip label={phpVersions.length} size="small" variant="outlined" />
</Box>
<Stack spacing={2}>
{phpVersions.map(v => (
<Card key={v.id} sx={{ bgcolor: 'rgba(255,255,255,0.03)', backgroundImage: 'none', border: '1px solid rgba(255,255,255,0.05)' }}>
<Card key={v.id} sx={{ bgcolor: 'var(--color-bg-soft)', backgroundImage: 'none', border: '1px solid var(--color-border-soft)' }}>
<CardActionArea onClick={() => handleOpenTuning(v, 'php')}>
<CardContent sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Box>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>PHP {v.version}</Typography>
<Typography variant="subtitle1" className="font-bold">PHP {v.version}</Typography>
<Typography variant="caption" color="text.secondary">php.ini optimization</Typography>
</Box>
<Button variant="outlined" size="small" startIcon={<TuneIcon />}>
@@ -300,22 +300,22 @@ export default function ServerTuning() {
{/* MariaDB Section */}
<Grid item xs={12} md={6}>
<Paper sx={{ p: 3, height: '100%', bgcolor: 'rgba(255,255,255,0.02)', borderRadius: 4, border: '1px solid rgba(255,255,255,0.05)' }}>
<Paper sx={{ p: 3, height: '100%', bgcolor: 'var(--color-bg-soft)', border: '1px solid var(--color-border-soft)' }} className="rounded-xl">
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3, gap: 2 }}>
<Box sx={{ p: 1.5, borderRadius: 2, bgcolor: 'secondary.main', display: 'flex' }}>
<DbIcon sx={{ color: 'white' }} />
</Box>
<Typography variant="h6" sx={{ fontWeight: 'bold' }}>MariaDB Tuning</Typography>
<Typography variant="h6" className="font-bold">MariaDB Tuning</Typography>
<Chip label={mariaDbVersions.length} size="small" variant="outlined" />
</Box>
<Stack spacing={2}>
{mariaDbVersions.map(v => (
<Card key={v.id} sx={{ bgcolor: 'rgba(255,255,255,0.03)', backgroundImage: 'none', border: '1px solid rgba(255,255,255,0.05)' }}>
<Card key={v.id} sx={{ bgcolor: 'var(--color-bg-soft)', backgroundImage: 'none', border: '1px solid var(--color-border-soft)' }}>
<CardActionArea onClick={() => handleOpenTuning(v, 'mariadb')}>
<CardContent sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Box>
<Typography variant="subtitle1" sx={{ fontWeight: 'bold' }}>MariaDB {v.version}</Typography>
<Typography variant="subtitle1" className="font-bold">MariaDB {v.version}</Typography>
<Typography variant="caption" color="text.secondary">Performance variables</Typography>
</Box>
<Button variant="outlined" color="secondary" size="small" startIcon={<TuneIcon />}>
@@ -343,7 +343,7 @@ export default function ServerTuning() {
<DialogTitle sx={{ borderBottom: '1px solid rgba(255,255,255,0.1)', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
<TuneIcon color={selectedVersion?.type === 'php' ? 'primary' : 'secondary'} />
<Typography variant="h6" sx={{ fontWeight: 800 }}>
<Typography variant="h6" className="font-black">
{selectedVersion?.type === 'php' ? `PHP ${selectedVersion?.version}` : `MariaDB ${selectedVersion?.version}`} Tuning
</Typography>
</Box>
@@ -362,7 +362,7 @@ export default function ServerTuning() {
<Box key={p.key}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 1, alignItems: 'center' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 'bold' }}>{p.label}</Typography>
<Typography variant="subtitle2" className="font-bold">{p.label}</Typography>
<Tooltip title={t(p.description)}>
<InfoIcon sx={{ fontSize: 16, color: 'rgba(255,255,255,0.3)', cursor: 'help' }} />
</Tooltip>
@@ -372,7 +372,7 @@ export default function ServerTuning() {
value={tuningValues[p.key]}
onChange={(e) => setTuningValues({ ...tuningValues, [p.key]: parseInt(e.target.value) || p.min })}
InputProps={{
endAdornment: <Typography variant="caption" sx={{ ml: 1, opacity: 0.5 }}>{p.unit}</Typography>
endAdornment: <Typography variant="caption" sx={{ ml: 1 }} className="text-secondary">{p.unit}</Typography>
} as any}
sx={{ width: 120, '& .MuiOutlinedInput-root': { height: 32 } }}
/>
@@ -391,7 +391,7 @@ export default function ServerTuning() {
}
}}
/>
<Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.4)', mt: -1, display: 'block' }}>
<Typography variant="caption" className="text-muted" sx={{ mt: -1, display: 'block' }}>
{t(p.description)}
</Typography>
</Box>
+48
View File
@@ -1,6 +1,54 @@
import { GlobalStyles as MuiGlobalStyles } from '@mui/material'
const styles = {
':root': {
'--font-size-xs': '0.65rem',
'--font-size-sm': '1rem',
'--font-size-md': '0.85rem',
'--font-size-base': '1rem',
'--font-size-lg': '1.25rem',
'--font-size-xl': '1.5rem',
'--font-weight-thin': '300',
'--font-weight-normal': '400',
'--font-weight-medium': '500',
'--font-weight-semibold': '600',
'--font-weight-bold': '700',
'--font-weight-black': '800',
'--radius-sm': '4px',
'--radius-md': '8px',
'--radius-lg': '12px',
'--radius-xl': '16px',
'--color-border-soft': 'rgba(255, 255, 255, 0.05)',
'--color-border-medium': 'rgba(255, 255, 255, 0.1)',
'--color-text-secondary': 'rgba(255, 255, 255, 0.5)',
'--color-text-muted': 'rgba(255, 255, 255, 0.3)',
'--color-bg-soft': 'rgba(255, 255, 255, 0.03)',
'--color-bg-medium': 'rgba(0, 0, 0, 0.2)',
},
'.text-xs': { fontSize: 'var(--font-size-xs) !important' },
'.text-sm': { fontSize: 'var(--font-size-sm) !important' },
'.text-md': { fontSize: 'var(--font-size-md) !important' },
'.text-base': { fontSize: 'var(--font-size-base) !important' },
'.text-lg': { fontSize: 'var(--font-size-lg) !important' },
'.text-xl': { fontSize: 'var(--font-size-xl) !important' },
'.font-normal': { fontWeight: 'var(--font-weight-normal) !important' },
'.font-medium': { fontWeight: 'var(--font-weight-medium) !important' },
'.font-semibold': { fontWeight: 'var(--font-weight-semibold) !important' },
'.font-bold': { fontWeight: 'var(--font-weight-bold) !important' },
'.font-black': { fontWeight: 'var(--font-weight-black) !important' },
'.text-secondary': { color: 'var(--color-text-secondary) !important' },
'.text-muted': { color: 'var(--color-text-muted) !important' },
'.border-soft': { border: '1px solid var(--color-border-soft) !important' },
'.border-medium': { border: '1px solid var(--color-border-medium) !important' },
'.bg-soft': { backgroundColor: 'var(--color-bg-soft) !important' },
'.bg-medium': { backgroundColor: 'var(--color-bg-medium) !important' },
'.rounded-sm': { borderRadius: 'var(--radius-sm) !important' },
'.rounded-md': { borderRadius: 'var(--radius-md) !important' },
'.rounded-lg': { borderRadius: 'var(--radius-lg) !important' },
'.rounded-xl': { borderRadius: 'var(--radius-xl) !important' },
'.flex-center': { display: 'flex', alignItems: 'center', justifyContent: 'center' },
'.flex-between': { display: 'flex', alignItems: 'center', justifyContent: 'space-between' },
'.flex-align-center': { display: 'flex', alignItems: 'center' },
'html, body, #root': {
margin: 0,
padding: 0,