From 59e256a042d2ebffac02dd4099084cb95dee414d Mon Sep 17 00:00:00 2001 From: Andreas Wrede Date: Tue, 12 May 2026 09:32:32 -0400 Subject: [PATCH] feat: add nav bar button to publish pending config changes Shows an orange "Publish Config" button to the left of the alert-pie for admin users when there are staged config changes. Uses localStorage to persist staged changes across page navigations so the button appears on any page, not just settings. Co-Authored-By: Claude Sonnet 4.6 --- hbd/server/templates/head.html | 17 +++++++++++++ hbd/server/templates/nav.html | 38 ++++++++++++++++++++++++++++++ hbd/server/templates/settings.html | 5 ++++ 3 files changed, 60 insertions(+) diff --git a/hbd/server/templates/head.html b/hbd/server/templates/head.html index 3167e90..517fc17 100644 --- a/hbd/server/templates/head.html +++ b/hbd/server/templates/head.html @@ -125,6 +125,23 @@ .nav-links a { margin-right: 0; padding: 6px 0; font-size: 1em; } } + /* Pending config publish button */ + .nav-publish-btn { + background: #e65100; + color: #fff; + border: none; + border-radius: 4px; + padding: 4px 10px; + font-size: 0.82em; + font-weight: 600; + cursor: pointer; + flex-shrink: 0; + white-space: nowrap; + margin-left: auto; + } + .nav-publish-btn:hover { background: #bf360c; } + .nav-publish-btn:disabled { opacity: 0.7; cursor: default; } + /* Swiss railway clock — nav */ .nav-pie { flex-shrink: 0; diff --git a/hbd/server/templates/nav.html b/hbd/server/templates/nav.html index 26d82a0..ab71d5a 100644 --- a/hbd/server/templates/nav.html +++ b/hbd/server/templates/nav.html @@ -11,6 +11,9 @@ {% endif %} About + {% if current_user and current_user.admin %} + + {% endif %} @@ -92,5 +95,40 @@ document.addEventListener('DOMContentLoaded', function() { updateAlertPie(); setInterval(updateAlertPie, 30000); + navCheckPendingConfig(); + window.addEventListener('storage', navCheckPendingConfig); }); + + function navCheckPendingConfig() { + var btn = document.getElementById('nav-publish-btn'); + if (!btn) return; + btn.style.display = localStorage.getItem('hbd_pending_config') ? '' : 'none'; + } + + async function navPublishConfig() { + var btn = document.getElementById('nav-publish-btn'); + var pending = localStorage.getItem('hbd_pending_config'); + if (!pending) return; + var staged; + try { staged = JSON.parse(pending); } catch(e) { return; } + if (btn) { btn.disabled = true; btn.textContent = 'Saving…'; } + try { + var resp = await fetch('/api/0/config', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: pending + }); + if (resp.ok) { + localStorage.removeItem('hbd_pending_config'); + window.location.reload(); + } else { + var err = await resp.json().catch(function() { return {}; }); + alert('Error: ' + (err.error || resp.statusText)); + if (btn) { btn.disabled = false; btn.textContent = '⚠ Publish Config'; } + } + } catch(e) { + alert('Network error: ' + e.message); + if (btn) { btn.disabled = false; btn.textContent = '⚠ Publish Config'; } + } + } diff --git a/hbd/server/templates/settings.html b/hbd/server/templates/settings.html index 2a55dc5..d910644 100644 --- a/hbd/server/templates/settings.html +++ b/hbd/server/templates/settings.html @@ -871,9 +871,13 @@ if (count > 0) { document.getElementById('pending-count').textContent = count; banner.style.display = 'flex'; + localStorage.setItem('hbd_pending_config', JSON.stringify(_staged)); } else { banner.style.display = 'none'; + localStorage.removeItem('hbd_pending_config'); } + const navBtn = document.getElementById('nav-publish-btn'); + if (navBtn) navBtn.style.display = count > 0 ? '' : 'none'; } function stageFormSection(sectionId, apiSection) { @@ -1050,6 +1054,7 @@ function discardAll() { Object.keys(_staged).forEach(k => delete _staged[k]); + localStorage.removeItem('hbd_pending_config'); updatePendingBanner(); window.location.reload(); }