From 102fc38da0b3880fd1ccb79e43f7cd29c46c44e1 Mon Sep 17 00:00:00 2001 From: librelad Date: Wed, 27 May 2026 00:42:58 +0100 Subject: [PATCH] ui(backup): merge System config into the Backup status grid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dashboard had two parallel sections — 'Per-app status' (every app's latest backup) and a standalone 'System config' card below it. Folded them into one grid: a single 'Backup status' card with the System config tile rendered FIRST, then every app tile. Why first: a bare-metal restore needs the system config (CFG_* + backup-location credentials) — without it the backups exist but the keys to reach them don't. Putting it at eye-level above the app tiles makes the dependency visible. System tile reuses the .backup-app-tile shape: server-stack icon, 'System config' as the name, status dot + 'Last backed up X ago' / 'No backup yet'. Plus two compact inline action buttons (Back up / Restore) on the right that wire into the same data-action handlers the old standalone card used — no behaviour change, just the visual container. grid-column: 1 / -1 on the system tile makes it span the row so the two action buttons fit alongside the meta text without crushing the app-tile grid template. Section header: 'Per-app status' → 'Backup status' + hint 'System config and every installed app's latest backup. System config always first — a bare-metal restore needs it.' Dashboard subtitle updated to match. Signed-off-by: librelad --- .../frontend/html/backup-content.html | 35 +++------- .../js/components/backup/backup-page.js | 64 +++++++++++++++---- 2 files changed, 59 insertions(+), 40 deletions(-) diff --git a/containers/libreportal/frontend/html/backup-content.html b/containers/libreportal/frontend/html/backup-content.html index c7ea0c0..6e974e4 100644 --- a/containers/libreportal/frontend/html/backup-content.html +++ b/containers/libreportal/frontend/html/backup-content.html @@ -83,9 +83,15 @@
-

Per-app status

- Latest backup per app on this host +

Backup status

+ System config and every installed app's latest backup. System config always first — a bare-metal restore needs it.
+
@@ -96,31 +102,6 @@
-
-
-

- - - - - - System config -

- Global settings, WebUI login & backup-location credentials -
-
- No backup yet -
-

- Snapshot the LibrePortal system config to every enabled location so a bare-metal - restore is self-sufficient — without it, the credentials needed to reach your own - backups live only on this box. Runs automatically with “Backup all apps” too. -

-
- - -
-
diff --git a/containers/libreportal/frontend/js/components/backup/backup-page.js b/containers/libreportal/frontend/js/components/backup/backup-page.js index 9f5d63b..707f9c5 100644 --- a/containers/libreportal/frontend/js/components/backup/backup-page.js +++ b/containers/libreportal/frontend/js/components/backup/backup-page.js @@ -456,7 +456,7 @@ class BackupPage { subtitleFor(tab) { return { - dashboard: 'Per-app status and storage at a glance.', + dashboard: 'Backup status — system config + every app — at a glance.', backups: 'Every snapshot across every enabled location.', locations: 'Where backups are stored. Add, edit, or remove destinations.', migrate: 'Restore an app from another LibrePortal that shares one of your backup locations.', @@ -586,20 +586,14 @@ class BackupPage { ${this.tile('Total stored', this.formatBytes(totalSize), 'deduplicated, encrypted')} `; + // System config tile is rendered FIRST so the bare-metal-restore + // prerequisite is always at eye-level — without it, the user's + // backups exist but the credentials needed to reach them don't. + const systemTileHtml = this.renderSystemTile(d.system || {}); if (!apps.length) { - appGrid.innerHTML = `
No apps installed yet.
`; + appGrid.innerHTML = systemTileHtml + `
No apps installed yet.
`; } else { - appGrid.innerHTML = apps.map(app => this.renderAppTile(app)).join(''); - } - - const sysStatus = document.getElementById('backup-system-status'); - if (sysStatus) { - const sys = d.system || {}; - const hasSys = !!sys.latest_snapshot; - sysStatus.innerHTML = ` - - ${hasSys ? 'Last backed up ' + this.formatRelative(sys.latest_time) : 'No backup yet'} - `; + appGrid.innerHTML = systemTileHtml + apps.map(app => this.renderAppTile(app)).join(''); } if (!locs.length) { @@ -620,6 +614,50 @@ class BackupPage { } } + // System config tile — same shape as an app tile but with a server icon + // and two inline action buttons (the standalone System config card used + // to host these). Rendered first in the Backup status grid so it always + // sits at eye-level. + renderSystemTile(sys) { + const has = !!sys.latest_snapshot; + const dot = has ? 'ok' : 'none'; + const when = has ? 'Last backed up ' + this.formatRelative(sys.latest_time) : 'No backup yet'; + return ` +
+
+ + + + + + +
+
+
System config
+
+ + ${this.escape(when)} +
+
+
+ + +
+
+ `; + } + tile(label, value, detail) { return `