App-weite Warnung bei ungespeicherten Aenderungen + Options-UI-Fix + Cache-Busting

1. Generischer Ja/Nein-Dialog bei ungespeicherten Aenderungen (kein Opt-in
   pro Seite noetig): initUnsavedChangesGuard() erkennt automatisch jedes
   Formular mit echten Eingabefeldern, das seit dem Laden der Seite
   veraendert wurde, und fragt beim Klick auf einen Link (z.B. Sidebar-
   Navigation), ob zuerst gespeichert werden soll -- eigener Dialog
   (confirmSaveDiscard) statt des bestehenden confirmAction (dort ist
   "Abbrechen" = Bleiben, hier gibt es bewusst nur die zwei angefragten
   Optionen: speichern oder verwerfen, beide verlassen die Seite).
   "Ja, speichern" sendet das Formular ganz normal ab (echter POST) und
   merkt sich das eigentliche Ziel in sessionStorage, um danach automatisch
   dorthin weiterzuleiten.

   Nachtrag noch in dieser Session gefixt: ein normales Speichern ueber den
   Seiten-eigenen Button loeste zusaetzlich die native Browser-Warnung
   (beforeunload) aus, obwohl die Aenderung bereits gespeichert wurde --
   navigatingAway-Flag wird jetzt bei jedem Submit eines beobachteten
   Formulars (eigener Button ODER eigener Ja/Nein-Dialog) gesetzt, die
   generische Browser-Warnung erscheint dadurch nur noch bei tatsaechlich
   ungefragter Navigation (Tab schliessen, Reload, Adresszeile). Live per
   Playwright in drei Szenarien verifiziert (direktes Speichern, Ja via
   eigenem Dialog, Nein via eigenem Dialog) -- kein Browser-Popup in den
   ersten beiden Faellen, korrektes Redirect zum urspruenglichen Ziel nach
   "Ja".

2. DHCP-Options-Dropdown (Reservierungs-Dialog) verschiebt ein neu
   ausgewaehltes Options-Feld jetzt direkt unter das Dropdown (in einen
   .poe-added-options-Container) statt es nur an seiner urspruenglichen
   Stelle in der bis zu 57 Eintraege langen Liste einzublenden -- lag dort
   oft ausserhalb des sichtbaren Modal-Ausschnitts und wirkte, als waere
   nichts passiert. Bereits gesetzte Overrides stehen von Anfang an im
   selben Container, weitere haengen sich beim Auswaehlen unten an.

3. Cache-Busting fuer app.js/style.css (asset_url()-Helper, haengt die
   Aenderungszeit der Datei als ?v=... an) -- ein im Browser gecachtes,
   veraltetes app.js konnte nach einem Update sonst den Eindruck erwecken,
   eine neue Funktion wuerde "nicht funktionieren", obwohl der Code auf dem
   Server laengst aktuell war.

