Добавление настроек в localStorage
This commit is contained in:
parent
f8ddae0f94
commit
11237605b9
4 changed files with 137 additions and 63 deletions
|
|
@ -66,19 +66,23 @@ const LabelCheckbox = styled.label`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
interface SettingItemProps {
|
||||||
|
setting: Setting;
|
||||||
|
value: any;
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
export const SettingItem = ({ setting }: { setting: Setting }) => {
|
export const SettingItem = ({ setting, value, onChange }: SettingItemProps) => {
|
||||||
const { updateSetting } = useSettingsStore();
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const { updateSetting } = useSettingsStore();
|
||||||
|
|
||||||
const handleChange = (
|
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||||
e: ChangeEvent<HTMLInputElement | HTMLSelectElement>
|
const newValue = setting.type === 'boolean'
|
||||||
) => {
|
? (e.target as HTMLInputElement).checked
|
||||||
const value =
|
: setting.type === 'number'
|
||||||
setting.type === 'boolean'
|
? Number(e.target.value)
|
||||||
? (e.target as HTMLInputElement).checked
|
|
||||||
: e.target.value;
|
: e.target.value;
|
||||||
updateSetting(setting.id, value);
|
onChange(newValue);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -122,7 +126,7 @@ export const SettingItem = ({ setting }: { setting: Setting }) => {
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title={setting.value}
|
title={setting.defaultValue || ''}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</SettingBlock>
|
</SettingBlock>
|
||||||
|
|
|
||||||
|
|
@ -53,32 +53,45 @@ const CloseBtn = styled.span`
|
||||||
`
|
`
|
||||||
|
|
||||||
export const Settings = ({onClose}: Props) => {
|
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(() => {
|
useEffect(() => {
|
||||||
initializeSettings();
|
initializeSettings();
|
||||||
}, [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(() => {
|
useEffect(() => {
|
||||||
if (sections.length > 0 && !activeSection) {
|
if (sections.length > 0 && !activeSection) {
|
||||||
setActiveSection(sections[0]);
|
setActiveSection(sections[0]);
|
||||||
}
|
}
|
||||||
}, [sections, activeSection, setActiveSection]);
|
}, [sections, activeSection, setActiveSection]);
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContainer style={{ height: '60vh', width: '50vw'}}>
|
<ModalContainer style={{ height: '60vh', width: '50vw'}}>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
|
|
@ -104,8 +117,13 @@ export const Settings = ({onClose}: Props) => {
|
||||||
<SectionActive>
|
<SectionActive>
|
||||||
{activeSection && settingsBySection[activeSection] && (
|
{activeSection && settingsBySection[activeSection] && (
|
||||||
<>
|
<>
|
||||||
{settingsBySection[activeSection].map((setting)=>(
|
{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"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title='Сохранить'
|
title='Сохранить'
|
||||||
onClick={onClose}
|
onClick={handleSave}
|
||||||
/>
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</ModalContainer>
|
</ModalContainer>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import React, {useState} from 'react';
|
import React, {useEffect, useState} from 'react';
|
||||||
import { DeviceUser } from '../../../../../../store/useDeviceStore';
|
import { DeviceUser } from '../../../../../../store/useDeviceStore';
|
||||||
|
|
||||||
interface TableScannerProps {
|
interface TableScannerProps {
|
||||||
|
|
@ -10,6 +10,7 @@ import {buttonNames} from "../../../../../../const/buttons/names";
|
||||||
import {ModalMap} from "../../../../../modalwindows/windowsType";
|
import {ModalMap} from "../../../../../modalwindows/windowsType";
|
||||||
import {useModalContext} from "../../../../../../store";
|
import {useModalContext} from "../../../../../../store";
|
||||||
import Pagination from "../../../../../UI/Pagination";
|
import Pagination from "../../../../../UI/Pagination";
|
||||||
|
import {useSettingsStore} from "../../../../../../store/settingsStore";
|
||||||
|
|
||||||
const TableContainer = styled.div`
|
const TableContainer = styled.div`
|
||||||
width: 100%;
|
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 [currentPage, setCurrentPage] = useState(1);
|
||||||
const [devicesPerPage] = useState(15);
|
const [devicesPerPage, setDevicesPerPage] = useState(paginateSetting);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setDevicesPerPage(paginateSetting);
|
||||||
|
}, [paginateSetting]);
|
||||||
|
|
||||||
const indexOfLastDevice = currentPage * devicesPerPage;
|
const indexOfLastDevice = currentPage * devicesPerPage;
|
||||||
const indexOfFirstDevice = indexOfLastDevice - devicesPerPage;
|
const indexOfFirstDevice = indexOfLastDevice - devicesPerPage;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,15 @@
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
|
||||||
|
type SettingValue = any;
|
||||||
|
type SettingsDictionary = Record<string, SettingValue>;
|
||||||
|
|
||||||
export type Setting = {
|
export type Setting = {
|
||||||
id: string;
|
id: string;
|
||||||
type: 'boolean' | 'select' | 'text' | 'number' | 'button';
|
type: 'boolean' | 'select' | 'text' | 'number' | 'button';
|
||||||
label: string;
|
label: string;
|
||||||
value: any;
|
defaultValue: any;
|
||||||
|
value?: string;
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
options?: { value: any; label: string }[];
|
options?: { value: any; label: string }[];
|
||||||
|
|
@ -12,42 +17,31 @@ export type Setting = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type SettingsState = {
|
type SettingsState = {
|
||||||
settings: Setting[];
|
settingsConfig: Setting[];
|
||||||
|
settingsValues: SettingsDictionary;
|
||||||
activeSection: string | null;
|
activeSection: string | null;
|
||||||
setActiveSection: (sectionId: string | null) => void;
|
setActiveSection: (sectionId: string | null) => void;
|
||||||
updateSetting: (id: string, value: any) => void;
|
updateSetting: (id: string, value: any) => void;
|
||||||
initializeSettings: () => void;
|
initializeSettings: () => void;
|
||||||
|
getSetting: (id: string) => Setting & { currentValue: any };
|
||||||
|
resetSettings: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSettingsStore = create<SettingsState>((set) => ({
|
const defaultSettings: Setting[] = [
|
||||||
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[] = [
|
|
||||||
// Узлы подсети
|
// Узлы подсети
|
||||||
{
|
{
|
||||||
id: 'rescan-nodes',
|
id: 'rescan-nodes',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'Узлы подсети',
|
||||||
label: 'Интервальное сканирование',
|
label: 'Интервальное сканирование',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
value: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'interval-nodes',
|
id: 'interval-nodes',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'Узлы подсети',
|
||||||
label: 'Интервал сканирования узлов (мин)',
|
label: 'Интервал сканирования узлов (мин)',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
value: 60,
|
defaultValue: 60,
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 2880
|
max: 2880
|
||||||
},
|
},
|
||||||
|
|
@ -56,7 +50,7 @@ export const useSettingsStore = create<SettingsState>((set) => ({
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'Узлы подсети',
|
||||||
label: 'Отображаемые имена при отсутствии',
|
label: 'Отображаемые имена при отсутствии',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
value: 'ip',
|
defaultValue: 'ip',
|
||||||
options: [
|
options: [
|
||||||
{ value: 'ip', label: 'IP адрес' },
|
{ value: 'ip', label: 'IP адрес' },
|
||||||
{ value: 'unknown', label: 'Неизвестно' },
|
{ value: 'unknown', label: 'Неизвестно' },
|
||||||
|
|
@ -65,9 +59,9 @@ export const useSettingsStore = create<SettingsState>((set) => ({
|
||||||
{
|
{
|
||||||
id: 'paginate-nodes',
|
id: 'paginate-nodes',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
label: 'Количество строк на таблице',
|
label: 'Количество строк в таблице',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'Узлы подсети',
|
||||||
value: 20,
|
defaultValue: 20,
|
||||||
min: 1,
|
min: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -75,21 +69,21 @@ export const useSettingsStore = create<SettingsState>((set) => ({
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'Отображение узлов',
|
label: 'Отображение узлов',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'Узлы подсети',
|
||||||
value: 'Настроить',
|
defaultValue: 'Настроить',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'clean-nodes',
|
id: 'clean-nodes',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'Очистить данные сессии',
|
label: 'Очистить данные сессии',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'Узлы подсети',
|
||||||
value: 'Очистить',
|
defaultValue: 'Очистить',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'export-nodes',
|
id: 'export-nodes',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'Экспорт данных узлов',
|
label: 'Экспорт данных узлов',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'Узлы подсети',
|
||||||
value: 'Экспортировать',
|
defaultValue: 'Экспортировать',
|
||||||
},
|
},
|
||||||
|
|
||||||
// Точки доступа
|
// Точки доступа
|
||||||
|
|
@ -98,14 +92,14 @@ export const useSettingsStore = create<SettingsState>((set) => ({
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'Очистить данные сессии',
|
label: 'Очистить данные сессии',
|
||||||
sectionId: 'Точки доступа',
|
sectionId: 'Точки доступа',
|
||||||
value: 'Очистить',
|
defaultValue: 'Очистить',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'export-network',
|
id: 'export-network',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'Экспорт данных точек доступа',
|
label: 'Экспорт данных точек доступа',
|
||||||
sectionId: 'Точки доступа',
|
sectionId: 'Точки доступа',
|
||||||
value: 'Экспортировать',
|
defaultValue: 'Экспортировать',
|
||||||
},
|
},
|
||||||
|
|
||||||
// Внешний вид
|
// Внешний вид
|
||||||
|
|
@ -114,7 +108,7 @@ export const useSettingsStore = create<SettingsState>((set) => ({
|
||||||
sectionId: 'Внешний вид',
|
sectionId: 'Внешний вид',
|
||||||
label: 'Цветовая тема',
|
label: 'Цветовая тема',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
value: 'light',
|
defaultValue: 'light',
|
||||||
options: [
|
options: [
|
||||||
{ value: 'light', label: 'Светлая' },
|
{ value: 'light', label: 'Светлая' },
|
||||||
{ value: 'dark', label: 'Тёмная' },
|
{ value: 'dark', label: 'Тёмная' },
|
||||||
|
|
@ -126,13 +120,60 @@ export const useSettingsStore = create<SettingsState>((set) => ({
|
||||||
sectionId: 'Внешний вид',
|
sectionId: 'Внешний вид',
|
||||||
label: 'Язык интерфейса',
|
label: 'Язык интерфейса',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
value: 'ru',
|
defaultValue: 'ru',
|
||||||
options: [
|
options: [
|
||||||
{ value: 'ru', label: 'Русский' },
|
{ value: 'ru', label: 'Русский' },
|
||||||
{ value: 'en', 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue