u
This commit is contained in:
@@ -21,19 +21,28 @@ import TableRow from '@mui/material/TableRow';
|
||||
import Tabs from '@mui/material/Tabs';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Dialog from '@mui/material/Dialog';
|
||||
import DialogTitle from '@mui/material/DialogTitle';
|
||||
import DialogContent from '@mui/material/DialogContent';
|
||||
import DialogContentText from '@mui/material/DialogContentText';
|
||||
import DialogActions from '@mui/material/DialogActions';
|
||||
import BackupOutlinedIcon from '@mui/icons-material/BackupOutlined';
|
||||
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutlined';
|
||||
import CloseIcon from '@mui/icons-material/Close';
|
||||
import CloudQueueOutlinedIcon from '@mui/icons-material/CloudQueueOutlined';
|
||||
import FolderOutlinedIcon from '@mui/icons-material/FolderOutlined';
|
||||
import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutlined';
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
|
||||
import PsychologyOutlinedIcon from '@mui/icons-material/PsychologyOutlined';
|
||||
import RefreshIcon from '@mui/icons-material/Refresh';
|
||||
import SearchIcon from '@mui/icons-material/Search';
|
||||
import SettingsBackupRestoreIcon from '@mui/icons-material/SettingsBackupRestore';
|
||||
import StorageIcon from '@mui/icons-material/Storage';
|
||||
import WarningAmberIcon from '@mui/icons-material/WarningAmber';
|
||||
import WidgetsOutlinedIcon from '@mui/icons-material/WidgetsOutlined';
|
||||
import { enqueueSnackbar } from 'notistack';
|
||||
import { vfs } from '../../api';
|
||||
import backupAutomat from '../../automats/backupAutomat';
|
||||
import { notifyError } from '../../utils/notify';
|
||||
import {
|
||||
@@ -92,11 +101,18 @@ export default class BackupSettingsPanel extends Component {
|
||||
vfsLoadingByBackup: vfsLoadingByBackup || {},
|
||||
tierTab: 'hourly', // 'hourly' | 'daily' | 'monthly'
|
||||
expandedItemName: null,
|
||||
restoringSkills: false,
|
||||
restoringApplets: false,
|
||||
confirmRestore: null, // null | 'skills' | 'applets'
|
||||
lastRestoreResult: null, // null | { type: 'skills' | 'applets', count: number, restored: string[] }
|
||||
};
|
||||
|
||||
this.handleRefresh = this.handleRefresh.bind(this);
|
||||
this.handleTierChange = this.handleTierChange.bind(this);
|
||||
this.handleToggleExpand = this.handleToggleExpand.bind(this);
|
||||
this.handleOpenConfirm = this.handleOpenConfirm.bind(this);
|
||||
this.handleCloseConfirm = this.handleCloseConfirm.bind(this);
|
||||
this.handleExecuteRestore = this.handleExecuteRestore.bind(this);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@@ -151,6 +167,65 @@ export default class BackupSettingsPanel extends Component {
|
||||
}));
|
||||
}
|
||||
|
||||
handleOpenConfirm(type) {
|
||||
this.setState({ confirmRestore: type });
|
||||
}
|
||||
|
||||
handleCloseConfirm() {
|
||||
if (this.state.restoringSkills || this.state.restoringApplets) return;
|
||||
this.setState({ confirmRestore: null });
|
||||
}
|
||||
|
||||
// #region handleExecuteRestore
|
||||
// MARK: calls POST /api/vfs/skills/restore or /api/vfs/applets/restore
|
||||
async handleExecuteRestore() {
|
||||
const { confirmRestore } = this.state;
|
||||
if (!confirmRestore) return;
|
||||
|
||||
if (confirmRestore === 'skills') {
|
||||
this.setState({ restoringSkills: true });
|
||||
try {
|
||||
const res = await vfs.restoreSkills();
|
||||
enqueueSnackbar(`${res.count} Standard-Skills (.SKILL) erfolgreich wiederhergestellt.`, {
|
||||
variant: 'success',
|
||||
});
|
||||
this.setState({
|
||||
confirmRestore: null,
|
||||
restoringSkills: false,
|
||||
lastRestoreResult: {
|
||||
type: 'skills',
|
||||
count: res.count,
|
||||
restored: res.restored || [],
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
this.setState({ restoringSkills: false });
|
||||
notifyError(err);
|
||||
}
|
||||
} else if (confirmRestore === 'applets') {
|
||||
this.setState({ restoringApplets: true });
|
||||
try {
|
||||
const res = await vfs.restoreApplets();
|
||||
enqueueSnackbar(`${res.count} Beispiel-Applets (.applet.js) erfolgreich wiederhergestellt.`, {
|
||||
variant: 'success',
|
||||
});
|
||||
this.setState({
|
||||
confirmRestore: null,
|
||||
restoringApplets: false,
|
||||
lastRestoreResult: {
|
||||
type: 'applets',
|
||||
count: res.count,
|
||||
restored: res.restored || [],
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
this.setState({ restoringApplets: false });
|
||||
notifyError(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
render() {
|
||||
const {
|
||||
status,
|
||||
@@ -161,6 +236,10 @@ export default class BackupSettingsPanel extends Component {
|
||||
vfsLoadingByBackup,
|
||||
tierTab,
|
||||
expandedItemName,
|
||||
restoringSkills,
|
||||
restoringApplets,
|
||||
confirmRestore,
|
||||
lastRestoreResult,
|
||||
} = this.state;
|
||||
|
||||
if (status === 'loading' && !backupStatus) {
|
||||
@@ -224,7 +303,7 @@ export default class BackupSettingsPanel extends Component {
|
||||
size="small"
|
||||
startIcon={<RefreshIcon />}
|
||||
onClick={this.handleRefresh}
|
||||
disabled={isRunning}
|
||||
disabled={status === 'loading'}
|
||||
>
|
||||
Aktualisieren
|
||||
</Button>
|
||||
@@ -246,19 +325,11 @@ export default class BackupSettingsPanel extends Component {
|
||||
>
|
||||
<Chip
|
||||
size="small"
|
||||
icon={backupStatus?.enabled ? <CheckCircleOutlineIcon /> : <WarningAmberIcon />}
|
||||
label={backupStatus?.enabled ? 'Dienst: Aktiv' : 'Dienst: Deaktiviert'}
|
||||
color={backupStatus?.enabled ? 'success' : 'default'}
|
||||
icon={backupStatus?.enabled && isRunning ? <CheckCircleOutlineIcon /> : <WarningAmberIcon />}
|
||||
label={backupStatus?.enabled && isRunning ? 'Dienst: Aktiv' : 'Dienst: Deaktiviert'}
|
||||
color={backupStatus?.enabled && isRunning ? 'success' : 'default'}
|
||||
variant="outlined"
|
||||
/>
|
||||
{isRunning && (
|
||||
<Chip
|
||||
size="small"
|
||||
icon={<CircularProgress size={12} color="inherit" />}
|
||||
label="Backup wird ausgeführt..."
|
||||
color="primary"
|
||||
/>
|
||||
)}
|
||||
<Chip
|
||||
size="small"
|
||||
icon={<StorageIcon />}
|
||||
@@ -431,6 +502,231 @@ export default class BackupSettingsPanel extends Component {
|
||||
</TableContainer>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{/* System Defaults Restore Section (VFS Skills & Applets) */}
|
||||
<Box sx={{ border: `1px solid ${BORDER}`, borderRadius: 2, bgcolor: WHITE, overflow: 'hidden' }}>
|
||||
<Box sx={{ borderBottom: `1px solid ${BORDER}`, bgcolor: SURFACE_RAISED, px: 2, py: 1.5 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<SettingsBackupRestoreIcon sx={{ color: PRIMARY, fontSize: 22 }} />
|
||||
<Typography variant="subtitle2" fontWeight={700} sx={{ color: DARK_4 }}>
|
||||
System-Standarddateien wiederherstellen (VFS)
|
||||
</Typography>
|
||||
</Box>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ mt: 0.5, display: 'block' }}>
|
||||
Setzt systemweite Vorlagen im virtuellen Dateisystem (VFS) des Unternehmens auf den Auslieferungszustand zurück. Bereits vorhandene gleichnamige Dateien werden dabei überschrieben.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ p: 2 }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: { xs: '1fr', sm: '1fr 1fr' },
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
{/* Skills Restore Card */}
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
border: `1px solid ${BORDER}`,
|
||||
bgcolor: SURFACE_BASE,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
gap: 1.5,
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 1.5,
|
||||
bgcolor: 'rgba(15, 118, 110, 0.1)',
|
||||
color: PRIMARY,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<PsychologyOutlinedIcon fontSize="small" />
|
||||
</Box>
|
||||
<Typography variant="subtitle2" fontWeight={700} sx={{ color: DARK_4 }}>
|
||||
Standard-Skills
|
||||
</Typography>
|
||||
</Box>
|
||||
<Chip size="small" label=".SKILL" variant="outlined" sx={{ height: 20, fontSize: '0.75rem', fontWeight: 600 }} />
|
||||
</Box>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontSize: '0.825rem', lineHeight: 1.4 }}>
|
||||
Stellt alle systemweiten Standard-.SKILL-Dateien für KI-Assistenten und Workflows im VFS wieder her.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', pt: 1, borderTop: `1px solid ${BORDER}` }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontStyle: 'italic', fontSize: '0.75rem' }}>
|
||||
Überschreibt existierende Dateien
|
||||
</Typography>
|
||||
<Button
|
||||
size="small"
|
||||
variant="contained"
|
||||
startIcon={restoringSkills ? <CircularProgress size={14} color="inherit" /> : <SettingsBackupRestoreIcon fontSize="small" />}
|
||||
onClick={() => this.handleOpenConfirm('skills')}
|
||||
disabled={restoringSkills}
|
||||
sx={{ textTransform: 'none', minHeight: 30, fontSize: '0.8rem', fontWeight: 600 }}
|
||||
>
|
||||
Skills wiederherstellen
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Applets Restore Card */}
|
||||
<Box
|
||||
sx={{
|
||||
p: 2,
|
||||
borderRadius: 2,
|
||||
border: `1px solid ${BORDER}`,
|
||||
bgcolor: SURFACE_BASE,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
gap: 1.5,
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<Box
|
||||
sx={{
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 1.5,
|
||||
bgcolor: 'rgba(15, 118, 110, 0.1)',
|
||||
color: PRIMARY,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<WidgetsOutlinedIcon fontSize="small" />
|
||||
</Box>
|
||||
<Typography variant="subtitle2" fontWeight={700} sx={{ color: DARK_4 }}>
|
||||
Beispiel-Applets
|
||||
</Typography>
|
||||
</Box>
|
||||
<Chip size="small" label=".applet.js" variant="outlined" sx={{ height: 20, fontSize: '0.75rem', fontWeight: 600 }} />
|
||||
</Box>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ fontSize: '0.825rem', lineHeight: 1.4 }}>
|
||||
Stellt alle systemweiten Standard-.applet.js-Beispiel-Applets für Workflows und Auswertungen im VFS wieder her.
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', pt: 1, borderTop: `1px solid ${BORDER}` }}>
|
||||
<Typography variant="caption" color="text.secondary" sx={{ fontStyle: 'italic', fontSize: '0.75rem' }}>
|
||||
Überschreibt existierende Dateien
|
||||
</Typography>
|
||||
<Button
|
||||
size="small"
|
||||
variant="contained"
|
||||
startIcon={restoringApplets ? <CircularProgress size={14} color="inherit" /> : <SettingsBackupRestoreIcon fontSize="small" />}
|
||||
onClick={() => this.handleOpenConfirm('applets')}
|
||||
disabled={restoringApplets}
|
||||
sx={{ textTransform: 'none', minHeight: 30, fontSize: '0.8rem', fontWeight: 600 }}
|
||||
>
|
||||
Applets wiederherstellen
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Last restore results notification / chip display */}
|
||||
{lastRestoreResult && (
|
||||
<Box sx={{ mt: 2, p: 1.5, borderRadius: 1.5, bgcolor: 'rgba(15, 118, 110, 0.08)', border: '1px solid rgba(15, 118, 110, 0.2)' }}>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mb: 1 }}>
|
||||
<Typography variant="caption" fontWeight={600} sx={{ color: PRIMARY }}>
|
||||
Erfolgreich wiederhergestellt ({lastRestoreResult.count} {lastRestoreResult.type === 'skills' ? 'Skills' : 'Applets'}):
|
||||
</Typography>
|
||||
<IconButton
|
||||
size="small"
|
||||
onClick={() => this.setState({ lastRestoreResult: null })}
|
||||
sx={{ p: 0.25, color: 'text.secondary' }}
|
||||
>
|
||||
<CloseIcon fontSize="small" sx={{ fontSize: 16 }} />
|
||||
</IconButton>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.75 }}>
|
||||
{lastRestoreResult.restored.map((name) => (
|
||||
<Chip
|
||||
key={name}
|
||||
size="small"
|
||||
label={name}
|
||||
variant="outlined"
|
||||
sx={{
|
||||
height: 22,
|
||||
fontSize: '0.75rem',
|
||||
fontFamily: 'monospace',
|
||||
bgcolor: WHITE,
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Confirmation Dialog for System Defaults Restore */}
|
||||
<Dialog
|
||||
open={Boolean(confirmRestore)}
|
||||
onClose={this.handleCloseConfirm}
|
||||
maxWidth="xs"
|
||||
fullWidth
|
||||
>
|
||||
<DialogTitle sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||
<WarningAmberIcon color="warning" />
|
||||
{confirmRestore === 'skills'
|
||||
? 'Standard-Skills wiederherstellen?'
|
||||
: 'Beispiel-Applets wiederherstellen?'}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText sx={{ color: DARK_2 }}>
|
||||
{confirmRestore === 'skills'
|
||||
? 'Möchten Sie alle systemweiten Standard-.SKILL-Dateien im VFS des aktuellen Unternehmens wiederherstellen? Bestehende .SKILL-Dateien werden dabei überschrieben.'
|
||||
: 'Möchten Sie alle systemweiten Standard-.applet.js-Beispiel-Applets im VFS des aktuellen Unternehmens wiederherstellen? Bestehende .applet.js-Dateien werden dabei überschrieben.'}
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions sx={{ px: 3, pb: 2 }}>
|
||||
<Button
|
||||
onClick={this.handleCloseConfirm}
|
||||
disabled={restoringSkills || restoringApplets}
|
||||
variant="outlined"
|
||||
sx={{ textTransform: 'none' }}
|
||||
>
|
||||
Abbrechen
|
||||
</Button>
|
||||
<Button
|
||||
onClick={this.handleExecuteRestore}
|
||||
disabled={restoringSkills || restoringApplets}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
startIcon={
|
||||
(confirmRestore === 'skills' ? restoringSkills : restoringApplets) ? (
|
||||
<CircularProgress size={14} color="inherit" />
|
||||
) : (
|
||||
<SettingsBackupRestoreIcon />
|
||||
)
|
||||
}
|
||||
sx={{ textTransform: 'none' }}
|
||||
>
|
||||
{(confirmRestore === 'skills' ? restoringSkills : restoringApplets)
|
||||
? 'Wiederherstellen...'
|
||||
: 'Jetzt wiederherstellen'}
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user