diff options
Diffstat (limited to 'src/zenserver/frontend/html/pages')
| -rw-r--r-- | src/zenserver/frontend/html/pages/cache.js | 5 | ||||
| -rw-r--r-- | src/zenserver/frontend/html/pages/hub.js | 66 | ||||
| -rw-r--r-- | src/zenserver/frontend/html/pages/projects.js | 5 | ||||
| -rw-r--r-- | src/zenserver/frontend/html/pages/start.js | 10 |
4 files changed, 75 insertions, 11 deletions
diff --git a/src/zenserver/frontend/html/pages/cache.js b/src/zenserver/frontend/html/pages/cache.js index 93059b81c..c6567f0be 100644 --- a/src/zenserver/frontend/html/pages/cache.js +++ b/src/zenserver/frontend/html/pages/cache.js @@ -56,7 +56,8 @@ export class Page extends ZenPage this._cache_table = section.add_widget(Table, columns, Table.Flag_FitLeft|Table.Flag_PackRight|Table.Flag_AlignNumeric); - this._cache_pager = new Pager(section, 25, () => this._render_cache_page()); + this._cache_pager = new Pager(section, 25, () => this._render_cache_page(), + Pager.make_search_fn(() => this._cache_data, item => item.namespace)); const cache_drop_link = document.createElement("span"); cache_drop_link.className = "dropall zen_action"; cache_drop_link.style.position = "static"; @@ -64,6 +65,7 @@ export class Page extends ZenPage cache_drop_link.addEventListener("click", () => this.drop_all()); this._cache_pager.prepend(cache_drop_link); + const loading = Pager.loading(section); const zcache_info = await new Fetcher().resource("/z$/").json(); const namespaces = zcache_info["Namespaces"] || []; const results = await Promise.allSettled( @@ -75,6 +77,7 @@ export class Page extends ZenPage .sort((a, b) => a.namespace.localeCompare(b.namespace)); this._cache_pager.set_total(this._cache_data.length); this._render_cache_page(); + loading.remove(); // Namespace detail area (inside namespaces section so it collapses together) this._namespace_host = section; diff --git a/src/zenserver/frontend/html/pages/hub.js b/src/zenserver/frontend/html/pages/hub.js index 7ae1deb5c..3cbfe6092 100644 --- a/src/zenserver/frontend/html/pages/hub.js +++ b/src/zenserver/frontend/html/pages/hub.js @@ -6,6 +6,7 @@ import { ZenPage } from "./page.js" import { Fetcher } from "../util/fetcher.js" import { Friendly } from "../util/friendly.js" import { Modal } from "../util/modal.js" +import { flash_highlight } from "../util/widgets.js" //////////////////////////////////////////////////////////////////////////////// const STABLE_STATES = new Set(["provisioned", "hibernated", "crashed"]); @@ -159,8 +160,36 @@ export class Page extends ZenPage this._btn_next.addEventListener("click", () => this._go_page(this._page + 1)); this._btn_provision = _make_bulk_btn("+", "Provision", () => this._show_provision_modal()); this._btn_obliterate = _make_bulk_btn("\uD83D\uDD25", "Obliterate", () => this._show_obliterate_modal()); + this._search_input = document.createElement("input"); + this._search_input.type = "text"; + this._search_input.className = "module-pager-search"; + this._search_input.placeholder = "Search module\u2026"; + this._search_input.addEventListener("keydown", (e) => + { + if (e.key === "Enter") + { + const term = this._search_input.value.trim().toLowerCase(); + if (!term) { return; } + const idx = this._modules_data.findIndex(m => + (m.moduleId || "").toLowerCase().includes(term) + ); + if (idx >= 0) + { + const id = this._modules_data[idx].moduleId; + this._navigate_to_module(id); + this._flash_module(id); + } + else + { + this._search_input.style.outline = "2px solid var(--theme_fail)"; + setTimeout(() => { this._search_input.style.outline = ""; }, 1000); + } + } + }); + pager.appendChild(this._btn_provision); pager.appendChild(this._btn_obliterate); + pager.appendChild(this._search_input); pager.appendChild(this._btn_prev); pager.appendChild(this._pager_label); pager.appendChild(this._btn_next); @@ -173,8 +202,11 @@ export class Page extends ZenPage this._row_cache = new Map(); // moduleId → row refs, for in-place DOM updates this._updating = false; this._page = 0; - this._page_size = 50; + this._page_size = 25; this._expanded = new Set(); // moduleIds with open metrics panel + this._pending_highlight = null; // moduleId to navigate+flash after next poll + this._pending_highlight_timer = null; + this._loading = mod_section.tag().classify("pager-loading").text("Loading\u2026").inner(); await this._update(); this._poll_timer = setInterval(() => this._update(), 2000); @@ -193,6 +225,15 @@ export class Page extends ZenPage this._render_capacity(stats); this._render_modules(status); + if (this._loading) { this._loading.remove(); this._loading = null; } + if (this._pending_highlight && this._module_map.has(this._pending_highlight)) + { + const id = this._pending_highlight; + this._pending_highlight = null; + clearTimeout(this._pending_highlight_timer); + this._navigate_to_module(id); + this._flash_module(id); + } } catch (e) { /* service unavailable */ } finally { this._updating = false; } @@ -844,14 +885,19 @@ export class Page extends ZenPage submit_label: "Provision", on_submit: async (moduleId) => { const resp = await fetch(`/hub/modules/${encodeURIComponent(moduleId)}/provision`, { method: "POST" }); - if (resp.ok) + if (!resp.ok) { - this._navigate_to_module(moduleId); - return true; + const msg = await resp.text(); + error_div.textContent = msg || ("HTTP " + resp.status); + return false; } - const msg = await resp.text(); - error_div.textContent = msg || ("HTTP " + resp.status); - return false; + // Endpoint returns compact binary (CbObjectWriter), not text + if (resp.status === 200 || resp.status === 202) + { + this._pending_highlight = moduleId; + this._pending_highlight_timer = setTimeout(() => { this._pending_highlight = null; }, 5000); + } + return true; } }); } @@ -885,4 +931,10 @@ export class Page extends ZenPage } } + _flash_module(id) + { + const cached = this._row_cache.get(id); + if (cached) { flash_highlight(cached.tr); } + } + } diff --git a/src/zenserver/frontend/html/pages/projects.js b/src/zenserver/frontend/html/pages/projects.js index 52d5dbb88..e613086a9 100644 --- a/src/zenserver/frontend/html/pages/projects.js +++ b/src/zenserver/frontend/html/pages/projects.js @@ -49,7 +49,8 @@ export class Page extends ZenPage this._project_table = section.add_widget(Table, columns, Table.Flag_FitLeft|Table.Flag_PackRight|Table.Flag_Sortable|Table.Flag_AlignNumeric); - this._project_pager = new Pager(section, 25, () => this._render_projects_page()); + this._project_pager = new Pager(section, 25, () => this._render_projects_page(), + Pager.make_search_fn(() => this._projects_data, p => p.Id)); const drop_link = document.createElement("span"); drop_link.className = "dropall zen_action"; drop_link.style.position = "static"; @@ -57,10 +58,12 @@ export class Page extends ZenPage drop_link.addEventListener("click", () => this.drop_all()); this._project_pager.prepend(drop_link); + const loading = Pager.loading(section); this._projects_data = await new Fetcher().resource("/prj/list").json(); this._projects_data.sort((a, b) => a.Id.localeCompare(b.Id)); this._project_pager.set_total(this._projects_data.length); this._render_projects_page(); + loading.remove(); // Project detail area (inside projects section so it collapses together) this._project_host = section; diff --git a/src/zenserver/frontend/html/pages/start.js b/src/zenserver/frontend/html/pages/start.js index 14ec4bd4a..9a3eb6de3 100644 --- a/src/zenserver/frontend/html/pages/start.js +++ b/src/zenserver/frontend/html/pages/start.js @@ -62,7 +62,8 @@ export class Page extends ZenPage ]; this._project_table = section.add_widget(Table, columns); - this._project_pager = new Pager(section, 25, () => this._render_projects_page()); + this._project_pager = new Pager(section, 25, () => this._render_projects_page(), + Pager.make_search_fn(() => this._projects_data, p => p.Id)); const drop_link = document.createElement("span"); drop_link.className = "dropall zen_action"; drop_link.style.position = "static"; @@ -70,10 +71,12 @@ export class Page extends ZenPage drop_link.addEventListener("click", () => this.drop_all("projects")); this._project_pager.prepend(drop_link); + const prj_loading = Pager.loading(section); this._projects_data = await new Fetcher().resource("/prj/list").json(); this._projects_data.sort((a, b) => a.Id.localeCompare(b.Id)); this._project_pager.set_total(this._projects_data.length); this._render_projects_page(); + prj_loading.remove(); } // cache @@ -92,7 +95,8 @@ export class Page extends ZenPage ]; this._cache_table = section.add_widget(Table, columns, Table.Flag_FitLeft|Table.Flag_PackRight); - this._cache_pager = new Pager(section, 25, () => this._render_cache_page()); + this._cache_pager = new Pager(section, 25, () => this._render_cache_page(), + Pager.make_search_fn(() => this._cache_data, item => item.namespace)); const cache_drop_link = document.createElement("span"); cache_drop_link.className = "dropall zen_action"; cache_drop_link.style.position = "static"; @@ -100,6 +104,7 @@ export class Page extends ZenPage cache_drop_link.addEventListener("click", () => this.drop_all("z$")); this._cache_pager.prepend(cache_drop_link); + const cache_loading = Pager.loading(section); const zcache_info = await new Fetcher().resource("/z$/").json(); const namespaces = zcache_info["Namespaces"] || []; const results = await Promise.allSettled( @@ -111,6 +116,7 @@ export class Page extends ZenPage .sort((a, b) => a.namespace.localeCompare(b.namespace)); this._cache_pager.set_total(this._cache_data.length); this._render_cache_page(); + cache_loading.remove(); } // version |