From ed319b0f94f3af71a289ba68b5fdaace0e9c14c7 Mon Sep 17 00:00:00 2001 From: librelad Date: Thu, 28 May 2026 14:49:18 +0100 Subject: [PATCH] fix(backup): configs backup gets its own task identity, not "Backup All Apps" System-config backups (libreportal backup system) carry no app slug, so the notification descriptor resolved a blank subject + no icon, and a system-only pick collapsed to `backup all` when no apps were installed. Give them the LibrePortal icon + a "Configs" subject, add backup-system to the system-task logo detection, and guard the whole-fleet collapse on having >=1 app. Rename the visible subject from "System config" to "Configs" throughout the backup UI. Co-Authored-By: Claude Opus 4.7 Signed-off-by: librelad --- .../js/components/backup/backup-page.js | 11 +++++----- .../js/components/tasks/tasks-manager.js | 22 ++++++++++++++----- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/containers/libreportal/frontend/js/components/backup/backup-page.js b/containers/libreportal/frontend/js/components/backup/backup-page.js index ca64b7f..30c72ca 100644 --- a/containers/libreportal/frontend/js/components/backup/backup-page.js +++ b/containers/libreportal/frontend/js/components/backup/backup-page.js @@ -706,7 +706,7 @@ class BackupPage {
-
System config
+
Configs
${this.escape(when)} @@ -1115,7 +1115,7 @@ class BackupPage { ? `/app/${encodeURIComponent(r.app)}/backups?snapshot=${encodeURIComponent(r.id)}` : null; const iconUrl = hasApp ? `/icons/apps/${encodeURIComponent(r.app)}.svg` : '/icons/apps/libreportal.svg'; - const displayName = hasApp ? this.appMeta(r.app).displayName : 'System config'; + const displayName = hasApp ? this.appMeta(r.app).displayName : 'Configs'; const appChip = hasApp ? `${this.escape(displayName)}` : `${this.escape(displayName)}`; @@ -2011,7 +2011,7 @@ class BackupPage { : 'No backup yet'; const rows = [ - row('__system__', '/icons/apps/libreportal.svg', 'System config', sysSub, preTickSystem), + row('__system__', '/icons/apps/libreportal.svg', 'Configs', sysSub, preTickSystem), ...sortedApps.map(app => { const meta = this.appMeta(app.app); const sub = app.latest_snapshot @@ -2063,8 +2063,9 @@ class BackupPage { const appSlugs = selected.filter(s => s !== '__system__'); // Whole-fleet shortcut — `backup all` queues a single task and also - // covers system, instead of N+1 separate tasks. - if (wantsSystem && appSlugs.length === apps.length) { + // covers system, instead of N+1 separate tasks. Requires at least one + // app so a system-only pick never collapses into "backup all". + if (wantsSystem && apps.length > 0 && appSlugs.length === apps.length) { await this.runTask('libreportal backup all', 'backup', null); return; } diff --git a/containers/libreportal/frontend/js/components/tasks/tasks-manager.js b/containers/libreportal/frontend/js/components/tasks/tasks-manager.js index 3628c2d..71457ea 100755 --- a/containers/libreportal/frontend/js/components/tasks/tasks-manager.js +++ b/containers/libreportal/frontend/js/components/tasks/tasks-manager.js @@ -89,7 +89,15 @@ class TasksManager { _taskNotificationDescriptor(task) { const appName = (task && task.app) || null; const action = (task && task.type) || ''; - const isSystemTask = action.startsWith('setup-') || appName === 'system'; + const command = (task && task.command) || ''; + // System-level backups carry no app slug, so they'd otherwise resolve to a + // blank subject + no icon. Give them the LibrePortal identity and a + // distinct subject ("Configs" vs the whole-fleet "All apps"). + const sysBackup = command.match(/^libreportal backup (system|all)\b/); + const sysBackupSubject = sysBackup + ? (sysBackup[1] === 'system' ? 'Configs' : 'All apps') + : null; + const isSystemTask = action.startsWith('setup-') || appName === 'system' || !!sysBackup; let actionTitle = this.formatActionTitle(action); // Tool tasks: prefer the catalog-defined label. const toolCmdMatch = ((task && task.command) || '').match(/libreportal app tool (\S+) (\S+)/); @@ -104,9 +112,11 @@ class TasksManager { if (!toolLabel) toolLabel = toolId.split(/[_-]/).map(w => w ? w.charAt(0).toUpperCase() + w.slice(1) : '').join(' '); actionTitle = toolLabel; } - const displayName = isSystemTask - ? 'LibrePortal' - : ((appName && window.getAppDisplayName) ? window.getAppDisplayName(appName) : (appName || '')); + const displayName = sysBackupSubject + ? sysBackupSubject + : (isSystemTask + ? 'LibrePortal' + : ((appName && window.getAppDisplayName) ? window.getAppDisplayName(appName) : (appName || ''))); const icon = isSystemTask ? '/icons/libreportal.svg' : (appName ? `/icons/apps/${encodeURIComponent(appName)}.svg` : null); @@ -988,7 +998,7 @@ class TasksManager { // -- Backup: system / locations ---------------------------------------- { match: /^libreportal backup all\b/, title: 'LibrePortal - Backup All Apps' }, { match: /^libreportal backup verify\b/, title: 'LibrePortal - Verify Backups' }, - { match: /^libreportal backup system\b/, title: 'LibrePortal - Backup System Config' }, + { match: /^libreportal backup system\b/, title: 'LibrePortal - Backup Configs' }, { match: /^libreportal backup location add\b/, title: 'LibrePortal - Add Backup Location' }, { match: /^libreportal backup location remove\b/, title: 'LibrePortal - Remove Backup Location' }, { match: /^libreportal backup location init\b/, title: 'LibrePortal - Initialise Backup Locations' }, @@ -1083,7 +1093,7 @@ class TasksManager { the row can show the LibrePortal logo instead of a blank icon slot. */ isLibrePortalSystemTask(task) { if (!task || !task.command || task.app) return false; - return /^libreportal (setup|backup\s+all|backup\s+verify|backup\s+location|backup\s+repo|restore\s+migrate\s+system|restore\s+migrate\s+discover|restore\s+first-run|webui|config|update)\b/.test(task.command); + return /^libreportal (setup|backup\s+all|backup\s+system|backup\s+verify|backup\s+location|backup\s+repo|restore\s+migrate\s+system|restore\s+migrate\s+discover|restore\s+first-run|webui|config|update)\b/.test(task.command); } /* Render the leading icon(s) on a task row: