Fix IP filter to check AP's own IP from DOM

Previous implementation fetched connected client IPs via API (which was
returning empty data). The filter now checks the AP's own management IP
directly from the data already in the table — no network request, instant.

An AP is shown when its IP last octet is between 150 and 155 inclusive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-27 15:51:43 +02:00
parent d693321ba1
commit b455b2fd15

View File

@@ -221,9 +221,13 @@
const visibleCount = document.getElementById('visible-count'); const visibleCount = document.getElementById('visible-count');
const allRows = [...document.querySelectorAll('#ap-table-body tr')]; const allRows = [...document.querySelectorAll('#ap-table-body tr')];
let ipMatchingMacs = null; // null = inactive; Set<string> = active let ipFilterActive = false;
function normMac(s) { return (s || '').toUpperCase().replace(/-/g, ':'); } function isTargetIp(ip) {
if (!ip) return false;
const last = parseInt((ip.split('.')[3] || ''), 10);
return last >= 150 && last <= 155;
}
function applyFilters() { function applyFilters() {
const q = filterInput.value.toLowerCase().trim(); const q = filterInput.value.toLowerCase().trim();
@@ -234,9 +238,9 @@
const nameMatch = !q || name.includes(q) || site.includes(q); const nameMatch = !q || name.includes(q) || site.includes(q);
let ipMatch = true; let ipMatch = true;
if (ipMatchingMacs !== null) { if (ipFilterActive) {
const cb = row.querySelector('.ap-checkbox'); const cb = row.querySelector('.ap-checkbox');
ipMatch = ipMatchingMacs.has(normMac(cb?.dataset.mac)); ipMatch = isTargetIp(cb?.dataset.ip || '');
} }
const visible = nameMatch && ipMatch; const visible = nameMatch && ipMatch;
@@ -252,18 +256,9 @@
// ── IP range filter button ───────────────────────────────────────────────── // ── IP range filter button ─────────────────────────────────────────────────
const btnIpFilter = document.getElementById('btn-ip-filter'); const btnIpFilter = document.getElementById('btn-ip-filter');
const ipFilterLabel = document.getElementById('ip-filter-label'); const ipFilterLabel = document.getElementById('ip-filter-label');
const ipFilterIcon = document.getElementById('ip-filter-icon');
let cachedApClients = null;
function isTargetIp(ip) {
if (!ip) return false;
const parts = ip.split('.');
if (parts.length !== 4) return false;
const last = parseInt(parts[3], 10);
return last >= 150 && last <= 155;
}
function setIpFilterActive(active) { function setIpFilterActive(active) {
ipFilterActive = active;
if (active) { if (active) {
btnIpFilter.classList.replace('bg-white', 'bg-blue-50'); btnIpFilter.classList.replace('bg-white', 'bg-blue-50');
btnIpFilter.classList.replace('hover:bg-gray-50', 'hover:bg-blue-100'); btnIpFilter.classList.replace('hover:bg-gray-50', 'hover:bg-blue-100');
@@ -279,40 +274,9 @@
} }
} }
btnIpFilter?.addEventListener('click', async () => { btnIpFilter?.addEventListener('click', () => {
if (ipMatchingMacs !== null) { setIpFilterActive(!ipFilterActive);
// Clear filter applyFilters();
ipMatchingMacs = null;
setIpFilterActive(false);
applyFilters();
return;
}
btnIpFilter.disabled = true;
ipFilterLabel.textContent = 'Loading…';
ipFilterIcon.classList.add('animate-spin');
try {
if (!cachedApClients) {
const res = await fetch('/api/all-clients');
if (!res.ok) { const e = await res.json(); throw new Error(e.detail || 'Request failed'); }
cachedApClients = (await res.json()).ap_clients || {};
}
ipMatchingMacs = new Set();
for (const [apMac, clients] of Object.entries(cachedApClients)) {
if (clients.some(c => isTargetIp(c.ip))) ipMatchingMacs.add(apMac);
}
setIpFilterActive(true);
applyFilters();
} catch (e) {
showToast('Failed to load client data: ' + e.message, 'error');
ipFilterLabel.textContent = 'Filter .150.155';
} finally {
btnIpFilter.disabled = false;
ipFilterIcon.classList.remove('animate-spin');
}
}); });
// ── Refresh ──────────────────────────────────────────────────────────────── // ── Refresh ────────────────────────────────────────────────────────────────