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

232 lines
No EOL
8.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../modalwindows/style";
import { MainButton } from "../UI/buttons/mainButton";
import {useCallback, useEffect, useMemo, useState} from "react";
import styled, {useTheme} from "styled-components";
import {Setting, useSettingsStore} from "../../store/settings/settingsStore";
import {SettingItem} from "./SettingItem";
import {IconComponent} from "../../const/buttons/icons";
import {buttonNames} from "../../const/buttons/names";
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;
`
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);
const resetToDefaults = useSettingsStore(state => state.resetToDefaults);
const initializeSettings = useSettingsStore(state => state.initializeSettings);
const theme = useTheme()
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
// Проверка действий
const handleValidationError = useCallback((fieldId: string, error: string | null) => {
setFieldErrors(prev => {
const newErrors = { ...prev };
if (error) {
newErrors[fieldId] = error;
} else {
delete newErrors[fieldId];
}
return newErrors;
});
}, []);
const settingsBySection = useMemo(() => {
return settingsConfig.reduce((acc, setting) => {
if (!acc[setting.sectionId]) {
acc[setting.sectionId] = [];
}
acc[setting.sectionId].push({
...setting,
value: settingsValues[setting.id] ?? setting.defaultValue
});
return acc;
}, {} as Record<string, Setting[]>);
}, [settingsConfig, settingsValues]);
const sections = useMemo(() => Object.keys(settingsBySection), [settingsBySection]);
useEffect(() => {
initializeSettings();
}, [initializeSettings]);
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]);
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);
};
}, []);
return (
<ModalContainer style={{ height: '60vh', width: '50vw', minWidth: "800px" }}>
<ModalTitle>
<h3>Настройки</h3>
<CloseBtn>
<IconComponent name={buttonNames[12]} width="30" height="30" onClick={handleCancel} />
</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)}
/>
))}
</>
)}
</SectionActive>
</ModalContent>
<ButtonContainer>
<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>
</ButtonContainer>
</ModalContainer>
);
};