Alle drei live auf der Test-VM deployt und per Playwright verifiziert,
keine Fehler im journalctl-Log. (Testartefakt aufgeraeumt: der echte
Host-Hostname wurde durch die Playwright-Tests versehentlich auf
"directsave-test"/"navsave-test" geaendert und wieder auf POETEST
zurueckgesetzt.)
This commit is contained in:
2026-08-11 16:39:03 +02:00
parent 1a0b55ed44
commit 255308f81b
6 changed files with 231 additions and 15 deletions
+50 -3
View File
@@ -298,9 +298,12 @@ der aktiv weiterentwickelte Nachfolger und bildet „globaler Wert, pro Client
Top-Level `option-data`, Overrides im `option-data` der jeweiligen
Kea-Reservierung, was Kea automatisch pro Client Vorrang geben lässt). Im
Reservierungs-Options-Dialog gilt dasselbe Prinzip: nur Options mit
gesetztem Override sind sichtbar, weitere kommen über ein Dropdown dazu
(das neu eingeblendete Feld wird automatisch in den sichtbaren Bereich
gescrollt und fokussiert).
gesetztem Override sind sichtbar, weitere kommen über ein Dropdown dazu.
Das ausgewählte Feld wird dabei direkt unter das Dropdown verschoben und
fokussiert, statt an seiner ursprünglichen Stelle irgendwo in der u.U.
langen Liste (bis zu 57 mögliche Options) zu erscheinen — sonst lag es
oft außerhalb des sichtbaren Modal-Ausschnitts und wirkte, als wäre nichts
passiert (live genau so reproduziert und behoben).
- **Reservierungen aus zwei Quellen**: automatisch für jedes aktive Gerät mit
gültiger MAC + IP (Client-Stammdaten dieser App) sowie zusätzlich **manuell
anlegbar** („Reservierung hinzufügen“, MAC/IP/Name) — für Geräte, die
@@ -459,6 +462,50 @@ erscheint es statt der Initialen sowohl in der Sidebar als auch im
Änderungslog neben dem Benutzernamen. Admins sehen auf derselben Seite
zusätzlich die Navbar-Reihenfolge (siehe unten).
## Warnung bei ungespeicherten Änderungen
Generischer, app-weiter Mechanismus (kein Opt-in pro Seite nötig): erkennt
automatisch, ob ein Formular mit echten Eingabefeldern (nicht nur
versteckten Aktions-Feldern wie bei Löschen-/Toggle-Buttons) seit dem Laden
der Seite verändert wurde, und fragt beim Verlassen (Klick auf einen Link,
z.B. in der Navigation) per eigenem **Ja/Nein-Dialog**, ob zuerst gespeichert
werden soll, statt Änderungen stillschweigend zu verwerfen:
- **„Ja, speichern“**: sendet das betroffene Formular ganz normal ab (echter
POST, kein AJAX-Nachbau) und merkt sich das eigentliche Navigationsziel in
`sessionStorage`, um nach dem Speichern automatisch dorthin
weiterzuleiten — der Link muss dafür nicht ein zweites Mal angeklickt
werden.
- **„Nein, verwerfen“**: verwirft die Änderungen und wechselt direkt zur
Zielseite.
- Klickt man stattdessen auf das eigene „Speichern“/„Anlegen“ der Seite
direkt (ohne über einen Link zu einer anderen Seite zu gehen), passiert
**nichts zusätzlich** — kein Dialog, kein Browser-Popup, es wird einfach
ganz normal gespeichert.
- Für Browser-eigene Navigation (Tab schließen, Neuladen, Adresszeile) gibt
es zusätzlich `beforeunload` — dort erlaubt der Browser aus
Sicherheitsgründen nur eine generische Warnung ohne eigene Buttons, kein
Ja/Nein/Speichern-Dialog. Sie wird bewusst unterdrückt, sobald die Seite
ohnehin schon bewusst verlassen wird (eigenes Speichern oder der
Ja/Nein-Dialog) — sie soll ausschließlich bei tatsächlich ungefragter
Navigation erscheinen, nicht zusätzlich bei einem ganz normalen Speichern
(live als genau dieses Doppel-Popup reproduziert und behoben).
- Ausnahmen über `data-no-unsaved-guard` auf Formular oder Link (z.B. das
Login-Formular — Zugangsdaten eintippen ist keine "Einstellung").
Live per Playwright auf mehreren Seiten (Systemeinstellungen, DHCP-Options,
Gruppen anlegen) verifiziert: eigener Dialog erscheint nur bei tatsächlich
ungespeicherten Änderungen, „Ja“ speichert und leitet danach automatisch
zum ursprünglichen Ziel weiter, „Nein“ verwirft und wechselt direkt, ein
normales Speichern über den Seiten-eigenen Button zeigt weder eigenen noch
Browser-Dialog.
Nebenbei wurde ein generisches Cache-Busting für `app.js`/`style.css`
ergänzt (`asset_url()`-Helper, hängt die Änderungszeit der Datei als
`?v=...` an) — vorher konnte ein bereits gecachtes, veraltetes `app.js` im
Browser nach einem Update scheinbar "nicht funktionierende" neue Funktionen
verursachen, obwohl der Code auf dem Server längst aktuell war.
## Navigation anpassen
Die Sidebar ist in drei aufklappbare Gruppen strukturiert: