235 lines
6.8 KiB
JavaScript
235 lines
6.8 KiB
JavaScript
// components/SettingsModal.jsx
|
||
import React, { useState, useEffect } from 'react';
|
||
import {
|
||
Dialog,
|
||
DialogTitle,
|
||
DialogContent,
|
||
DialogActions,
|
||
Button,
|
||
Tabs,
|
||
Tab,
|
||
Box,
|
||
Typography,
|
||
IconButton,
|
||
styled,
|
||
CircularProgress,
|
||
Slide,
|
||
Snackbar,
|
||
Alert
|
||
} from '@mui/material';
|
||
import CloseIcon from '@mui/icons-material/Close';
|
||
import SaveIcon from '@mui/icons-material/Save';
|
||
import MetricRangeEditor from './SettingsComponents/MetricRangeEditor';
|
||
import UserManagement from './SettingsComponents/UserManagement';
|
||
import MenuEditor from './SettingsComponents/MenuEditor'
|
||
|
||
const Transition = React.forwardRef(function Transition(props, ref) {
|
||
return <Slide direction="up" ref={ref} {...props} />;
|
||
});
|
||
|
||
const StyledDialog = styled(Dialog)(({ theme }) => ({
|
||
'& .MuiDialog-paper': {
|
||
minWidth: 600,
|
||
maxHeight: '80vh',
|
||
backgroundColor: theme.palette.background.paper,
|
||
},
|
||
}));
|
||
|
||
const TabPanel = (props) => {
|
||
const { children, value, index, ...other } = props;
|
||
|
||
return (
|
||
<div
|
||
role="tabpanel"
|
||
hidden={value !== index}
|
||
id={`settings-tabpanel-${index}`}
|
||
aria-labelledby={`settings-tab-${index}`}
|
||
{...other}
|
||
>
|
||
{value === index && (
|
||
<Box sx={{ p: 3 }}>
|
||
<Typography component="div">{children}</Typography>
|
||
</Box>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|
||
|
||
const SettingsModal = ({ open, onClose, onMenuUpdate }) => {
|
||
const [tabValue, setTabValue] = useState(0);
|
||
const [isSaving, setIsSaving] = useState(false);
|
||
const [showSuccess, setShowSuccess] = useState(false);
|
||
const [hasChanges, setHasChanges] = useState(false);
|
||
const [showConfirmClose, setShowConfirmClose] = useState(false);
|
||
const [metricEditorState, setMetricEditorState] = useState({
|
||
hasChanges: false,
|
||
save: () => { }
|
||
});
|
||
const [menuEditorState, setMenuEditorState] = useState({
|
||
hasChanges: false,
|
||
save: () => Promise.resolve(true)
|
||
});
|
||
|
||
const handleTabChange = (event, newValue) => {
|
||
if (hasChanges) {
|
||
setShowConfirmClose(true);
|
||
} else {
|
||
setTabValue(newValue);
|
||
}
|
||
};
|
||
|
||
const handleMenuEditorChange = ({ hasChanges, saveChanges }) => {
|
||
setMenuEditorState({ hasChanges, save: saveChanges });
|
||
setHasChanges(hasChanges);
|
||
};
|
||
|
||
const handleSave = async () => {
|
||
setIsSaving(true);
|
||
try {
|
||
let success = true;
|
||
|
||
if (tabValue === 0 && menuEditorState.hasChanges) {
|
||
success = await menuEditorState.save();
|
||
}
|
||
|
||
if (tabValue === 1 && metricEditorState.hasChanges) {
|
||
success = success && await metricEditorState.save();
|
||
}
|
||
|
||
if (success) {
|
||
setShowSuccess(true);
|
||
setHasChanges(false);
|
||
if (onMenuUpdate) {
|
||
onMenuUpdate();
|
||
}
|
||
}
|
||
} finally {
|
||
setIsSaving(false);
|
||
}
|
||
};
|
||
|
||
const handleMetricEditorChange = ({ hasChanges, saveChanges }) => {
|
||
setMetricEditorState({ hasChanges, save: saveChanges });
|
||
setHasChanges(hasChanges);
|
||
};
|
||
|
||
const handleClose = () => {
|
||
if (hasChanges) {
|
||
setShowConfirmClose(true);
|
||
} else {
|
||
onClose();
|
||
}
|
||
};
|
||
|
||
const handleConfirmClose = (shouldClose) => {
|
||
setShowConfirmClose(false);
|
||
if (shouldClose) {
|
||
onClose();
|
||
}
|
||
};
|
||
|
||
const handleSettingChange = () => {
|
||
setHasChanges(true);
|
||
};
|
||
|
||
return (
|
||
<>
|
||
<StyledDialog
|
||
open={open}
|
||
onClose={handleClose}
|
||
aria-labelledby="settings-dialog-title"
|
||
maxWidth="md"
|
||
fullWidth
|
||
TransitionComponent={Transition}
|
||
>
|
||
<DialogTitle id="settings-dialog-title">
|
||
Настройки
|
||
<IconButton
|
||
aria-label="close"
|
||
onClick={handleClose}
|
||
sx={{
|
||
position: 'absolute',
|
||
right: 8,
|
||
top: 8,
|
||
color: (theme) => theme.palette.grey[500],
|
||
}}
|
||
>
|
||
<CloseIcon />
|
||
</IconButton>
|
||
</DialogTitle>
|
||
|
||
<Box sx={{ borderBottom: 1, borderColor: 'divider' }}>
|
||
<Tabs value={tabValue} onChange={handleTabChange} aria-label="settings tabs">
|
||
<Tab label="Меню" id="settings-tab-0" aria-controls="settings-tabpanel-0" />
|
||
<Tab label="Границы метрик" id="settings-tab-1" aria-controls="settings-tabpanel-1" />
|
||
<Tab label="Управление пользователями" id="settings-tab-2" aria-controls="settings-tabpanel-2" />
|
||
{/* Добавить новые вкладки здесь */}
|
||
</Tabs>
|
||
</Box>
|
||
|
||
<DialogContent dividers>
|
||
<TabPanel value={tabValue} index={0}>
|
||
<MenuEditor onSave={handleMenuEditorChange} />
|
||
</TabPanel>
|
||
|
||
<TabPanel value={tabValue} index={1}>
|
||
<MetricRangeEditor onSave={handleMetricEditorChange} />
|
||
</TabPanel>
|
||
|
||
<TabPanel value={tabValue} index={2}>
|
||
<UserManagement />
|
||
</TabPanel>
|
||
|
||
{/* Добавляйте новые TabPanel для новых вкладок */}
|
||
</DialogContent>
|
||
|
||
|
||
<DialogActions>
|
||
<Button onClick={handleClose}>Закрыть</Button>
|
||
<Button
|
||
onClick={handleSave}
|
||
variant="contained"
|
||
color="primary"
|
||
disabled={isSaving || !hasChanges}
|
||
startIcon={isSaving ? <CircularProgress size={20} /> : <SaveIcon />}
|
||
>
|
||
{isSaving ? 'Сохранение...' : 'Сохранить'}
|
||
</Button>
|
||
</DialogActions>
|
||
</StyledDialog>
|
||
|
||
{/* Уведомление об успешном сохранении */}
|
||
<Snackbar
|
||
open={showSuccess}
|
||
autoHideDuration={3000}
|
||
onClose={() => setShowSuccess(false)}
|
||
anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
|
||
>
|
||
<Alert onClose={() => setShowSuccess(false)} severity="success" sx={{ width: '100%' }}>
|
||
Настройки успешно сохранены!
|
||
</Alert>
|
||
</Snackbar>
|
||
|
||
{/* Диалог подтверждения закрытия */}
|
||
<Dialog
|
||
open={showConfirmClose}
|
||
onClose={() => handleConfirmClose(false)}
|
||
aria-labelledby="alert-dialog-title"
|
||
aria-describedby="alert-dialog-description"
|
||
>
|
||
<DialogTitle id="alert-dialog-title">Есть несохраненные изменения</DialogTitle>
|
||
<DialogContent>
|
||
<Typography>Вы уверены, что хотите закрыть без сохранения изменений?</Typography>
|
||
</DialogContent>
|
||
<DialogActions>
|
||
<Button onClick={() => handleConfirmClose(false)}>Отмена</Button>
|
||
<Button onClick={() => handleConfirmClose(true)} autoFocus color="error">
|
||
Закрыть без сохранения
|
||
</Button>
|
||
</DialogActions>
|
||
</Dialog>
|
||
</>
|
||
);
|
||
};
|
||
|
||
export default SettingsModal; |