aboutsummaryrefslogtreecommitdiff
path: root/src/zenserver/frontend/html/pages
diff options
context:
space:
mode:
Diffstat (limited to 'src/zenserver/frontend/html/pages')
-rw-r--r--src/zenserver/frontend/html/pages/cache.js5
-rw-r--r--src/zenserver/frontend/html/pages/hub.js66
-rw-r--r--src/zenserver/frontend/html/pages/projects.js5
-rw-r--r--src/zenserver/frontend/html/pages/start.js10
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