feat(notruf): Alarmpause pro Nutzer im Notfall-Profil (Minuten, 0=aus, leer=global)

Bisher war die Wiederauslöse-Sperre nur global einstellbar. Jetzt zusätzlich
pro Nutzer im Notfall-Profil (Admin), hinter „Standort im Notfall mitsenden":
„Pause bis zum erneuten Alarm: [n] Minuten" (leer = globaler Standard, 0 = aus).

- Auflösung serverseitig: Nutzer-Pref > globale Einstellung > 5
  (_resolve_cooldown_minutes in emergency.py; emergency_cooldown_status nutzt sie,
  die Notruf-UI/der Status-Endpunkt liefern dadurch automatisch den per-User-Wert).
- Schema AdminUserPrefsUpdate.emergency_cooldown_minutes (String) mit Validator
  (leer/0–60, sonst 422). /api/admin/users gibt das Feld jetzt mit aus
  (sonst lädt das Formular leer).
- Frontend: Zahlenfeld + Hinweis im Notfall-Profil, über PROF_KEYS/PROF_VALIDATORS
  verdrahtet. Bump app.js v=51 -> v=52.
- Doku: BEDIENUNGSANLEITUNG § Nutzer › Verwalten.

Tests: per-User-Override/Erben/Aus + Schema-Validierung. 254 Offline-Tests grün.
Live verifiziert: PUT prefs -> /status spiegelt 300->120s, 99 -> 422, Formular
zeigt/lädt das Feld hinter der Standort-Checkbox.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-06-26 17:10:12 +02:00
commit 904f7db82c
7 changed files with 97 additions and 8 deletions

View file

@ -174,6 +174,9 @@ class AdminUserPrefsUpdate(BaseModel):
emergency_contacts: str | None = None # Kontakt-E-Mail(s), CSV
emergency_phones: str | None = None # Kontakt-Telefon(e), CSV
location_consent: bool | None = None # Standort im Notfall mitsenden?
# Alarmpause pro Nutzer (ganze Minuten, als String). Leer = globaler Standard,
# "0" = aus. Präzedenz: dieser Wert > globale Einstellung > 5.
emergency_cooldown_minutes: str | None = None
@field_validator("postal_code")
@classmethod
@ -210,6 +213,18 @@ class AdminUserPrefsUpdate(BaseModel):
raise ValueError("Kontakt-Telefon(e): ungültige Nummer")
return v
@field_validator("emergency_cooldown_minutes")
@classmethod
def _check_cooldown(cls, v):
if v is None or str(v).strip() == "":
return v # leer = globaler Standard (erben)
s = str(v).strip()
if not s.isdigit():
raise ValueError("Pause: nur ganze Minuten (060)")
if int(s) > 60:
raise ValueError("Pause: höchstens 60 Minuten")
return s
class UserAdminUpdate(BaseModel):
"""Admin schaltet Admin-Rechte für einen Nutzer ein/aus."""