fix(webui): address Migrate-refactor review findings

- Restore empty-state 'Open Locations' now deep-links to the backup center's
  Locations sub-tab: the embedded center honors /overview/backups/<sub> and
  switchTab()s to it after mount (was landing on Dashboard).
- PeersPage.notify + MigratePage.notify use the real window.notificationSystem
  (were calling a never-defined window.showNotification → console-only).
- Remove the now-dead Admin config-manager peers branch (Peers left Admin).
- Trim the dead migrate.json/peers fetch + hostnameToPeerName from BackupPage
  (no consumer after the migrate removal).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
librelad 2026-06-01 11:12:11 +01:00
parent b37e2454e3
commit 164f782a95
5 changed files with 24 additions and 38 deletions

View File

@ -72,24 +72,8 @@ if (typeof window.ConfigManager === 'undefined') {
// instances (kind=backup-channel for shared-backup migrate, or // instances (kind=backup-channel for shared-backup migrate, or
// kind=direct-ssh-direct for live SSH pulls). Same shape as SSH Access: // kind=direct-ssh-direct for live SSH pulls). Same shape as SSH Access:
// we inject its content template, then init PeersPage. // we inject its content template, then init PeersPage.
if (category === 'peers') { // Peers moved out of Admin → Overview Migrate Peers; /admin/tools/peers
try { this.sidebar.populateSidebar(); } catch (e) {} // redirects there, so this branch is intentionally gone.
await lazyLoad('/components/admin/peers/js/peers-page.js');
try {
const html = await fetch('/components/admin/peers/html/peers-content.html').then(r => r.text());
configSection.innerHTML = html;
} catch (e) {
configSection.innerHTML = '<div class="error">Peers page template failed to load.</div>';
return;
}
if (typeof PeersPage !== 'undefined') {
window.peersPage = new PeersPage('config-section');
await window.peersPage.init();
} else {
configSection.innerHTML = '<div class="error">PeersPage controller not loaded.</div>';
}
return;
}
// System is an admin tool page that internally routes between index / // System is an admin tool page that internally routes between index /
// metric / per-app / storage sub-views based on the URL sub-path. // metric / per-app / storage sub-views based on the URL sub-path.

View File

@ -431,7 +431,7 @@ class PeersPage {
} }
notify(message, kind) { notify(message, kind) {
if (typeof window.showNotification === 'function') window.showNotification(message, kind); if (window.notificationSystem && window.notificationSystem.show) window.notificationSystem.show(message, kind || 'info');
else if (kind === 'error') console.error(message); else if (kind === 'error') console.error(message);
else console.log(message); else console.log(message);
} }

View File

