fix(webui/backups): sync fleet Backups sub-tab URLs (/apps/overview/backups/<sub>)
The embedded backup center deep-links *in* fine (mountBackupCenter parses the sub-segment), but clicking a sub-tab never wrote the URL: BackupPage disables pushTabToUrl() when embedded and nothing on the fleet side stepped in, so the address bar stayed stale and the sub-tabs weren't shareable/deep-linkable — unlike the sibling Migrate sub-tabs. Give BackupPage an onTabChange hook fired on every switch; the OverviewManager supplies it to replaceState /apps/overview/backups/<sub> (replaceState, matching Migrate, so the on-entry deep-link re-sync can't stack a duplicate history entry). Standalone /backup keeps using pushTabToUrl() unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
ff711d3c2b
commit
73a93cf145
@ -643,7 +643,16 @@ class OverviewManager {
|
||||
return;
|
||||
}
|
||||
try { if (window.overviewBackupPage) window.overviewBackupPage.dispose(); } catch (_) {}
|
||||
window.overviewBackupPage = new BackupPage({ embedded: true });
|
||||
// Own the URL for the embedded center's sub-tabs: reflect each switch as
|
||||
// /apps/overview/backups/<sub> so they're shareable + deep-linkable (the
|
||||
// read side is already handled by mountBackupCenter's segment parse). Use
|
||||
// replaceState, mirroring the Migrate sub-tabs — the switchTab() we fire
|
||||
// on entry to honor a deep link would otherwise stack a duplicate history
|
||||
// entry on top of the one we navigated in on.
|
||||
window.overviewBackupPage = new BackupPage({
|
||||
embedded: true,
|
||||
onTabChange: (sub) => this._pushUrl(`/apps/overview/backups/${sub}`, true),
|
||||
});
|
||||
await window.overviewBackupPage.init();
|
||||
if (sub) { try { window.overviewBackupPage.switchTab(sub); } catch (_) {} }
|
||||
} catch (_) {
|
||||
|
||||
@ -8,8 +8,14 @@
|
||||
class BackupPage {
|
||||
constructor(opts = {}) {
|
||||
// Embedded in the fleet Overview's Backups tab: the same controller, but
|
||||
// it must not couple to the /backup URL (the fleet owns /overview/backups).
|
||||
// it must not write the standalone /backup URL — the fleet owns the
|
||||
// address bar. It hands us onTabChange so a sub-tab switch still syncs the
|
||||
// canonical /apps/overview/backups/<sub> URL via the OverviewManager.
|
||||
this.embedded = !!opts.embedded;
|
||||
// Host-supplied when embedded: called with the new sub-tab slug after
|
||||
// every switch so the host can reflect it in the URL. Null for the
|
||||
// standalone page, which uses pushTabToUrl() instead.
|
||||
this.onTabChange = typeof opts.onTabChange === 'function' ? opts.onTabChange : null;
|
||||
this.currentTab = 'dashboard';
|
||||
this.dashboard = null;
|
||||
this.locations = null;
|
||||
@ -37,7 +43,10 @@ class BackupPage {
|
||||
and /backup?backup=dashboard (standard query string) so links from
|
||||
either source resolve correctly. */
|
||||
parseTabFromUrl() {
|
||||
if (this.embedded) return null; // embedded: always open on Dashboard; sub-tabs are in-page only
|
||||
// Embedded: the fleet host (OverviewManager.mountBackupCenter) parses the
|
||||
// /apps/overview/backups/<sub> segment and drives switchTab, so the
|
||||
// embedded controller never reads the URL itself.
|
||||
if (this.embedded) return null;
|
||||
const allowed = new Set(['dashboard', 'backups', 'locations', 'configuration']);
|
||||
// Path-based: /backup/<tab> (bare /backup → default tab).
|
||||
const seg = window.location.pathname.replace(/^\/backup\/?/, '').split('/')[0];
|
||||
@ -339,11 +348,18 @@ class BackupPage {
|
||||
this.currentTab = tab;
|
||||
this.applyActiveTabUi(tab);
|
||||
this.updatePrimaryAction();
|
||||
if (!opts.fromPopstate) this.pushTabToUrl(tab);
|
||||
if (!opts.fromPopstate) {
|
||||
// Standalone writes /backup/<tab> itself; embedded lets the fleet host
|
||||
// map the switch onto /apps/overview/backups/<tab> via onTabChange (its
|
||||
// own pushTabToUrl is a no-op). Both are skipped when the switch was
|
||||
// triggered by a popstate — the URL has already moved.
|
||||
this.pushTabToUrl(tab);
|
||||
if (this.onTabChange) { try { this.onTabChange(tab); } catch (_) {} }
|
||||
}
|
||||
}
|
||||
|
||||
pushTabToUrl(tab) {
|
||||
if (this.embedded) return; // embedded: keep the URL at /overview/backups (no sub-tab coupling)
|
||||
if (this.embedded) return; // embedded: the fleet host owns the URL (see onTabChange)
|
||||
const url = `/backup/${tab}`;
|
||||
// Use replaceState for the *first* push (initial tab inferred from
|
||||
// URL); otherwise pushState so back/forward navigates between tabs.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user