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
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 [isOpen, setIsOpen] = useState(false);
const [highlightedIndex, setHighlightedIndex] = useState(-1);
@@ -89,9 +89,22 @@ function CommunityAutocomplete({ label, value, onChange, communities = [], requi
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 (normalizedCValue === normalizedSearch) return true;
if (normalizedCValue.includes(normalizedSearch)) return true;
// Поиск по части после двоеточия (например, "120" найдёт "65001:120")
if (cValue.includes(':')) {
const parts = cValue.split(':');
@@ -108,6 +121,11 @@ function CommunityAutocomplete({ label, value, onChange, communities = [], requi
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.description && c.description.toLowerCase().includes(search)) return true;
@@ -586,6 +604,8 @@ function FilterManager() {
// Communities directory for autocomplete
const [communities, setCommunities] = useState([]);
// Базовая AS из настроек
const [baseAS, setBaseAS] = useState('65001');
// Inventory servers (from /servers endpoint)
const [inventoryServers, setInventoryServers] = useState([]);
@@ -647,6 +667,17 @@ function FilterManager() {
})();
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 () => {
try {
const res = await api.get(`/communities`);
@@ -1815,6 +1846,7 @@ function FilterManager() {
value={newFilter.community}
onChange={(val) => setNewFilter({ ...newFilter, community: val })}
communities={communities}
baseAS={baseAS}
required
/>
@@ -1855,6 +1887,7 @@ function FilterManager() {
value={editingFilter.community}
onChange={(val) => setEditingFilter({ ...editingFilter, community: val })}
communities={communities}
baseAS={baseAS}
required
/>
+15 -1
View File
@@ -16,6 +16,7 @@ export default function SettingsModal({ open, onClose }) {
const [etag, setEtag] = useState('');
const [dohServer, setDohServer] = useState('');
const [wsUrl, setWsUrl] = useState('');
const [baseAS, setBaseAS] = useState('65001');
useEffect(() => {
if (!open) return;
@@ -29,6 +30,7 @@ export default function SettingsModal({ open, onClose }) {
const data = res?.data || {};
setDohServer(String(data?.dohServer || ''));
setWsUrl(String(data?.wsUpdateUrl || ''));
setBaseAS(String(data?.baseAS || '65001'));
const e = res?.headers?.etag || res?.headers?.ETag || '';
setEtag(e ? String(e) : '');
} catch (e) {
@@ -87,7 +89,8 @@ export default function SettingsModal({ open, onClose }) {
const payload = {
settings: {
dohServer: String(dohServer || '').trim(),
wsUpdateUrl: String(wsUrl || '').trim()
wsUpdateUrl: String(wsUrl || '').trim(),
baseAS: String(baseAS || '65001').trim()
},
etag
};
@@ -172,6 +175,17 @@ export default function SettingsModal({ open, onClose }) {
helpText="HTTPS URL для DNS-over-HTTPS"
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 className="modal-footer">