diff --git a/frontend/src/SettingsPage.jsx b/frontend/src/SettingsPage.jsx index b4237d7..17da6f6 100644 --- a/frontend/src/SettingsPage.jsx +++ b/frontend/src/SettingsPage.jsx @@ -1,4 +1,5 @@ import { useEffect, useState, useMemo } from 'react'; +import { useLocation, useNavigate } from 'react-router-dom'; import api from './lib/api.js'; import { IconSettings, @@ -8,7 +9,7 @@ import { IconCloud, IconChartPie, IconWorld, - IconChevronDown, + IconSearch, } from '@tabler/icons-react'; import FormField from './components/FormField'; import ErrorAlert from './components/ErrorAlert'; @@ -19,7 +20,18 @@ import ServerAutocompleteInput from './components/ServerAutocompleteInput.jsx'; * Страница «Настройки интерфейса» — отдельный раздел в стиле Tabler UI и UniFi: * коллапсируемые карточки-секции, иконки, чёткая структура. */ +// Разделы бокового меню (как в Tabler / UniFi) +const SIDEBAR_SECTIONS = [ + { id: 'live-doh', title: 'BGP Live и DoH', icon: IconPlugConnected }, + { id: 'network-as', title: 'Сеть и AS', icon: IconNetwork }, + { id: 'ping', title: 'Пинг через MikroTik', icon: IconCloud }, + { id: 'ping-services', title: 'Пинг на главной', icon: IconChartPie }, + { id: 'ptr-zone', title: 'PTR зона', icon: IconWorld }, +]; + export default function SettingsPage() { + const location = useLocation(); + const navigate = useNavigate(); const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [error, setError] = useState(''); @@ -38,6 +50,11 @@ export default function SettingsPage() { const [pingServicesGatewayIp, setPingServicesGatewayIp] = useState(''); const [pingServicesCacheSeconds, setPingServicesCacheSeconds] = useState(''); const [serversList, setServersList] = useState([]); + const [sidebarSearch, setSidebarSearch] = useState(''); + const [activeSection, setActiveSection] = useState(() => { + const hash = (typeof location.hash === 'string' && location.hash.slice(1)) || ''; + return SIDEBAR_SECTIONS.some((s) => s.id === hash) ? hash : SIDEBAR_SECTIONS[0].id; + }); const routerServersForPing = useMemo(() => { return (serversList || []).filter( @@ -48,6 +65,26 @@ export default function SettingsPage() { ); }, [serversList]); + const sidebarItems = useMemo(() => { + const q = (sidebarSearch || '').trim().toLowerCase(); + if (!q) return SIDEBAR_SECTIONS; + return SIDEBAR_SECTIONS.filter( + (s) => s.title.toLowerCase().includes(q) || s.id.toLowerCase().includes(q) + ); + }, [sidebarSearch]); + + useEffect(() => { + const hash = (location.hash || '').slice(1); + if (hash && SIDEBAR_SECTIONS.some((s) => s.id === hash)) { + setActiveSection(hash); + } + }, [location.hash]); + + const goToSection = (id) => { + setActiveSection(id); + navigate(`/settings#${id}`, { replace: true }); + }; + useEffect(() => { setError(''); setSuccess(''); @@ -173,19 +210,11 @@ export default function SettingsPage() { } }; - // Коллапсируемая карточка в стиле UniFi - function SectionCard({ id, title, icon: Icon, defaultOpen = true, children }) { - const [open, setOpen] = useState(defaultOpen); + // Карточка секции (контент без коллапса — разделы переключаются через боковое меню) + function SectionCard({ id, title, icon: Icon, children }) { return ( -
-
setOpen((v) => !v)} - aria-expanded={open} - aria-controls={`settings-section-${id}`} - id={`settings-heading-${id}`} - > +
+

{Icon && ( @@ -194,20 +223,8 @@ export default function SettingsPage() { )} {title}

-
- {open && ( -
- {children} -
- )} +
{children}
); } @@ -285,13 +302,56 @@ export default function SettingsPage() { )}
-
- {/* Секция: BGP Live и DoH */} + {/* Боковое меню в стиле Tabler / UniFi */} +
+
+
+
+ + + + setSidebarSearch(e.target.value)} + aria-label="Поиск разделов" + /> +
+ + {sidebarItems.length === 0 && ( +

Нет подходящих разделов

+ )} +
+
+
+ + {/* Контент выбранного раздела */} +
+ {activeSection === 'live-doh' && (
@@ -322,13 +382,13 @@ export default function SettingsPage() {
+ )} - {/* Секция: Сеть и AS */} + {activeSection === 'network-as' && (
@@ -345,13 +405,13 @@ export default function SettingsPage() {
+ )} - {/* Секция: Пинг (домен и кеш) */} + {activeSection === 'ping' && (
@@ -381,13 +441,13 @@ export default function SettingsPage() {
+ )} - {/* Секция: Пинг сервисов на главной */} + {activeSection === 'ping-services' && (
@@ -456,13 +516,13 @@ export default function SettingsPage() {
)}
+ )} - {/* Секция: PTR зона */} + {activeSection === 'ptr-zone' && (
@@ -491,6 +551,7 @@ export default function SettingsPage() {
+ )}