From fc94f56162faf5df15a661a23e545386a2241cbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jesko=20Ansch=C3=BCtz?= Date: Fri, 27 Mar 2026 20:24:21 +0100 Subject: [PATCH] =?UTF-8?q?feat(ui):=20per-Screen-Override=20in=20=C3=9Cbe?= =?UTF-8?q?rsichtskarte=20und=20Detailseite?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- .../internal/httpapi/manage/templates.go | 89 +++++++++++++++++++ server/backend/internal/httpapi/manage/ui.go | 3 + 2 files changed, 92 insertions(+) diff --git a/server/backend/internal/httpapi/manage/templates.go b/server/backend/internal/httpapi/manage/templates.go index 1038207..07e93c5 100644 --- a/server/backend/internal/httpapi/manage/templates.go +++ b/server/backend/internal/httpapi/manage/templates.go @@ -982,6 +982,27 @@ const manageTmpl = `

✓ Gespeichert

+ +
+

Einschalten bis (Override)

+ {{if not_expired .Schedule.OverrideOnUntil}} +

+ ⏰ Aktiv bis {{.Schedule.OverrideOnUntil.Format "02.01.2006 15:04"}} +

+ + {{else}} +

+ Überschreibt Zeitplan und Wochenend-Sperre — Monitor bleibt bis zum angegebenen Zeitpunkt eingeschaltet. +

+
+ + +
+ {{end}} +

✓ Gespeichert

+
@@ -1189,6 +1210,33 @@ function saveSchedule() { }).catch(function() { showToast('Netzwerkfehler', 'is-danger'); }); } +function setScreenOverridePage() { + var val = document.getElementById('screen-override-until').value; + if (!val) { showToast('Bitte Datum und Uhrzeit angeben', 'is-warning'); return; } + var dt = new Date(val); + fetch('/api/v1/screens/' + SCREEN_SLUG + '/override', { + method: 'POST', + headers: {'Content-Type': 'application/json', 'X-CSRF-Token': getCsrf(), 'X-Requested-With': 'fetch'}, + body: JSON.stringify({on_until: dt.toISOString()}) + }).then(function(r) { + if (r.ok) { + var ok = document.getElementById('screen-override-ok'); + if (ok) { ok.classList.add('show'); setTimeout(function() { ok.classList.remove('show'); }, 2000); } + } else { showToast('Fehler beim Setzen des Overrides', 'is-danger'); } + }).catch(function() { showToast('Netzwerkfehler', 'is-danger'); }); +} + +function clearScreenOverridePage() { + fetch('/api/v1/screens/' + SCREEN_SLUG + '/override', { + method: 'POST', + headers: {'Content-Type': 'application/json', 'X-CSRF-Token': getCsrf(), 'X-Requested-With': 'fetch'}, + body: JSON.stringify({on_until: null}) + }).then(function(r) { + if (r.ok) { location.reload(); } + else { showToast('Fehler beim Aufheben des Overrides', 'is-danger'); } + }).catch(function() { showToast('Netzwerkfehler', 'is-danger'); }); +} + // ─── ?msg= toast ───────────────────────────────────────────────── (function() { var msg = new URLSearchParams(window.location.search).get('msg'); @@ -1447,6 +1495,24 @@ const screenOverviewTmpl = `
+ +
+ {{if .OverrideOnUntil}} + ⏰ Ein bis {{.OverrideOnUntil.Format "02.01. 15:04"}} + + {{else}} +
+ Einschalten bis… +
+ + +
+
+ {{end}} +
@@ -1575,6 +1641,29 @@ function deleteGlobalOverride() { if (r.ok) { location.reload(); } }).catch(function(){}); } + +function setScreenOverride(slug) { + var val = document.getElementById('override-until-' + slug).value; + if (!val) return; + var dt = new Date(val); + fetch('/api/v1/screens/' + slug + '/override', { + method: 'POST', + headers: {'Content-Type': 'application/json', 'X-CSRF-Token': getCsrf(), 'X-Requested-With': 'fetch'}, + body: JSON.stringify({on_until: dt.toISOString()}) + }).then(function(r) { + if (r.ok) { location.reload(); } + }).catch(function(){}); +} + +function clearScreenOverride(slug) { + fetch('/api/v1/screens/' + slug + '/override', { + method: 'POST', + headers: {'Content-Type': 'application/json', 'X-CSRF-Token': getCsrf(), 'X-Requested-With': 'fetch'}, + body: JSON.stringify({on_until: null}) + }).then(function(r) { + if (r.ok) { location.reload(); } + }).catch(function(){}); +} ` diff --git a/server/backend/internal/httpapi/manage/ui.go b/server/backend/internal/httpapi/manage/ui.go index e7dc136..fb7a443 100644 --- a/server/backend/internal/httpapi/manage/ui.go +++ b/server/backend/internal/httpapi/manage/ui.go @@ -137,6 +137,9 @@ var tmplFuncs = template.FuncMap{ } return t.Format("02.01.2006 15:04") }, + "not_expired": func(t *time.Time) bool { + return t != nil && time.Now().Before(*t) + }, } // HandleAdminUI renders the admin overview page (screens + users tabs).