From 88b431ee86d92467f17fa3fe6ea2a8726d528ec2 Mon Sep 17 00:00:00 2001 From: librelad Date: Tue, 26 May 2026 23:58:52 +0100 Subject: [PATCH] style(migrate): tighten card header + give the empty state a real CTA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Migrate tab carried two walls of explanation text — a 3-line hint under the h2 ("Pulls a snapshot taken on another host…") and an even longer empty-state paragraph ("Either no other LibrePortal has backed up to a location this host can see, or this is the only host using its locations…"). Both spelled out diagnosis the user can infer from the empty list itself, and the tone didn't match the rest of the backup page (cards elsewhere have a short title + a 4-6 word hint, with any long explanation as a hover title attribute). Three changes: 1. h2 down to "Cross-host migrate" with a small ℹ️ carrying the full explanation as a title= tooltip — matches the existing tooltip pattern in the Locations form (BACKUP_RETENTION_PRESET_META). The short subtitle "Restore an app from another LibrePortal" stays as backup-card-hint, mirroring "Per-app status / Latest backup per app on this host" elsewhere on the page. 2. The empty state is now the standard `
` container (same shape Locations + Snapshots use), one trimmed line ("No backups from other hosts visible in any enabled location. Add a shared backup location on both hosts to enable cross-host migrate.") instead of two paragraphs. 3. Added an "Open Locations" CTA button inside the empty state — the #1 next-step for a user staring at this empty list is to add a shared location, which lives one tab over. New data-action "go-to-locations" wired through the existing event-delegation handler in backup-page.js calling switchTab('locations'). The renderMigrate JS still toggles #backup-migrate-empty.hidden — the wrapper id is unchanged, only its inner markup tightened. No behavioural change beyond the CTA + tab switch. Co-Authored-By: Claude Opus 4.7 Signed-off-by: librelad --- .../frontend/html/backup-content.html | 21 ++++++++----------- .../js/components/backup/backup-page.js | 3 +++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/containers/libreportal/frontend/html/backup-content.html b/containers/libreportal/frontend/html/backup-content.html index 16aa404..9466658 100644 --- a/containers/libreportal/frontend/html/backup-content.html +++ b/containers/libreportal/frontend/html/backup-content.html @@ -154,20 +154,17 @@
-

Restore an app from another LibrePortal

- - Pulls a snapshot taken on another host out of a shared backup location and - lays it down here. The destination's existing copy of the app is snapshotted - first (rollback safety), then replaced. - +

Cross-host migrate ℹ️

+ Restore an app from another LibrePortal
diff --git a/containers/libreportal/frontend/js/components/backup/backup-page.js b/containers/libreportal/frontend/js/components/backup/backup-page.js index 05997a1..9f5d63b 100644 --- a/containers/libreportal/frontend/js/components/backup/backup-page.js +++ b/containers/libreportal/frontend/js/components/backup/backup-page.js @@ -282,6 +282,9 @@ class BackupPage { const exportBtn = e.target.closest('[data-action="export-passwords"]'); if (exportBtn) { this.toggleExportMenu(false); this.exportRepositoryPasswords(exportBtn); return; } + const goToLocations = e.target.closest('[data-action="go-to-locations"]'); + if (goToLocations) { this.switchTab('locations'); return; } + const dismissWarn = e.target.closest('[data-action="dismiss-config-warning"]'); if (dismissWarn) { window.Dismissible?.dismiss('backup-config-warning');