feat(backup): make config-backup warning stand out and dismissible

Add a large amber alert-triangle icon to the 'keep your config backed up offline' banner and a close (x) button in its top-right. Dismissal is stored in localStorage (libreportal:backup-config-warning-dismissed) — a per-browser UI nudge, not server config — and hides both the banner and its divider until cleared.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
librelad 2026-05-23 00:07:38 +01:00
parent 218378a752
commit 14ba3b03c7
2 changed files with 55 additions and 1 deletions

View File

@ -840,6 +840,31 @@
min-width: 0;
}
/* Big amber alert icon so the warning reads at a glance. */
.backup-warning-banner-icon {
flex-shrink: 0;
color: #f59e0b;
}
/* Dismiss (×) in the banner's top-right corner. */
.backup-warning-banner-close {
flex-shrink: 0;
align-self: flex-start;
background: transparent;
border: none;
color: var(--text-secondary, rgba(var(--text-rgb), 0.6));
cursor: pointer;
padding: 2px;
border-radius: 6px;
line-height: 0;
transition: background 0.15s ease, color 0.15s ease;
}
.backup-warning-banner-close:hover {
background: rgba(var(--text-rgb), 0.1);
color: var(--text-primary);
}
.backup-warning-banner [data-action="export-passwords"] {
flex-shrink: 0;
white-space: nowrap;

View File

@ -203,6 +203,16 @@ class BackupPage {
const exportBtn = e.target.closest('[data-action="export-passwords"]');
if (exportBtn) { this.exportRepositoryPasswords(exportBtn); return; }
const dismissWarn = e.target.closest('[data-action="dismiss-config-warning"]');
if (dismissWarn) {
localStorage.setItem('libreportal:backup-config-warning-dismissed', '1');
const banner = dismissWarn.closest('.backup-warning-banner');
const divider = banner?.nextElementSibling;
if (divider && divider.classList.contains('config-divider')) divider.remove();
banner?.remove();
return;
}
const saveBtn = e.target.closest('[data-backup-save]');
if (saveBtn) {
this.saveSection(saveBtn.dataset.backupSave);
@ -783,8 +793,17 @@ class BackupPage {
const body = document.getElementById('backup-configuration-body');
if (!body) return;
body.innerHTML = `
// The warning is a dismissible nudge, so its dismissed state lives in
// localStorage (per-browser) rather than server config. Banner + its
// divider are omitted entirely once dismissed.
const warningDismissed = localStorage.getItem('libreportal:backup-config-warning-dismissed') === '1';
const warningHTML = warningDismissed ? '' : `
<div class="backup-warning-banner">
<svg class="backup-warning-banner-icon" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
<line x1="12" y1="9" x2="12" y2="13"></line>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
</svg>
<div class="backup-warning-banner-text">
<strong>Keep your LibrePortal config backed up offline.</strong>
<span>Repository passwords live inside the config directory. Without that backup, snapshots cannot be decrypted by anyone including you.</span>
@ -797,8 +816,18 @@ class BackupPage {
</svg>
Export Repository Passwords
</button>
<button type="button" class="backup-warning-banner-close" data-action="dismiss-config-warning" title="Dismiss this warning" aria-label="Dismiss this warning">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
<div class="config-divider"></div>
`;
body.innerHTML = `
${warningHTML}
<div id="config-section" class="backup-embedded-config"></div>
`;