gonets/FRONT/DEV/src/components/settings/Settings.tsx
test ee350c1852
Some checks failed
ci/woodpecker/manual/woodpecker Pipeline failed
Добавление настроек и их функциональности
2025-07-04 15:07:41 +05:00

186 lines
No EOL
6.5 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 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;
`
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 initializeSettings = useSettingsStore(state => state.initializeSettings);
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]);
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>
<MainButton
type="button"
size="small"
title='Отменить'
onClick={handleCancel}
/>
<MainButton
type="button"
size="small"
title='Сохранить'
onClick={handleSave}
/>
</ButtonContainer>
</ModalContainer>
);
};