diff --git a/containers/libreportal/frontend/css/backup.css b/containers/libreportal/frontend/css/backup.css index f3aab44..ede4a82 100755 --- a/containers/libreportal/frontend/css/backup.css +++ b/containers/libreportal/frontend/css/backup.css @@ -723,6 +723,47 @@ border-top: 1px dashed rgba(var(--text-rgb), 0.08); } +/* Full-width "Advanced" disclosure in the location editor's Connection + section. Reveals the advanced fields (URI override, SSH port, append-only). */ +.backup-loc-advanced-toggle { + display: flex; + align-items: center; + gap: 8px; + width: 100%; + margin-top: 14px; + padding: 10px 12px; + background: rgba(var(--text-rgb), 0.03); + border: 1px solid rgba(var(--text-rgb), 0.08); + border-radius: 8px; + color: var(--text-primary); + font: inherit; + font-size: 0.9rem; + font-weight: 600; + cursor: pointer; + transition: background 0.15s ease; +} + +.backup-loc-advanced-toggle:hover { + background: rgba(var(--text-rgb), 0.06); +} + +.backup-loc-advanced-chevron { + transition: transform 0.15s ease; + flex-shrink: 0; +} + +.backup-loc-advanced-toggle.open .backup-loc-advanced-chevron { + transform: rotate(90deg); +} + +.backup-loc-advanced { + margin-top: 12px; +} + +.backup-loc-advanced[hidden] { + display: none; +} + .backup-form-footer { display: flex; justify-content: flex-end; diff --git a/containers/libreportal/frontend/js/components/backup/backup-page.js b/containers/libreportal/frontend/js/components/backup/backup-page.js index ec586c5..816f003 100644 --- a/containers/libreportal/frontend/js/components/backup/backup-page.js +++ b/containers/libreportal/frontend/js/components/backup/backup-page.js @@ -198,6 +198,18 @@ class BackupPage { return; } + const advToggle = e.target.closest('[data-action="toggle-loc-advanced"]'); + if (advToggle) { + const sec = document.getElementById(advToggle.dataset.target); + if (sec) { + const show = sec.hasAttribute('hidden'); + sec.toggleAttribute('hidden', !show); + advToggle.setAttribute('aria-expanded', show ? 'true' : 'false'); + advToggle.classList.toggle('open', show); + } + return; + } + if (e.target.closest('[data-close-modal]') || e.target.matches('.backup-modal')) { this.closeAllModals(); return; @@ -263,7 +275,8 @@ class BackupPage { const ts = Date.now(); const [dashboard, locations] = await Promise.all([ this.fetchJson(`/data/backup/generated/dashboard.json?t=${ts}`), - this.fetchJson(`/data/backup/generated/locations.json?t=${ts}`) + this.fetchJson(`/data/backup/generated/locations.json?t=${ts}`), + this.loadSystemConfigs() ]); this.dashboard = dashboard; this.locations = locations; @@ -285,6 +298,19 @@ class BackupPage { catch { return null; } } + /* Load the unified config file once for the Locations editor: configData + carries field metadata (titles/descriptions/options/advanced) the editor + renders from; systemConfigs is the flat key->value map used for default + lookups (e.g. CFG_BACKUP_ENGINE) and save-time change detection. */ + async loadSystemConfigs() { + const data = await this.fetchJson(`/data/config/generated/configs.json?t=${Date.now()}`); + if (!data) return; + window.configData = data; + const flat = {}; + for (const [k, v] of Object.entries(data.config || {})) flat[k] = v?.value ?? ''; + window.systemConfigs = flat; + } + async loadEngines() { const ts = Date.now(); const index = await this.fetchJson(`/data/backup/generated/engines/index.json?t=${ts}`); @@ -1400,19 +1426,61 @@ class BackupPage { KEEP_YEARLY: loc.keep_yearly }; - // Single .config-fields grid, exactly like /config's renderer — the grid - // (repeat(3, 1fr)) handles the row layout itself, and hidden fields - // (PATH_MODE/SSH/etc.) drop out cleanly without leaving column gaps. - let html = '