From 25dc51d63e44c27295425adf4d2bd1295225a907 Mon Sep 17 00:00:00 2001 From: librelad Date: Wed, 3 Jun 2026 00:23:10 +0100 Subject: [PATCH] fix(webui): make Overview sub-tab areas match the app-detail layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrate and Backups detached their sub-tab strip from the content (16px gap) and Backups had no top-level header and a transparent (card-less) body — so both read as different from the per-app Config sub-tabs. - Migrate: nest .tabs-content inside the same .tabs-wrapper as .tabs-list (the canonical app-detail structure) and drop the gap, so the strip joins the content card with no space and clean corners. - Backups: inject the shared "Backups" .config-title header above the embedded BackupPage; drop the sub-tab strip's bottom gap; give the content (.main) the connected .tabs-content surface (card bg + rounded bottom) and round the strip's outer top corners — so it reads as one tabs-within-tabs unit. Co-Authored-By: Claude Opus 4.8 --- .../apps/core/html/apps-unified-layout.html | 12 +++++++----- .../components/apps/overview/css/overview.css | 18 +++++++++++++----- .../apps/overview/js/overview-manager.js | 5 ++++- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/containers/libreportal/frontend/components/apps/core/html/apps-unified-layout.html b/containers/libreportal/frontend/components/apps/core/html/apps-unified-layout.html index 2494a88..1f17c9e 100755 --- a/containers/libreportal/frontend/components/apps/core/html/apps-unified-layout.html +++ b/containers/libreportal/frontend/components/apps/core/html/apps-unified-layout.html @@ -314,7 +314,9 @@
- +
-
-
-
-
+
+
+
+
diff --git a/containers/libreportal/frontend/components/apps/overview/css/overview.css b/containers/libreportal/frontend/components/apps/overview/css/overview.css index a7f810a..0a2c6d2 100644 --- a/containers/libreportal/frontend/components/apps/overview/css/overview.css +++ b/containers/libreportal/frontend/components/apps/overview/css/overview.css @@ -134,7 +134,7 @@ max-width: none; height: auto; min-height: 0; - margin: 0 0 16px; + margin: 0; /* no gap — the strip joins the content card below */ padding: 0; background: transparent; border: none; @@ -179,15 +179,23 @@ height: 16px; flex: 0 0 auto; } +/* Round only the outer top corners to follow the strip's chrome (the global + .tab-button rule does this for app/Migrate sub-tabs; .category needs its own). */ +#overview-view #ov-pane-backups .backup-layout > .sidebar .category:first-child { border-top-left-radius: 12px; } +#overview-view #ov-pane-backups .backup-layout > .sidebar .category:last-child { border-top-right-radius: 12px; } +/* The content area becomes the connected .tabs-content card (visible surface, + rounded bottom) so the strip + body read as one unit, like the app Config tab. */ #overview-view #ov-pane-backups .backup-layout > .main { width: 100%; min-width: 0; + background: var(--card-bg); + border-radius: 0 0 12px 12px; } -/* ---- Migrate tab: nested segmented sub-tabs (per-app Config-tab design) -- */ -/* .tabs-wrapper/.tabs-list/.tab-button come from the global base.css; only the - panel show/hide is scoped here. */ -#overview-view .ov-subtabs { margin-bottom: 16px; } +/* ---- Migrate tab: segmented sub-tabs (per-app Config-tab design) -------- */ +/* .tabs-wrapper/.tabs-list/.tabs-content/.tab-button come from base.css; the + list + content share one .tabs-wrapper (markup) so they join with no gap, + exactly like the app Config tab. Only the panel show/hide is scoped here. */ #overview-view .ov-subtabs-content .tab-panel { display: none; } #overview-view .ov-subtabs-content .tab-panel.active { display: block; } diff --git a/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js b/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js index 21f340f..e9db4ab 100644 --- a/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js +++ b/containers/libreportal/frontend/components/apps/overview/js/overview-manager.js @@ -473,7 +473,10 @@ class OverviewManager { // and #mobile-overlay). BackupPage selects its own nodes by class, so this // is safe; it just keeps the document free of duplicate ids. html = html.replace('id="sidebar"', '').replace('
', ''); - pane.innerHTML = html; + // Lead with the shared in-content header (BackupPage supplies only its own + // per-section headers inside the layout), so Backups matches every other + // fleet tab. It persists across refreshes since revisits don't reset innerHTML. + pane.innerHTML = this.renderHeader('backups') + html; if (typeof BackupPage === 'undefined') { pane.innerHTML = '
Backup center unavailable.
'; return;