@ -428,17 +428,26 @@ class OverviewManager {
// configuration sections (its sidebar restyled as a nested tab strip). On a // configuration sections (its sidebar restyled as a nested tab strip). On a
// revisit we refresh rather than re-mount, to keep its sub-tab + expand state. // revisit we refresh rather than re-mount, to keep its sub-tab + expand state.
mountBackupCenter(pane) { mountBackupCenter(pane) {
// Honor an optional sub-tab deep-link (/overview/backups/<sub>), e.g. the
// Restore empty-state's "Open Locations" button.
const seg = window.location.pathname.replace(/^\/overview\/backups\/?/, '').split('/')[0];
const sub = ['dashboard', 'backups', 'locations', 'configuration'].includes(seg) ? seg : null;
// Detect a prior mount by the embedded fragment in THIS pane — not by // Detect a prior mount by the embedded fragment in THIS pane — not by
// #backup-section, which the per-app Backups tab also defines. // #backup-section, which the per-app Backups tab also defines.
if (pane.querySelector('.backup-layout') && window.overviewBackupPage) { if (pane.querySelector('.backup-layout') && window.overviewBackupPage) {
try { window.overviewBackupPage.refreshAll().then(() => window.overviewBackupPage.render()).catch(() => {}); } catch (_) {} try {
window.overviewBackupPage.refreshAll().then(() => {
window.overviewBackupPage.render();
if (sub) window.overviewBackupPage.switchTab(sub);
}).catch(() => {});
} catch (_) {}
return; return;
} }
pane.innerHTML = '<div class="updater-empty">Loading backup center…</div>'; pane.innerHTML = '<div class="updater-empty">Loading backup center…</div>';
this._loadBackupCenter(pane); this._loadBackupCenter(pane, sub);
} }
async _loadBackupCenter(pane) { async _loadBackupCenter(pane, sub) {
if (this._backupLoading) return; if (this._backupLoading) return;
this._backupLoading = true; this._backupLoading = true;
try { try {
@ -456,6 +465,7 @@ class OverviewManager {
try { if (window.overviewBackupPage) window.overviewBackupPage.dispose(); } catch (_) {} try { if (window.overviewBackupPage) window.overviewBackupPage.dispose(); } catch (_) {}
window.overviewBackupPage = new BackupPage({ embedded: true }); window.overviewBackupPage = new BackupPage({ embedded: true });
await window.overviewBackupPage.init(); await window.overviewBackupPage.init();
if (sub) { try { window.overviewBackupPage.switchTab(sub); } catch (_) {} }
} catch (_) { } catch (_) {
pane.innerHTML = '<div class="updater-empty">Failed to load the backup center.</div>'; pane.innerHTML = '<div class="updater-empty">Failed to load the backup center.</div>';
} finally { } finally {

View File

@ -68,7 +68,7 @@ class MigratePage {
} }
const locBtn = e.target.closest('[data-action="go-to-locations"]'); const locBtn = e.target.closest('[data-action="go-to-locations"]');
if (locBtn && this.root()?.contains(locBtn)) { if (locBtn && this.root()?.contains(locBtn)) {
if (window.navigateToRoute) window.navigateToRoute('/overview/backups'); if (window.navigateToRoute) window.navigateToRoute('/overview/backups/locations');
return; return;
} }
if (e.target.closest('#ov-migrate-confirm')) { this.confirmMigrate(); return; } if (e.target.closest('#ov-migrate-confirm')) { this.confirmMigrate(); return; }
@ -107,8 +107,9 @@ class MigratePage {
closeModal() { document.getElementById('ov-migrate-modal')?.classList.remove('open'); } closeModal() { document.getElementById('ov-migrate-modal')?.classList.remove('open'); }
notify(msg, type) { notify(msg, type) {
const n = (window.LP && window.LP.services && window.LP.services.notify) || window.notify; if (window.notificationSystem && window.notificationSystem.show) window.notificationSystem.show(msg, type || 'info');
if (n && n.show) n.show(msg, type || 'info'); else if (type === 'error') console.error(msg);
else console.log(msg);
} }
async runTask(command, type, app) { async runTask(command, type, app) {

View File

@ -2,26 +2,17 @@
Object.assign(BackupPage.prototype, { Object.assign(BackupPage.prototype, {
async refreshAll() { async refreshAll() {
const ts = Date.now(); const ts = Date.now();
const [dashboard, locations, , schema, migrate, peersData] = await Promise.all([ const [dashboard, locations, , schema] = await Promise.all([
this.fetchJson(`/data/backup/generated/dashboard.json?t=${ts}`), this.fetchJson(`/data/backup/generated/dashboard.json?t=${ts}`),
this.fetchJson(`/data/backup/generated/locations.json?t=${ts}`), this.fetchJson(`/data/backup/generated/locations.json?t=${ts}`),
this.loadSystemConfigs(), this.loadSystemConfigs(),
this.fetchJson(`/data/backup/generated/schema.json?t=${ts}`), this.fetchJson(`/data/backup/generated/schema.json?t=${ts}`)
this.fetchJson(`/data/backup/generated/migrate.json?t=${ts}`),
this.fetchJson(`/data/peers/generated/peers.json?t=${ts}`)
]); ]);
this.dashboard = dashboard; this.dashboard = dashboard;
this.locations = locations; this.locations = locations;
this.locSchema = schema; this.locSchema = schema;
this.migrate = migrate; // (migrate.json + peers lookup moved to MigratePage when Migrate left the
// Build hostname → friendly-name lookup once so renderMigrate can show // backup center — nothing here consumes them anymore.)
// "homelab (host: homelab.lan)" instead of bare hostnames.
this.hostnameToPeerName = {};
for (const p of (peersData?.peers || [])) {
if (p.kind === 'backup-channel' && p.config?.hostname) {
this.hostnameToPeerName[p.config.hostname] = p.name;
}
}
this.snapshotsByLoc = {}; this.snapshotsByLoc = {};
if (!this.engines.length) await this.loadEngines(); if (!this.engines.length) await this.loadEngines();