Speicherleiste ersatzlos gestrichen, Felder speichern beim Verlassen

Auf Entscheidung des Eigentuemers nach der HIG-Pruefung. Gegen die Leiste
sprach viererlei: iOS kennt kein solches Muster auf einer geschobenen
Einstellungsseite; sie schwebte ueber der Tab-Leiste und deckte einen Knopf
der Kachel dahinter zu (gemessen); sie war die Ursache des Datenverlusts bei
der Erstzulassung; und das Panel hatte sie nie - dort schreibt jede Aenderung
sofort ins Profil.

Jetzt speichern Textfelder beim Verlassen, Auswahlfelder sofort. Die Meldung
"Gespeichert." entfaellt mit. Der Regler "Fahrt beenden" behaelt seinen
eigenen Bestaetigungsschritt - ihn bei jedem Zwischenschritt zu sichern hiesse
ein Dutzend Schreibvorgaenge je Bedienung.

Mit echten Eingabeereignissen am laufenden System geprueft: getippt, ins
naechste Feld geklickt, Wert steht in fahrzeugprofil.json; danach der
urspruengliche Wert wiederhergestellt. 299 Tests gruen, 0 Tracebacks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 12:42:50 +02:00
parent b22a0eca5b
commit 71403efafe
5 changed files with 80 additions and 33 deletions
+30 -31
View File
@@ -80,7 +80,6 @@ export function Einstellungen({
Eigentuemers, 04.09.2026). `null` heisst: nichts angefasst. */
const [pausenzeitEntwurf, setzePausenzeitEntwurf] = useState<number | null>(null)
const [laeuft, setzeLaeuft] = useState(false)
const [gespeichert, setzeGespeichert] = useState(false)
const [importOffen, setzeImportOffen] = useState(false)
/* Abfrage "bis wann gilt der Rabatt", die das Einschalten des SmartDeals
ausloest (sdpopup im Panel). */
@@ -236,18 +235,30 @@ export function Einstellungen({
eigenen Entwurf und uebergibt ihn erst beim Druck auf Speichern. Ohne den
Umweg wuerde `entwurf` im selben Rendern noch den alten Wert tragen -
setState wirkt erst beim naechsten Durchlauf. */
/* `erstzulassung` fuehrt einen EIGENEN Entwurf, unabhaengig von
`entwurf` - es gehoert zum Fahrzeug, nicht zu den Einstellungen. Diese
Bedingung hat es dabei vergessen: wer nur die Erstzulassung aenderte,
kam hier nie vorbei, und weiter unten erschien auch die Speicherleiste
nicht (die haengt ebenfalls allein an `entwurf`). Der eingetippte Wert
war damit gar nicht speicherbar und beim Verlassen der Seite weg -
gemeldet vom Eigentuemer am 05.09.2026. Das Panel speichert an dieser
Stelle sofort bei jeder Aenderung und war deshalb nie betroffen. */
/* Es gibt keine Speicherleiste mehr. Jedes Feld wirkt beim Verlassen,
Auswahlfelder sofort - wie im Panel, wo der `data-einrichten`-Handler
seit jeher bei jeder Aenderung ins Profil schreibt.
Ansage des Eigentuemers am 05.09.2026: "speichern: ersatzlos streichen".
Drei Gruende sprachen dafuer:
* iOS kennt keine dauerhafte Speichern/Verwerfen-Leiste auf einer
geschobenen Einstellungsseite. Apples Muster sind "wirkt sofort"
(Einstellungen-App) oder ein modales Blatt mit Abbrechen/Fertig in
der Navigationsleiste.
* Die Leiste schwebte ueber der Tab-Leiste und deckte dabei einen Knopf
der Kachel dahinter zu (gemessen 05.09.2026).
* Sie war die Ursache eines echten Datenverlusts: wer nur die
Erstzulassung aenderte, bekam gar keinen Speichern-Knopf zu sehen.
Eine Leiste, die eine Eingabe erst gueltig macht, kann sie auch
verschlucken.
`speichern()` bleibt fuer den Regler "Fahrt beenden": ein Schieberegler
bei jedem Zwischenschritt zu sichern hiesse ein Dutzend Schreibvorgaenge
je Bedienung, deshalb behaelt er seinen eigenen Bestaetigungsschritt. */
const speichern = async (zusatz?: Partial<EinstellungenWerte>) => {
if (!entwurf && !zusatz && erstzulassung === null) return
setzeLaeuft(true)
setzeGespeichert(false)
try {
await profilSpeichern({
einstellungen: { ...werte, ...(zusatz ?? {}) },
@@ -256,12 +267,15 @@ export function Einstellungen({
setzeEntwurf(null)
setzeErstzulassung(null)
setzePausenzeitEntwurf(null)
setzeGespeichert(true)
} finally {
setzeLaeuft(false)
}
}
/** Uebernimmt den aktuellen Stand des Bildschirms - beim Verlassen eines
Feldes oder sofort bei einem Auswahlfeld. Ohne Zusatz und ohne Knopf. */
const sichern = (zusatz?: Partial<EinstellungenWerte>) => void speichern(zusatz)
/* Zugaenge: je Dienst ein Entwurf im Eingabefeld. Der gespeicherte Token
kommt NIE zurueck (siehe zugaenge.py), das Feld startet deshalb immer leer
und zeigt als Platzhalter, ob schon einer hinterlegt ist. */
@@ -394,6 +408,7 @@ export function Einstellungen({
className="dm-eingabe dm-eingabe--fahrzeug"
value={werte.fahrzeugtitel}
onChange={(e) => aendern({ fahrzeugtitel: e.target.value })}
onBlur={() => sichern()}
/>
</Feld>
<Feld label="Kennzeichen">
@@ -401,6 +416,7 @@ export function Einstellungen({
className="dm-eingabe dm-eingabe--fahrzeug"
value={werte.kennzeichen}
onChange={(e) => aendern({ kennzeichen: e.target.value })}
onBlur={() => sichern()}
/>
</Feld>
<Feld label="Erstzulassung">
@@ -409,6 +425,7 @@ export function Einstellungen({
placeholder="TT.MM.JJJJ"
value={erstzulassungWert}
onChange={(e) => setzeErstzulassung(e.target.value)}
onBlur={() => sichern()}
/>
</Feld>
<Feld label="Ausführung" last>
@@ -416,6 +433,7 @@ export function Einstellungen({
className="dm-eingabe dm-eingabe--fahrzeug"
value={werte.ausfuehrung}
onChange={(e) => aendern({ ausfuehrung: e.target.value })}
onBlur={() => sichern()}
/>
</Feld>
{/* „Setup — Sensoren zuordnen" bleibt dem Panel vorbehalten: die
@@ -700,7 +718,7 @@ export function Einstellungen({
<select
className="dm-auswahl"
value={werte.backupIntervall}
onChange={(e) => aendern({ backupIntervall: e.target.value })}
onChange={(e) => sichern({ backupIntervall: e.target.value })}
>
<option value="aus">Aus</option>
<option value="taeglich">Täglich</option>
@@ -1031,25 +1049,6 @@ export function Einstellungen({
</>
)}
{gespeichert && <p className="dm-erfolg">Gespeichert.</p>}
{(entwurf || erstzulassung !== null) && (
<div className="dm-speicherleiste">
<ActionButton
onClick={() => {
setzeEntwurf(null)
// Sonst bliebe die Leiste nach "Verwerfen" stehen, weil der
// Erstzulassungs-Entwurf sie weiter offen haelt.
setzeErstzulassung(null)
}}
disabled={laeuft}
>
Verwerfen
</ActionButton>
<ActionButton variant="primary" onClick={() => void speichern()} disabled={laeuft}>
{laeuft ? "Speichere " : "Speichern"}
</ActionButton>
</div>
)}
</>
)
}