feat(SettingsModal): enhance server selection for ping services with autocomplete input and load server list from API
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m58s

This commit is contained in:
2026-02-16 23:40:33 +07:00
parent 838ca6b220
commit ee357ed59a
+33 -17
View File
@@ -1,8 +1,9 @@
import FormField from './FormField';
import { useEffect, useState } from 'react';
import { useEffect, useState, useMemo } from 'react';
import api from '../lib/api.js';
import { IconSettings, IconDeviceFloppy } from '@tabler/icons-react';
import ErrorAlert from './ErrorAlert';
import ServerAutocompleteInput from './ServerAutocompleteInput.jsx';
/**
* SettingsModal - модальное окно настроек интерфейса
@@ -25,17 +26,27 @@ export default function SettingsModal({ open, onClose }) {
const [pingServicesSource, setPingServicesSource] = useState('web');
const [pingServicesServerId, setPingServicesServerId] = useState('');
const [pingServicesGatewayIp, setPingServicesGatewayIp] = useState('');
const [serversList, setServersList] = useState([]);
const routerServersForPing = useMemo(() => {
return (serversList || []).filter(
(s) => s && (String(s.type || '').toLowerCase() === 'jumphost' || String(s.type || '').toLowerCase() === 'home')
);
}, [serversList]);
useEffect(() => {
if (!open) return;
setError('');
setSuccess('');
setLoading(true);
(async () => {
try {
const res = await api.get('/ui-settings');
const data = res?.data || {};
const [settingsRes, serversRes] = await Promise.all([
api.get('/ui-settings'),
api.get('/servers').catch(() => ({ data: [] })),
]);
const data = settingsRes?.data || {};
setRawSettings(data);
setDohServer(String(data?.dohServer || ''));
setWsUrl(String(data?.wsUpdateUrl || ''));
@@ -47,8 +58,9 @@ export default function SettingsModal({ open, onClose }) {
setPingServicesSource(String(data?.pingServicesSource || 'web').toLowerCase() === 'router' ? 'router' : 'web');
setPingServicesServerId(String(data?.pingServicesServerId || '').trim());
setPingServicesGatewayIp(String(data?.pingServicesGatewayIp || '').trim());
const e = res?.headers?.etag || res?.headers?.ETag || '';
const e = settingsRes?.headers?.etag || settingsRes?.headers?.ETag || '';
setEtag(e ? String(e) : '');
setServersList(Array.isArray(serversRes?.data) ? serversRes.data : []);
} catch (e) {
setError('Не удалось загрузить настройки');
} finally {
@@ -256,26 +268,30 @@ export default function SettingsModal({ open, onClose }) {
{pingServicesSource === 'router' && (
<div className="row g-2">
<div className="col-md-6">
<FormField
label="Роутер (jumphost)"
name="pingServicesServerId"
type="text"
value={pingServicesServerId}
onChange={setPingServicesServerId}
placeholder="ID, DNS или IP jumphost"
helpText="Оставьте пустым — будет использован первый jumphost из списка серверов."
disabled={loading || saving}
/>
<label className="form-label small">Домашний роутер</label>
<div className={loading || saving ? 'opacity-75 pe-none' : ''}>
<ServerAutocompleteInput
value={pingServicesServerId}
onChange={(v) => setPingServicesServerId(String(v || '').trim())}
servers={routerServersForPing}
placeholder="Выберите роутер (jumphost или входной)"
className="form-control form-control-sm"
maxSuggestions={10}
/>
</div>
<div className="form-text small">
Выбор роутера с MikroTik API для пинга с главной. Пусто первый jumphost из списка.
</div>
</div>
<div className="col-md-6">
<FormField
label="IP шлюза (опционально)"
label="IP шлюза (не обязательно)"
name="pingServicesGatewayIp"
type="text"
value={pingServicesGatewayIp}
onChange={setPingServicesGatewayIp}
placeholder="IP шлюза"
helpText="Оставьте пустымберётся первый шлюз роутера."
helpText="Не обязательно. Пустоиспользуется первый шлюз выбранного роутера."
disabled={loading || saving}
/>
</div>