gonets/FRONT/DEV/src/components/settings/Settings.tsx

232 lines
8.3 KiB
TypeScript
Raw Normal View History

2025-06-27 17:50:08 +05:00
import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../modalwindows/style";
import { MainButton } from "../UI/buttons/mainButton";
import {useCallback, useEffect, useMemo, useState} from "react";
2025-07-04 15:54:02 +05:00
import styled, {useTheme} from "styled-components";
import {Setting, useSettingsStore} from "../../store/settings/settingsStore";
2025-06-27 17:50:08 +05:00
import {SettingItem} from "./SettingItem";
import {IconComponent} from "../../const/buttons/icons";
import {buttonNames} from "../../const/buttons/names";
2025-07-04 15:54:02 +05:00
2025-06-27 17:50:08 +05:00
interface Props {
onClose: () => void;
}
const SectionsSidebar = styled.ul`
display: flex;
justify-content: start;
align-items: start;
flex-direction: column;
gap: 4px;
width: 20%;
min-width: 180px;
list-style-type: none;
padding: 0;
margin: 0;
`
const SectionsBTN = styled.li`
font-size: 18px;
border: none;
cursor: pointer;
width: 100%;
padding:10px 16px;
box-sizing: border-box;
border-radius: 0 12px 12px 0;
&.active{
background-color: ${(props) => props.theme.accent};
color: ${props => props.theme.primary};
}
`
const SectionActive = styled.div`
width: 80%;
`
const SectionLine = styled.span`
height: 100%;
width: 1px;
background-color: #9D9D9D;
`
const CloseBtn = styled.span`
cursor: pointer;
`
2025-07-04 15:54:02 +05:00
const SettingsButtons = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
span{
display: flex;
gap: 12px;
}
`
export const Settings = ({ onClose }: Props) => {
const settingsConfig = useSettingsStore(state => state.settingsConfig);
const settingsValues = useSettingsStore(state => state.settingsValues);
const activeSection = useSettingsStore(state => state.activeSection);
const pendingChanges = useSettingsStore(state => state.pendingChanges);
const setActiveSection = useSettingsStore(state => state.setActiveSection);
const updateSetting = useSettingsStore(state => state.updateSetting);
const applyChanges = useSettingsStore(state => state.applyChanges);
const discardChanges = useSettingsStore(state => state.discardChanges);
const getSetting = useSettingsStore(state => state.getSetting);
2025-07-04 15:54:02 +05:00
const resetToDefaults = useSettingsStore(state => state.resetToDefaults);
const initializeSettings = useSettingsStore(state => state.initializeSettings);
2025-06-27 17:50:08 +05:00
2025-07-04 15:54:02 +05:00
const theme = useTheme()
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
2025-07-04 15:54:02 +05:00
// Проверка действий
const handleValidationError = useCallback((fieldId: string, error: string | null) => {
setFieldErrors(prev => {
const newErrors = { ...prev };
if (error) {
newErrors[fieldId] = error;
} else {
delete newErrors[fieldId];
}
return newErrors;
});
}, []);
2025-06-27 17:50:08 +05:00
const settingsBySection = useMemo(() => {
return settingsConfig.reduce((acc, setting) => {
2025-06-27 17:50:08 +05:00
if (!acc[setting.sectionId]) {
acc[setting.sectionId] = [];
}
acc[setting.sectionId].push({
...setting,
value: settingsValues[setting.id] ?? setting.defaultValue
});
2025-06-27 17:50:08 +05:00
return acc;
}, {} as Record<string, Setting[]>);
}, [settingsConfig, settingsValues]);
2025-06-27 17:50:08 +05:00
const sections = useMemo(() => Object.keys(settingsBySection), [settingsBySection]);
2025-06-27 17:50:08 +05:00
useEffect(() => {
initializeSettings();
}, [initializeSettings]);
2025-06-27 17:50:08 +05:00
useEffect(() => {
if (sections.length > 0 && !activeSection) {
setActiveSection(sections[0]);
}
}, [sections, activeSection, setActiveSection]);
const handleSave = useCallback(() => {
const hasErrors = Object.values(fieldErrors).some(error => error?.trim());
if (hasErrors) {
alert('Исправьте ошибки перед сохранением');
return;
}
const requiredFields = settingsConfig.filter(s => s.required);
const emptyRequiredFields = requiredFields.filter(f => {
const value = pendingChanges[f.id] ?? settingsValues[f.id] ?? f.defaultValue;
return value == null || value === '';
});
if (emptyRequiredFields.length > 0) {
alert('Заполните все обязательные поля');
return;
}
applyChanges();
onClose();
}, [fieldErrors, settingsConfig, pendingChanges, settingsValues, applyChanges, onClose]);
const handleCancel = useCallback(() => {
discardChanges();
setFieldErrors({});
onClose();
}, [discardChanges, onClose]);
2025-07-04 15:54:02 +05:00
const handleReset = () => {
if (window.confirm('Вы уверены, что хотите сбросить все настройки к значениям по умолчанию?')) {
resetToDefaults();
}
};
// Обработка нажатий клавиш
useEffect(() => {
const handleKeyPress = (e: React.KeyboardEvent<HTMLElement>) => {
if (e.key === 'Enter')
handleSave();
if (e.key === 'Escape')
handleCancel()
};
document.addEventListener('keydown', handleKeyPress);
return () => {
document.removeEventListener('keydown', handleKeyPress);
};
}, []);
2025-06-27 17:50:08 +05:00
return (
<ModalContainer style={{ height: '60vh', width: '50vw', minWidth: "800px" }}>
2025-06-27 17:50:08 +05:00
<ModalTitle>
<h3>Настройки</h3>
<CloseBtn>
<IconComponent name={buttonNames[12]} width="30" height="30" onClick={handleCancel} />
2025-06-27 17:50:08 +05:00
</CloseBtn>
</ModalTitle>
<ModalContent style={{ display: 'flex', height:"100%", gap: '24px', padding:0 }}>
<SectionsSidebar>
{sections.map((sectionId) => (
<SectionsBTN
key={sectionId}
className={activeSection === sectionId ? 'active' : ''}
onClick={()=>setActiveSection(sectionId)}
>
{sectionId}
</SectionsBTN>
))}
</SectionsSidebar>
<SectionLine/>
<SectionActive>
{activeSection && settingsBySection[activeSection] && (
<>
{settingsBySection[activeSection].map((setting) => (
<SettingItem
key={setting.id}
setting={setting}
value={getSetting(setting.id).currentValue}
onValidationError={(error) => handleValidationError(setting.id, error)}
onChange={(value) => updateSetting(setting.id, value)}
/>
2025-06-27 17:50:08 +05:00
))}
</>
)}
</SectionActive>
</ModalContent>
<ButtonContainer>
2025-07-04 15:54:02 +05:00
<SettingsButtons>
<MainButton
type="button"
size="small"
title='Сбросить настройки'
onClick={handleReset}
color={theme.offlineColor}
hoverColor={theme.borderColor}
/>
<span>
<MainButton
type="button"
size="small"
title='Отменить'
onClick={handleCancel}
color={theme.offlineColor}
hoverColor={theme.borderColor}
/>
<MainButton
type="button"
size="small"
title='Сохранить'
onClick={handleSave}
/>
</span>
</SettingsButtons>
2025-06-27 17:50:08 +05:00
</ButtonContainer>
</ModalContainer>
);
};