feat: Introduce baseAS state management in FilterManager and SettingsModal, enhancing community search normalization and user settings configuration for improved filtering accuracy.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m54s

This commit is contained in:
2025-12-07 03:23:36 +07:00
parent 2bdb67a715
commit 45bd395870
2 changed files with 50 additions and 3 deletions
+35 -2
View File
@@ -71,7 +71,7 @@ const countryToFlag = (code) => {
}; };
// Компонент автокомплита для выбора Community // Компонент автокомплита для выбора Community
function CommunityAutocomplete({ label, value, onChange, communities = [], required = false, placeholder = 'Введите или выберите community...' }) { function CommunityAutocomplete({ label, value, onChange, communities = [], required = false, placeholder = 'Введите или выберите community...', baseAS = '65001' }) {
const [inputValue, setInputValue] = useState(value || ''); const [inputValue, setInputValue] = useState(value || '');
const [isOpen, setIsOpen] = useState(false); const [isOpen, setIsOpen] = useState(false);
const [highlightedIndex, setHighlightedIndex] = useState(-1); const [highlightedIndex, setHighlightedIndex] = useState(-1);
@@ -89,9 +89,22 @@ function CommunityAutocomplete({ label, value, onChange, communities = [], requi
const cValue = (c.value || '').toLowerCase(); const cValue = (c.value || '').toLowerCase();
// Поиск по полному значению // Нормализуем значение из справочника с учётом baseAS
// Если в справочнике просто число (например "120"), считаем его как "baseAS:120"
const normalizedCValue = cValue.includes(':') ? cValue : `${baseAS}:${cValue}`;
// Нормализуем поисковый запрос
// Если введено просто число, считаем его как "baseAS:число"
const normalizedSearch = search.includes(':') ? search : `${baseAS}:${search}`;
// Поиск по полному значению (с нормализацией)
if (normalizedCValue.includes(search)) return true;
if (cValue.includes(search)) return true; if (cValue.includes(search)) return true;
// Поиск нормализованного значения
if (normalizedCValue === normalizedSearch) return true;
if (normalizedCValue.includes(normalizedSearch)) return true;
// Поиск по части после двоеточия (например, "120" найдёт "65001:120") // Поиск по части после двоеточия (например, "120" найдёт "65001:120")
if (cValue.includes(':')) { if (cValue.includes(':')) {
const parts = cValue.split(':'); const parts = cValue.split(':');
@@ -108,6 +121,11 @@ function CommunityAutocomplete({ label, value, onChange, communities = [], requi
if (searchParts[1] && cValue.includes(searchParts[1])) return true; if (searchParts[1] && cValue.includes(searchParts[1])) return true;
} }
// Поиск просто по числу без AS
if (!search.includes(':') && !cValue.includes(':')) {
if (cValue.includes(search)) return true;
}
// Поиск по названию и описанию // Поиск по названию и описанию
if (c.name && c.name.toLowerCase().includes(search)) return true; if (c.name && c.name.toLowerCase().includes(search)) return true;
if (c.description && c.description.toLowerCase().includes(search)) return true; if (c.description && c.description.toLowerCase().includes(search)) return true;
@@ -586,6 +604,8 @@ function FilterManager() {
// Communities directory for autocomplete // Communities directory for autocomplete
const [communities, setCommunities] = useState([]); const [communities, setCommunities] = useState([]);
// Базовая AS из настроек
const [baseAS, setBaseAS] = useState('65001');
// Inventory servers (from /servers endpoint) // Inventory servers (from /servers endpoint)
const [inventoryServers, setInventoryServers] = useState([]); const [inventoryServers, setInventoryServers] = useState([]);
@@ -647,6 +667,17 @@ function FilterManager() {
})(); })();
useEffect(() => { useEffect(() => {
// Загружаем базовую AS из настроек
(async () => {
try {
const res = await api.get('/ui-settings');
const data = res?.data || {};
if (data?.baseAS) setBaseAS(String(data.baseAS));
} catch (e) {
// мягко игнорируем, используем значение по умолчанию
}
})();
(async () => { (async () => {
try { try {
const res = await api.get(`/communities`); const res = await api.get(`/communities`);
@@ -1815,6 +1846,7 @@ function FilterManager() {
value={newFilter.community} value={newFilter.community}
onChange={(val) => setNewFilter({ ...newFilter, community: val })} onChange={(val) => setNewFilter({ ...newFilter, community: val })}
communities={communities} communities={communities}
baseAS={baseAS}
required required
/> />
@@ -1855,6 +1887,7 @@ function FilterManager() {
value={editingFilter.community} value={editingFilter.community}
onChange={(val) => setEditingFilter({ ...editingFilter, community: val })} onChange={(val) => setEditingFilter({ ...editingFilter, community: val })}
communities={communities} communities={communities}
baseAS={baseAS}
required required
/> />
+15 -1
View File
@@ -16,6 +16,7 @@ export default function SettingsModal({ open, onClose }) {
const [etag, setEtag] = useState(''); const [etag, setEtag] = useState('');
const [dohServer, setDohServer] = useState(''); const [dohServer, setDohServer] = useState('');
const [wsUrl, setWsUrl] = useState(''); const [wsUrl, setWsUrl] = useState('');
const [baseAS, setBaseAS] = useState('65001');
useEffect(() => { useEffect(() => {
if (!open) return; if (!open) return;
@@ -29,6 +30,7 @@ export default function SettingsModal({ open, onClose }) {
const data = res?.data || {}; const data = res?.data || {};
setDohServer(String(data?.dohServer || '')); setDohServer(String(data?.dohServer || ''));
setWsUrl(String(data?.wsUpdateUrl || '')); setWsUrl(String(data?.wsUpdateUrl || ''));
setBaseAS(String(data?.baseAS || '65001'));
const e = res?.headers?.etag || res?.headers?.ETag || ''; const e = res?.headers?.etag || res?.headers?.ETag || '';
setEtag(e ? String(e) : ''); setEtag(e ? String(e) : '');
} catch (e) { } catch (e) {
@@ -87,7 +89,8 @@ export default function SettingsModal({ open, onClose }) {
const payload = { const payload = {
settings: { settings: {
dohServer: String(dohServer || '').trim(), dohServer: String(dohServer || '').trim(),
wsUpdateUrl: String(wsUrl || '').trim() wsUpdateUrl: String(wsUrl || '').trim(),
baseAS: String(baseAS || '65001').trim()
}, },
etag etag
}; };
@@ -172,6 +175,17 @@ export default function SettingsModal({ open, onClose }) {
helpText="HTTPS URL для DNS-over-HTTPS" helpText="HTTPS URL для DNS-over-HTTPS"
disabled={loading || saving} disabled={loading || saving}
/> />
<FormField
label="Базовая AS"
name="baseAS"
type="text"
value={baseAS}
onChange={setBaseAS}
placeholder="65001"
helpText="AS по умолчанию для community (например, 65001). Используется при поиске и нормализации community."
disabled={loading || saving}
/>
</div> </div>
<div className="modal-footer"> <div className="modal-footer">