Files
felhom-controller/controller/internal/settings/notif_groups.go
T

89 lines
3.1 KiB
Go

package settings
// R-388 slice 1 (`08-alarm-ladder.md` §8.1, `09` §3 decision 200; operator answers 2026-10-12):
// the notification page shows THREE group switches instead of one switch per detector. A group is a
// view over the SAME `enabled_events` list — saving a group writes all its detectors into it, so
// nothing on the wire and nothing on the hub changes.
//
// Every detector the page can save belongs to EXACTLY ONE group. Pinned by
// TestR388_EveryPageSwitchIsInExactlyOneGroup (web), which reads the rendered page's switches and
// fails when a new detector gets a checkbox but no group.
// Notification group keys (also the form-field stems `group_<key>` / `group_<key>_shown`).
const (
NotifGroupMachine = "machine" // „A gépem és a meghajtóim" — the household acts
NotifGroupApps = "apps" // „Az alkalmazásaim"
NotifGroupHandled = "handled" // „Amit mi intézünk" — we act; the operator always gets these
)
// Group states as the page renders them.
const (
NotifGroupOn = "on"
NotifGroupOff = "off"
NotifGroupCustom = "custom" // some of its detectors on, some off — „egyedi"
)
// NotificationGroup is one switch on the notification page and the detectors behind it.
type NotificationGroup struct {
Key string
Events []string
}
// NotificationGroups is the §8.1 membership table, in page order.
var NotificationGroups = []NotificationGroup{
{Key: NotifGroupMachine, Events: []string{
"storage_disconnected", "storage_reconnected", "disk_warning", "disk_critical", "offbox_enlarge_blocked",
}},
{Key: NotifGroupApps, Events: []string{
"app_stopped_unhealthy", "app_start_failed", "app_update_held", "app_update_undone",
}},
{Key: NotifGroupHandled, Events: []string{
"backup_failed", "db_dump_failed", "backup_integrity_failed", "crossdrive_failed",
"expected_backup_missed", "expected_dbdump_missed", "node_down", "health_critical", "health_recovered",
}},
}
// NotificationGroupByKey returns the group with this key, or false.
func NotificationGroupByKey(key string) (NotificationGroup, bool) {
for _, g := range NotificationGroups {
if g.Key == key {
return g, true
}
}
return NotificationGroup{}, false
}
// NotificationGroupState reports whether all, none or some of the group's detectors are in enabled.
// An unknown key reads as off (renders unchecked, and the save leaves the details authoritative).
func NotificationGroupState(enabled []string, key string) string {
g, ok := NotificationGroupByKey(key)
if !ok {
return NotifGroupOff
}
set := make(map[string]bool, len(enabled))
for _, e := range enabled {
set[e] = true
}
on := 0
for _, e := range g.Events {
if set[e] {
on++
}
}
switch {
case on == len(g.Events):
return NotifGroupOn
case on == 0:
return NotifGroupOff
default:
return NotifGroupCustom
}
}
// GroupState is the template's view of NotificationGroupState over this household's list.
// VALUE receiver on purpose: a pointer receiver compiles and passes vet, then 500s at render
// (`.claude/rules/ui-hungarian.md`).
func (p NotificationPrefs) GroupState(key string) string {
return NotificationGroupState(p.EnabledEvents, key)
}