Compare commits

...

2 Commits

Author SHA1 Message Date
librelad
7a5f4cdc33 Merge claude/1 2026-05-31 01:21:07 +01:00
librelad
c920ca2dc9 refactor(webui): align page-controller names with the -page convention
From the feng-shui audit naming findings:
- admin/overview/js/admin-overview.js -> overview-page.js (class AdminOverview ->
  OverviewPage, window globals + the 'admin-overview' task-refresh id ->
  overview-page, lazy-load path + typeof/new in config-manager.js).
- admin/system/js/admin-system.js -> system-page.js (class AdminSystem ->
  SystemPage; now sits beside its -page sub-views system-metric-page.js /
  system-storage-page.js).
- tasks/js/tasks-logs-modal.js -> tasks-log-modal.js (singular 'log' to match its
  sibling tasks-log-stream.js; single path ref in system-loader.js).

These were the only page controllers breaking the dominant <thing>-page.js /
<Thing>Page convention (ssh-page/peers-page/backup-page/updater-page/
system-metric-page/system-storage-page). Pure renames; node --check clean.

Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-31 01:21:07 +01:00
8 changed files with 27 additions and 27 deletions

View File

@ -42,14 +42,14 @@ if (typeof window.ConfigManager === 'undefined') {
// Overview is the Admin landing — an ops/health board, not a config form. // Overview is the Admin landing — an ops/health board, not a config form.
if (category === 'overview') { if (category === 'overview') {
try { this.sidebar.populateSidebar(); } catch (e) {} try { this.sidebar.populateSidebar(); } catch (e) {}
// charts.js is the chart-rendering helper admin-overview pulls in. // charts.js is the chart-rendering helper overview-page pulls in.
await Promise.all([ await Promise.all([
lazyLoad('/components/admin/overview/js/admin-overview.js'), lazyLoad('/components/admin/overview/js/overview-page.js'),
lazyLoad('/components/admin/system/js/charts.js') lazyLoad('/components/admin/system/js/charts.js')
]); ]);
if (typeof AdminOverview !== 'undefined') { if (typeof OverviewPage !== 'undefined') {
window.adminOverview = new AdminOverview('config-section'); window.overviewPage = new OverviewPage('config-section');
await window.adminOverview.init(); await window.overviewPage.init();
} else { } else {
configSection.innerHTML = '<div class="error">Admin overview failed to load.</div>'; configSection.innerHTML = '<div class="error">Admin overview failed to load.</div>';
} }
@ -101,13 +101,13 @@ if (typeof window.ConfigManager === 'undefined') {
try { this.sidebar.populateSidebar(); } catch (e) {} try { this.sidebar.populateSidebar(); } catch (e) {}
await Promise.all([ await Promise.all([
lazyLoad('/components/admin/system/js/charts.js'), lazyLoad('/components/admin/system/js/charts.js'),
lazyLoad('/components/admin/system/js/admin-system.js'), lazyLoad('/components/admin/system/js/system-page.js'),
lazyLoad('/components/admin/system/js/system-metric-page.js'), lazyLoad('/components/admin/system/js/system-metric-page.js'),
lazyLoad('/components/admin/system/js/system-storage-page.js') lazyLoad('/components/admin/system/js/system-storage-page.js')
]); ]);
if (typeof AdminSystem !== 'undefined') { if (typeof SystemPage !== 'undefined') {
window.adminSystem = new AdminSystem('config-section'); window.systemPage = new SystemPage('config-section');
await window.adminSystem.init(); await window.systemPage.init();
} else { } else {
configSection.innerHTML = '<div class="error">System page failed to load.</div>'; configSection.innerHTML = '<div class="error">System page failed to load.</div>';
} }

View File

@ -33,10 +33,10 @@ LP.features.register({
async unmount(ctx) { async unmount(ctx) {
// Release only this view's leaks; never destroy the configManager singleton. // Release only this view's leaks; never destroy the configManager singleton.
// The sub-controllers renderConfig() spawns are re-created on each visit, so // The sub-controllers renderConfig() spawns are re-created on each visit, so
// null them; AdminSystem additionally holds a live SSE sub + a 30s interval // null them; SystemPage additionally holds a live SSE sub + a 30s interval
// we can stop cleanly. // we can stop cleanly.
try { try {
const as = window.adminSystem; const as = window.systemPage;
if (as) { if (as) {
if (typeof as._stopLive === 'function') as._stopLive(); if (typeof as._stopLive === 'function') as._stopLive();
if (as._timer) { clearInterval(as._timer); as._timer = null; } if (as._timer) { clearInterval(as._timer); as._timer = null; }
@ -46,11 +46,11 @@ LP.features.register({
as._subview = null; as._subview = null;
} }
} catch (_) {} } catch (_) {}
// Drop AdminOverview's task-refresh registration so a finished verify/update // Drop OverviewPage's task-refresh registration so a finished verify/update
// task doesn't repaint a torn-down board. // task doesn't repaint a torn-down board.
try { ctx.services.tasks.refresh && ctx.services.tasks.refresh.unregister('admin-overview'); } catch (_) {} try { ctx.services.tasks.refresh && ctx.services.tasks.refresh.unregister('overview-page'); } catch (_) {}
window.adminOverview = null; window.overviewPage = null;
window.adminSystem = null; window.systemPage = null;
window.sshPage = null; window.sshPage = null;
window.peersPage = null; window.peersPage = null;
// configManager (+ its inner managers) intentionally left intact. // configManager (+ its inner managers) intentionally left intact.

View File

@ -2,7 +2,7 @@
// from the user Dashboard, which is app-centric): it summarises updates, // from the user Dashboard, which is app-centric): it summarises updates,
// backups, SSH/security and system health from data we already generate, and // backups, SSH/security and system health from data we already generate, and
// each card links to where you act on it. Renders into #config-section. // each card links to where you act on it. Renders into #config-section.
class AdminOverview { class OverviewPage {
constructor(rootId = 'config-section') { constructor(rootId = 'config-section') {
this.rootId = rootId; this.rootId = rootId;
this.taskManager = (typeof TaskManager !== 'undefined') ? new TaskManager() : null; this.taskManager = (typeof TaskManager !== 'undefined') ? new TaskManager() : null;
@ -57,7 +57,7 @@ class AdminOverview {
// and re-render so the badge reflects reality without a manual reload. // and re-render so the badge reflects reality without a manual reload.
// Registered with the task-refresh coordinator (single source of truth). // Registered with the task-refresh coordinator (single source of truth).
window.taskRefresh?.register({ window.taskRefresh?.register({
id: 'admin-overview', id: 'overview-page',
match: (d) => ['verify', 'update', 'system_update'].includes(d.action) match: (d) => ['verify', 'update', 'system_update'].includes(d.action)
|| /^libreportal (verify|update)\b/.test((d.task && d.task.command) || d.command || ''), || /^libreportal (verify|update)\b/.test((d.task && d.task.command) || d.command || ''),
run: () => this.refreshVerify(), run: () => this.refreshVerify(),
@ -274,4 +274,4 @@ class AdminOverview {
} }
} }
window.AdminOverview = AdminOverview; window.OverviewPage = OverviewPage;

View File

@ -122,7 +122,7 @@ class SystemMetricPage {
// Metric registry — keys map to label, unit, formatter, accent. Mirrors // Metric registry — keys map to label, unit, formatter, accent. Mirrors
// the same list the index view exposes; lives here so the metric page // the same list the index view exposes; lives here so the metric page
// can mount cleanly without AdminSystem present (direct URL load). // can mount cleanly without SystemPage present (direct URL load).
_metricDef(key) { _metricDef(key) {
const fmt = window.SystemFmt || { bytes: (n) => `${n}`, rate: (n) => `${n}/s`, rgbVar: () => '0, 212, 255' }; const fmt = window.SystemFmt || { bytes: (n) => `${n}`, rate: (n) => `${n}/s`, rgbVar: () => '0, 212, 255' };
const pct = (v) => `${(v ?? 0).toFixed(1)}%`; const pct = (v) => `${(v ?? 0).toFixed(1)}%`;

View File

@ -1,6 +1,6 @@
// Admin → System — orchestrator + index view. // Admin → System — orchestrator + index view.
// //
// One AdminSystem instance per page mount. Reads the URL path on init and // One SystemPage instance per page mount. Reads the URL path on init and
// dispatches to one of four sub-views: // dispatches to one of four sub-views:
// //
// /admin/system → index (gauges + trends + per-app table) // /admin/system → index (gauges + trends + per-app table)
@ -12,14 +12,14 @@
// each own their own DOM + lifecycle inside #config-section. We mount one // each own their own DOM + lifecycle inside #config-section. We mount one
// at a time; switching means tearing down the active renderer and starting // at a time; switching means tearing down the active renderer and starting
// a fresh one. The SPA re-runs handleAdmin() on every navigation, which // a fresh one. The SPA re-runs handleAdmin() on every navigation, which
// re-runs ConfigManager.renderConfig('system') → AdminSystem.init, so the // re-runs ConfigManager.renderConfig('system') → SystemPage.init, so the
// dispatch happens organically with the router. // dispatch happens organically with the router.
// //
// Live (1 Hz, via LiveSystem SSE) data flows directly to whichever sub-view // Live (1 Hz, via LiveSystem SSE) data flows directly to whichever sub-view
// is mounted. The index view ticks gauges in place; the metric page splices // is mounted. The index view ticks gauges in place; the metric page splices
// the live value into its chart's tail. // the live value into its chart's tail.
class AdminSystem { class SystemPage {
constructor(rootId = 'config-section') { constructor(rootId = 'config-section') {
this.rootId = rootId; this.rootId = rootId;
this.range = 60; // minutes of history to chart this.range = 60; // minutes of history to chart
@ -134,7 +134,7 @@ class AdminSystem {
return; return;
} }
// Expand a metric → navigate to its detail page. The SPA picks up // Expand a metric → navigate to its detail page. The SPA picks up
// the new URL, ConfigManager re-renders, AdminSystem.init mounts // the new URL, ConfigManager re-renders, SystemPage.init mounts
// the metric page. // the metric page.
const ex = e.target.closest('[data-sys-expand]'); const ex = e.target.closest('[data-sys-expand]');
if (ex) { if (ex) {
@ -440,7 +440,7 @@ class AdminSystem {
// Lightweight formatter helpers shared with sub-pages so they don't each // Lightweight formatter helpers shared with sub-pages so they don't each
// reimplement bytes()/rate(). Attached as a global so a sub-page that mounts // reimplement bytes()/rate(). Attached as a global so a sub-page that mounts
// before AdminSystem still has them. // before SystemPage still has them.
window.SystemFmt = window.SystemFmt || { window.SystemFmt = window.SystemFmt || {
escape(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c])); }, escape(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c])); },
bytes(n) { bytes(n) {
@ -468,4 +468,4 @@ window.SystemFmt = window.SystemFmt || {
}, },
}; };
window.AdminSystem = AdminSystem; window.SystemPage = SystemPage;

View File

@ -177,7 +177,7 @@ class SystemLoader {
'/components/tasks/js/tasks-row-expand.js', '/components/tasks/js/tasks-row-expand.js',
'/components/tasks/js/tasks-actions.js', '/components/tasks/js/tasks-actions.js',
'/components/tasks/js/tasks-modals.js', '/components/tasks/js/tasks-modals.js',
'/components/tasks/js/tasks-logs-modal.js' '/components/tasks/js/tasks-log-modal.js'
] ]
}); });

View File

@ -9,7 +9,7 @@
// feature's module top level, so the lazy getters always resolve. // feature's module top level, so the lazy getters always resolve.
// //
// NOTE: page-owned controllers (appsManager, appTabbedManager, configManager, // NOTE: page-owned controllers (appsManager, appTabbedManager, configManager,
// backupPage, adminSystem, …) are NOT services — they belong to their feature — // backupPage, systemPage, …) are NOT services — they belong to their feature —
// so they are deliberately absent here. Slot names + globals were verified // so they are deliberately absent here. Slot names + globals were verified
// against the real code (the design doc's §4 list had several wrong names). // against the real code (the design doc's §4 list had several wrong names).
(function () { (function () {