Добавление настроек в localStorage

This commit is contained in:
test 2025-07-01 17:21:42 +05:00
parent f8ddae0f94
commit 11237605b9
4 changed files with 137 additions and 63 deletions

View file

@ -66,19 +66,23 @@ const LabelCheckbox = styled.label`
}
`
interface SettingItemProps {
setting: Setting;
value: any;
onChange: (value: any) => void;
}
export const SettingItem = ({ setting }: { setting: Setting }) => {
const { updateSetting } = useSettingsStore();
export const SettingItem = ({ setting, value, onChange }: SettingItemProps) => {
const theme = useTheme();
const { updateSetting } = useSettingsStore();
const handleChange = (
e: ChangeEvent<HTMLInputElement | HTMLSelectElement>
) => {
const value =
setting.type === 'boolean'
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const newValue = setting.type === 'boolean'
? (e.target as HTMLInputElement).checked
: setting.type === 'number'
? Number(e.target.value)
: e.target.value;
updateSetting(setting.id, value);
onChange(newValue);
};
return (
@ -122,7 +126,7 @@ export const SettingItem = ({ setting }: { setting: Setting }) => {
<MainButton
type="button"
size="small"
title={setting.value}
title={setting.defaultValue || ''}
/>
)}
</SettingBlock>

View file

@ -53,32 +53,45 @@ const CloseBtn = styled.span`
`
export const Settings = ({onClose}: Props) => {
const {settings, activeSection, setActiveSection, initializeSettings} = useSettingsStore();
const {
settingsConfig,
settingsValues,
activeSection,
setActiveSection,
initializeSettings,
updateSetting
} = useSettingsStore();
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 = Object.keys(settingsBySection);
useEffect(() => {
initializeSettings();
}, [initializeSettings]);
const settingsBySection = useMemo(() => {
return settings.reduce((acc, setting) => {
if (!acc[setting.sectionId]) {
acc[setting.sectionId] = [];
}
acc[setting.sectionId].push(setting);
return acc;
}, {} as Record<string, Setting[]>);
}, [settings]);
const sections = Object.keys(settingsBySection);
useEffect(() => {
if (sections.length > 0 && !activeSection) {
setActiveSection(sections[0]);
}
}, [sections, activeSection, setActiveSection]);
const handleSave = () => {
onClose();
};
return (
<ModalContainer style={{ height: '60vh', width: '50vw'}}>
<ModalTitle>
@ -105,7 +118,12 @@ export const Settings = ({onClose}: Props) => {
{activeSection && settingsBySection[activeSection] && (
<>
{settingsBySection[activeSection].map((setting) => (
<SettingItem setting={setting} key={setting.id}/>
<SettingItem
key={setting.id}
setting={setting}
value={settingsValues[setting.id] ?? setting.defaultValue}
onChange={(value) => updateSetting(setting.id, value)}
/>
))}
</>
)}
@ -117,7 +135,7 @@ export const Settings = ({onClose}: Props) => {
type="button"
size="small"
title='Сохранить'
onClick={onClose}
onClick={handleSave}
/>
</ButtonContainer>
</ModalContainer>

View file

@ -1,4 +1,4 @@
import React, {useState} from 'react';
import React, {useEffect, useState} from 'react';
import { DeviceUser } from '../../../../../../store/useDeviceStore';
interface TableScannerProps {
@ -10,6 +10,7 @@ import {buttonNames} from "../../../../../../const/buttons/names";
import {ModalMap} from "../../../../../modalwindows/windowsType";
import {useModalContext} from "../../../../../../store";
import Pagination from "../../../../../UI/Pagination";
import {useSettingsStore} from "../../../../../../store/settingsStore";
const TableContainer = styled.div`
width: 100%;
@ -74,9 +75,19 @@ const TableScanner = ({devices}:TableScannerProps) => {
});
};
const paginateSetting = useSettingsStore(state =>
state.settingsValues['paginate-nodes'] ??
state.settingsConfig.find(s => s.id === 'paginate-nodes')?.defaultValue ??
15
);
// Пагинация
const [currentPage, setCurrentPage] = useState(1);
const [devicesPerPage] = useState(15);
const [devicesPerPage, setDevicesPerPage] = useState(paginateSetting);
useEffect(() => {
setDevicesPerPage(paginateSetting);
}, [paginateSetting]);
const indexOfLastDevice = currentPage * devicesPerPage;
const indexOfFirstDevice = indexOfLastDevice - devicesPerPage;

View file

@ -1,10 +1,15 @@
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
type SettingValue = any;
type SettingsDictionary = Record<string, SettingValue>;
export type Setting = {
id: string;
type: 'boolean' | 'select' | 'text' | 'number' | 'button';
label: string;
value: any;
defaultValue: any;
value?: string;
min?: number;
max?: number;
options?: { value: any; label: string }[];
@ -12,27 +17,16 @@ export type Setting = {
};
type SettingsState = {
settings: Setting[];
settingsConfig: Setting[];
settingsValues: SettingsDictionary;
activeSection: string | null;
setActiveSection: (sectionId: string | null) => void;
updateSetting: (id: string, value: any) => void;
initializeSettings: () => void;
getSetting: (id: string) => Setting & { currentValue: any };
resetSettings: () => void;
};
export const useSettingsStore = create<SettingsState>((set) => ({
settings: [],
activeSection: null,
setActiveSection: (sectionId) => set({ activeSection: sectionId }),
updateSetting: (id, value) =>
set((state) => ({
settings: state.settings.map((setting) =>
setting.id === id ? { ...setting, value } : setting
),
})),
initializeSettings: () => {
const defaultSettings: Setting[] = [
// Узлы подсети
{
@ -40,14 +34,14 @@ export const useSettingsStore = create<SettingsState>((set) => ({
sectionId: 'Узлы подсети',
label: 'Интервальное сканирование',
type: 'boolean',
value: false,
defaultValue: false,
},
{
id: 'interval-nodes',
sectionId: 'Узлы подсети',
label: 'Интервал сканирования узлов (мин)',
type: 'number',
value: 60,
defaultValue: 60,
min: 1,
max: 2880
},
@ -56,7 +50,7 @@ export const useSettingsStore = create<SettingsState>((set) => ({
sectionId: 'Узлы подсети',
label: 'Отображаемые имена при отсутствии',
type: 'select',
value: 'ip',
defaultValue: 'ip',
options: [
{ value: 'ip', label: 'IP адрес' },
{ value: 'unknown', label: 'Неизвестно' },
@ -65,9 +59,9 @@ export const useSettingsStore = create<SettingsState>((set) => ({
{
id: 'paginate-nodes',
type: 'number',
label: 'Количество строк на таблице',
label: 'Количество строк в таблице',
sectionId: 'Узлы подсети',
value: 20,
defaultValue: 20,
min: 1,
},
{
@ -75,21 +69,21 @@ export const useSettingsStore = create<SettingsState>((set) => ({
type: 'button',
label: 'Отображение узлов',
sectionId: 'Узлы подсети',
value: 'Настроить',
defaultValue: 'Настроить',
},
{
id: 'clean-nodes',
type: 'button',
label: 'Очистить данные сессии',
sectionId: 'Узлы подсети',
value: 'Очистить',
defaultValue: 'Очистить',
},
{
id: 'export-nodes',
type: 'button',
label: 'Экспорт данных узлов',
sectionId: 'Узлы подсети',
value: 'Экспортировать',
defaultValue: 'Экспортировать',
},
// Точки доступа
@ -98,14 +92,14 @@ export const useSettingsStore = create<SettingsState>((set) => ({
type: 'button',
label: 'Очистить данные сессии',
sectionId: 'Точки доступа',
value: 'Очистить',
defaultValue: 'Очистить',
},
{
id: 'export-network',
type: 'button',
label: 'Экспорт данных точек доступа',
sectionId: 'Точки доступа',
value: 'Экспортировать',
defaultValue: 'Экспортировать',
},
// Внешний вид
@ -114,7 +108,7 @@ export const useSettingsStore = create<SettingsState>((set) => ({
sectionId: 'Внешний вид',
label: 'Цветовая тема',
type: 'select',
value: 'light',
defaultValue: 'light',
options: [
{ value: 'light', label: 'Светлая' },
{ value: 'dark', label: 'Тёмная' },
@ -126,13 +120,60 @@ export const useSettingsStore = create<SettingsState>((set) => ({
sectionId: 'Внешний вид',
label: 'Язык интерфейса',
type: 'select',
value: 'ru',
defaultValue: 'ru',
options: [
{ value: 'ru', label: 'Русский' },
{ value: 'en', label: 'Английский' },
],
},
];
set({ settings: defaultSettings });
export const useSettingsStore = create<SettingsState>()(
persist(
(set, get) => ({
settingsConfig: defaultSettings,
settingsValues: {},
activeSection: null,
setActiveSection: (sectionId) => set({ activeSection: sectionId }),
updateSetting: (id, value) =>
set((state) => ({
settingsValues: { ...state.settingsValues, [id]: value }
})),
initializeSettings: () => {
const { settingsValues, settingsConfig } = get();
const newValues = { ...settingsValues };
settingsConfig.forEach(setting => {
if (newValues[setting.id] === undefined) {
newValues[setting.id] = setting.defaultValue;
}
});
set({ settingsValues: newValues });
},
}));
getSetting: (id) => {
const setting = get().settingsConfig.find(s => s.id === id);
if (!setting) throw new Error(`Setting ${id} not found`);
return {
...setting,
currentValue: get().settingsValues[id] ?? setting.defaultValue
};
},
resetSettings: () => set({ settingsValues: {} }),
}),
{
name: 'app-settings',
partialize: (state) => ({
settingsValues: state.settingsValues
}),
onRehydrateStorage: () => (state) => {
state?.initializeSettings();
}
}
)
);