Reservations page refreshes itself every 15 seconds
Online and last-seen times stay current. A newer draw (typing, a change) wins over a slower older one; no refresh while the tab is hidden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5e6c61cc8e
commit
29fc991ce1
2 files changed
+10
-1
No files matched your search
@@ -113,6 +113,8 @@ and a CLI, built the way Jarl is.
|
||||
(Leases page, reservations' online badge, `steward lease list|show`,
|
||||
`steward reservation list|show`, `last_seen` in the API). A client that
|
||||
blocks pings stays online for as long as its lease lasts, without one.
|
||||
- **The Reservations page refreshes itself** every 15 seconds, so online
|
||||
and last seen stay current.
|
||||
- **Activity by subnet:** `steward activity --subnet`, a subnet filter on
|
||||
the Activity page, `GET /events?subnet=` (ID, name or prefix).
|
||||
- **Token rotation** (`steward token rotate`, `POST /tokens/{id}/rotate`,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { h, mount, table, time, input, modal, errorBox, field, confirmDialog, select, debounce, badge, checkbox } from "../ui.js";
|
||||
import { get, change, can } from "../api.js";
|
||||
import { optionsEditor, subnetLabel } from "../widgets.js";
|
||||
import { onLeave } from "../app.js";
|
||||
|
||||
// reservationsTable lists reservations with Edit and Delete.
|
||||
export function reservationsTable(rs, done, opts = {}) {
|
||||
@@ -87,19 +88,25 @@ export async function render(el, params, query) {
|
||||
const subnetSel = select([["", "all subnets"], ...subnets.map((s) => [String(s.id), subnetLabel(s)])], query.get("subnet") || "");
|
||||
const search = input({ type: "search", placeholder: "address, MAC or host name" });
|
||||
const out = h("div");
|
||||
let gen = 0;
|
||||
const draw = async () => {
|
||||
const my = ++gen;
|
||||
const q = search.value.trim().toLowerCase();
|
||||
const rs = (await get("/reservations", { subnet: subnetSel.value })).filter((r) =>
|
||||
!q || r.ip.includes(q) || (r.mac || "").includes(q) || (r.hostname || "").toLowerCase().includes(q) || (r.client_id || "").includes(q));
|
||||
if (my !== gen) return; // a newer draw (typing, a change) won
|
||||
mount(out, reservationsTable(rs, draw, { empty: q || subnetSel.value ? "None match." : "No reservations yet." }));
|
||||
};
|
||||
subnetSel.addEventListener("change", draw);
|
||||
search.addEventListener("input", debounce(draw, 200));
|
||||
mount(el,
|
||||
h("div", { class: "page-head" },
|
||||
h("div", { class: "grow" }, h("h1", null, "Reservations"), h("div", { class: "muted" }, "Fixed addresses for clients, by MAC address or client identifier.")),
|
||||
h("div", { class: "grow" }, h("h1", null, "Reservations"), h("div", { class: "muted" }, "Fixed addresses for clients, by MAC address or client identifier. Refreshed every 15 seconds.")),
|
||||
can("config", true) ? h("div", { class: "actions" }, h("button", { class: "primary", onclick: () => reservationDialog(null, draw) }, "New reservation")) : null),
|
||||
h("div", { class: "filter-row" }, subnetSel, search),
|
||||
h("div", { class: "panel" }, out));
|
||||
await draw();
|
||||
// Online and last seen change as clients come and go.
|
||||
const t = setInterval(() => { if (!document.hidden) draw().catch(() => {}); }, 15000);
|
||||
onLeave(() => clearInterval(t));
|
||||
}
|
||||
Reference in new issue
Block a user