Add sortable columns for Name, Site, IP Address
Clicking a column header sorts ascending; clicking again reverses to descending. Active column shows ↑/↓ in blue; inactive columns show ↕ in gray. IP addresses are compared numerically (octet by octet) so .9 sorts before .10. Sort composes with the name filter and IP filter. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -72,9 +72,18 @@
|
||||
<input type="checkbox" id="chk-all"
|
||||
class="rounded border-gray-300 bg-white text-blue-500 focus:ring-blue-500 cursor-pointer" />
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-500 uppercase tracking-wide text-xs">Name</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-500 uppercase tracking-wide text-xs">Site</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-500 uppercase tracking-wide text-xs">IP Address</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-500 uppercase tracking-wide text-xs cursor-pointer select-none hover:text-gray-700"
|
||||
data-sort="name">
|
||||
<span class="flex items-center gap-1">Name <span class="sort-icon text-gray-300">↕</span></span>
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-500 uppercase tracking-wide text-xs cursor-pointer select-none hover:text-gray-700"
|
||||
data-sort="site">
|
||||
<span class="flex items-center gap-1">Site <span class="sort-icon text-gray-300">↕</span></span>
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-500 uppercase tracking-wide text-xs cursor-pointer select-none hover:text-gray-700"
|
||||
data-sort="ip">
|
||||
<span class="flex items-center gap-1">IP Address <span class="sort-icon text-gray-300">↕</span></span>
|
||||
</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-500 uppercase tracking-wide text-xs">MAC Address</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-500 uppercase tracking-wide text-xs">Model</th>
|
||||
<th class="px-4 py-3 text-left font-semibold text-gray-500 uppercase tracking-wide text-xs">Status</th>
|
||||
@@ -256,6 +265,69 @@
|
||||
|
||||
filterInput.addEventListener('input', applyFilters);
|
||||
|
||||
// ── Column sort ────────────────────────────────────────────────────────────
|
||||
let sortCol = null;
|
||||
let sortDir = 'asc';
|
||||
|
||||
const colIndex = { name: 2, site: 3, ip: 4 };
|
||||
|
||||
function ipToNum(ip) {
|
||||
return (ip || '').split('.').reduce((acc, p) => {
|
||||
const n = parseInt(p, 10);
|
||||
return acc * 256 + (isNaN(n) ? 0 : n);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
function getCellText(row, idx) {
|
||||
return (row.querySelector(`td:nth-child(${idx})`)?.textContent || '').trim();
|
||||
}
|
||||
|
||||
function updateSortIcons() {
|
||||
document.querySelectorAll('[data-sort]').forEach(th => {
|
||||
const icon = th.querySelector('.sort-icon');
|
||||
if (!icon) return;
|
||||
if (th.dataset.sort === sortCol) {
|
||||
icon.textContent = sortDir === 'asc' ? '↑' : '↓';
|
||||
icon.className = 'sort-icon text-blue-500';
|
||||
} else {
|
||||
icon.textContent = '↕';
|
||||
icon.className = 'sort-icon text-gray-300';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-sort]').forEach(th => {
|
||||
th.addEventListener('click', () => {
|
||||
const col = th.dataset.sort;
|
||||
if (sortCol === col) {
|
||||
sortDir = sortDir === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
sortCol = col;
|
||||
sortDir = 'asc';
|
||||
}
|
||||
|
||||
const idx = colIndex[col];
|
||||
const tbody = document.getElementById('ap-table-body');
|
||||
const rows = [...tbody.querySelectorAll('tr')];
|
||||
|
||||
rows.sort((a, b) => {
|
||||
const av = getCellText(a, idx);
|
||||
const bv = getCellText(b, idx);
|
||||
let cmp;
|
||||
if (col === 'ip') {
|
||||
cmp = ipToNum(av) - ipToNum(bv);
|
||||
} else {
|
||||
cmp = av.localeCompare(bv, undefined, { sensitivity: 'base' });
|
||||
}
|
||||
return sortDir === 'asc' ? cmp : -cmp;
|
||||
});
|
||||
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
updateSortIcons();
|
||||
applyFilters();
|
||||
});
|
||||
});
|
||||
|
||||
// ── IP range filter button ─────────────────────────────────────────────────
|
||||
const btnIpFilter = document.getElementById('btn-ip-filter');
|
||||
const ipFilterLabel = document.getElementById('ip-filter-label');
|
||||
|
||||
Reference in New Issue
Block a user