From 37f38aa6d6a5f4db0ebd89df426b42c42d4c73f3 Mon Sep 17 00:00:00 2001 From: Levi Neely Date: Mon, 16 Feb 2026 09:36:51 +0100 Subject: [PATCH] anvilweb: sortable data columns --- cmd/anvilweb/static/index.html | 46 +++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/cmd/anvilweb/static/index.html b/cmd/anvilweb/static/index.html index e911817..db930ba 100644 --- a/cmd/anvilweb/static/index.html +++ b/cmd/anvilweb/static/index.html @@ -74,6 +74,11 @@ content: 'No log data yet...'; color: #666; } + th.sortable { cursor: pointer; user-select: none; } + th.sortable:hover { background: var(--pico-secondary-background); } + th.sortable::after { content: ' ⇅'; opacity: 0.3; } + th.sortable.asc::after { content: ' ↑'; opacity: 1; } + th.sortable.desc::after { content: ' ↓'; opacity: 1; } @@ -190,6 +195,8 @@ let currentSessionId = null; let allSessions = []; let logEventSource = null; + let sortColumn = 'alias'; + let sortAsc = true; function escapeHtml(text) { const div = document.createElement('div'); @@ -221,21 +228,31 @@ } sessions.sort((a, b) => { - const nameA = (a.alias || a.id).toLowerCase(); - const nameB = (b.alias || b.id).toLowerCase(); - return nameA.localeCompare(nameB); + let valA, valB; + switch (sortColumn) { + case 'alias': valA = (a.alias || a.id).toLowerCase(); valB = (b.alias || b.id).toLowerCase(); break; + case 'id': valA = a.id; valB = b.id; break; + case 'state': valA = a.state; valB = b.state; break; + case 'backend': valA = a.backend || ''; valB = b.backend || ''; break; + case 'cwd': valA = a.cwd; valB = b.cwd; break; + case 'pid': valA = parseInt(a.pid) || 0; valB = parseInt(b.pid) || 0; break; + default: valA = ''; valB = ''; + } + const cmp = typeof valA === 'number' ? valA - valB : String(valA).localeCompare(String(valB)); + return sortAsc ? cmp : -cmp; }); + const sortClass = col => `sortable${sortColumn === col ? (sortAsc ? ' asc' : ' desc') : ''}`; const table = ` - - - - - - + + + + + + @@ -458,6 +475,17 @@ // Search box filtering document.getElementById('search-box').addEventListener('input', filterSessions); + // Column sorting + document.addEventListener('click', (e) => { + const th = e.target.closest('th[data-sort]'); + if (th) { + const col = th.dataset.sort; + if (sortColumn === col) sortAsc = !sortAsc; + else { sortColumn = col; sortAsc = true; } + filterSessions(); + } + }); + // Event delegation for session actions document.addEventListener('click', (e) => { const btn = e.target.closest('button[data-session-id]');
AliasIDStateBackendWorking DirectoryPIDAliasIDStateBackendWorking DirectoryPID Actions