v0.309.0: the notification page shows three group switches; details and cooldown under Részletek (R-388 slice 1, decision 200)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-10-12 16:50:41 +02:00
parent 68294ecb6f
commit 462196e29b
11 changed files with 659 additions and 89 deletions
+43
View File
@@ -22,6 +22,7 @@ import (
"gitea.dooplex.hu/admin/felhom-controller/internal/crypto"
"gitea.dooplex.hu/admin/felhom-controller/internal/i18n"
"gitea.dooplex.hu/admin/felhom-controller/internal/infra"
"gitea.dooplex.hu/admin/felhom-controller/internal/logx"
"gitea.dooplex.hu/admin/felhom-controller/internal/scheduler"
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
@@ -2846,6 +2847,47 @@ func dedupeEvents(in []string) []string {
return out
}
// applyNotificationGroupSwitches — R-388 slice 1 (`08` §8.1). The page posts, per group, the switch
// (`group_<key>` = "on" when ticked) and the state it was RENDERED in (`group_<key>_shown`:
// on / off / custom). The per-detector switches under „Részletek" are posted too, so the rule is:
//
// - the household MOVED the group switch (rendered on → now off, rendered off/custom → now on):
// the group wins — all its detectors are set, or all cleared;
// - the switch is where it was rendered: the per-detector switches are authoritative, exactly as
// before this slice. So a „custom" (egyedi) group saved untouched writes its detectors back as
// they were — no household's saved choice changes (TestR388_CustomGroupSavedBackUnchanged).
//
// A form with no `_shown` field (an old page left open, a bookmarked POST) is not touched by this
// step at all: the per-detector names decide, as in v0.308.0.
func (s *Server) applyNotificationGroupSwitches(r *http.Request, enabled []string) []string {
for _, g := range settings.NotificationGroups {
shown := r.FormValue("group_" + g.Key + "_shown")
if shown != settings.NotifGroupOn && shown != settings.NotifGroupOff && shown != settings.NotifGroupCustom {
continue
}
on := r.FormValue("group_"+g.Key) == "on"
switch {
case shown == settings.NotifGroupOn && !on:
members := make(map[string]bool, len(g.Events))
for _, e := range g.Events {
members[e] = true
}
kept := enabled[:0:0]
for _, e := range enabled {
if !members[e] {
kept = append(kept, e)
}
}
enabled = kept
logx.Debugf(s.logger, "[web] notification group switched off: group=%s", g.Key)
case shown != settings.NotifGroupOn && on:
enabled = append(enabled, g.Events...)
logx.Debugf(s.logger, "[web] notification group switched on: group=%s was=%s", g.Key, shown)
}
}
return enabled
}
func (s *Server) settingsNotificationsHandler(w http.ResponseWriter, r *http.Request) {
_ = r.ParseForm()
@@ -2916,6 +2958,7 @@ func (s *Server) settingsNotificationsHandler(w http.ResponseWriter, r *http.Req
enabledEvents = append(enabledEvents, c.events...)
}
}
enabledEvents = s.applyNotificationGroupSwitches(r, enabledEvents)
enabledEvents = dedupeEvents(enabledEvents)
// R-329 Part 4 — A SAVE THAT CHANGES NOTHING MUST STORE NOTHING NEW.