diff --git a/frontend/src/MikrotikTools.jsx b/frontend/src/MikrotikTools.jsx index 87fd06b..697d1ce 100644 --- a/frontend/src/MikrotikTools.jsx +++ b/frontend/src/MikrotikTools.jsx @@ -1,13 +1,17 @@ import { useEffect, useMemo, useState } from 'react'; import api from './lib/api.js'; import { useNotify } from './components/NotifyProvider.jsx'; -import ServerAutocompleteInput from './components/ServerAutocompleteInput.jsx'; import GatewayAutocompleteInput from './components/GatewayAutocompleteInput.jsx'; +import PageHeader from './components/PageHeader.jsx'; +import TableSkeleton from './components/TableSkeleton.jsx'; +import EmptyState from './components/EmptyState.jsx'; +import { countryToFlag } from './utils/serverUtils.js'; import { IconRoute, IconWorld, IconAlertTriangle, IconRefresh, + IconSearch, } from '@tabler/icons-react'; /** @@ -32,6 +36,8 @@ function MikrotikTools() { const [analysis, setAnalysis] = useState([]); const [useDns, setUseDns] = useState(true); + const [serverSearch, setServerSearch] = useState(''); + // Загрузка серверов и сетевого конфига useEffect(() => { const load = async () => { @@ -128,22 +134,145 @@ function MikrotikTools() { setAnalysis([]); }; + const jumphostServers = useMemo( + () => (servers || []).filter((s) => String(s.type || '').toLowerCase() === 'jumphost'), + [servers] + ); + + const makeServerId = (s) => (s.id || s.dns || s.ip || '').toString(); + + const jumphostInputServers = useMemo( + () => jumphostServers.map((s) => ({ ...s, id: makeServerId(s) })), + [jumphostServers] + ); + + const visibleJumphostServers = useMemo(() => { + if (!serverSearch.trim()) return jumphostInputServers; + const q = serverSearch.trim().toLowerCase(); + return jumphostInputServers.filter((s) => { + const fields = [ + s.id, + s.ip, + s.dns, + s.extIp, + s.internalIp, + s.country, + s.provider, + ] + .filter(Boolean) + .join(' ') + .toLowerCase(); + return fields.includes(q); + }); + }, [jumphostInputServers, serverSearch]); + + const currentServer = useMemo( + () => jumphostServers.find((s) => makeServerId(s) === serverId) || null, + [jumphostServers, serverId] + ); + + const currentServerLabel = + currentServer?.dns || currentServer?.ip || makeServerId(currentServer || {}) || 'Не выбран'; + + const isInitialLoading = loading && servers.length === 0; + return ( -
-
-
-
-

- - Инструменты MikroTik: трассировка -

-
- Проверка маршрута до домена/IP через выбранный jumphost и gateway с расшифровкой хопов. +
+ + + {/* Выбор сервера для трассировки (в стиле MikrotikBackups / filters) */} +
+
+
+
+

Сервер для трассировки

+
+ Доступно Jumphost-серверов: {jumphostServers.length}.{' '} + {serverId + ? 'Кликните по другой карточке, чтобы сменить сервер.' + : 'Выберите сервер, чтобы выполнять трассировку через него.'} +
+
+
+ setServerSearch(e.target.value)} + /> + + + +
+
+
+
+
+ {isInitialLoading ? ( + + ) : jumphostServers.length === 0 ? ( +
+ Нет серверов типа jumphost. Добавьте их в разделе «Серверы», чтобы использовать инструменты MikroTik. +
+ ) : visibleJumphostServers.length === 0 ? ( +
+ По вашему запросу серверы не найдены. Попробуйте изменить фильтр поиска. +
+ ) : ( +
+ {visibleJumphostServers.map((s) => { + const id = s.id; + const isActive = id === serverId; + const flag = s.country ? countryToFlag(s.country) : ''; + const line = [s.dns, s.ip].filter(Boolean).join(' · '); + return ( +
+
setServerId(id)} + > +
+ + {flag || s.ip?.slice(0, 2) || '?'} + +
+
+
+ {s.dns || s.ip || id} +
+
+ {line && ( +
+ {line} +
+ )} +
+ {[s.country, s.provider].filter(Boolean).join(' · ') || '—'} +
+
+
+
+
+ ); + })} +
+ )} +
+ Текущий сервер:  + {currentServerLabel}
+ {/* Параметры трассировки + результаты */}
@@ -151,22 +280,10 @@ function MikrotikTools() {

Параметры проверки

- {loading ? ( -
Загрузка серверов и сетевых настроек…
+ {loading && !networkConfig ? ( +
Загрузка сетевых настроек…
) : ( <> -
- - String(s.type).toLowerCase() === 'jumphost')} - placeholder="Начните вводить IP, DNS или провайдера…" - /> -
- Выберите MikroTik‑jumphost, через который будет выполняться трассировка. -
-