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:
@@ -11854,3 +11854,51 @@ Gemessen in beiden: Farbe `rgb(52,199,89)` bzw. `rgb(48,209,88)` - das ist
|
||||
**Nachgezogen:** die Beschriftung heisst nur noch "Integration".
|
||||
"Home-Assistant-Integration" brach auf Telefonbreite in zwei Zeilen um; auf
|
||||
Entscheidung des Eigentuemers gekuerzt. Nachgemessen: einzeilig, 20px hoch.
|
||||
|
||||
## CY. Die Speicherleiste ist ersatzlos entfallen (2026.9.5.21)
|
||||
|
||||
Frage des Eigentuemers: *"entspricht Speichern/verwerfen anzeige bei Fahrzeug
|
||||
einrichten der Apple HIG?"* - Antwort: nur teilweise. Entscheidung danach:
|
||||
*"speichern: ersatzlos streichen"*.
|
||||
|
||||
### Was dagegen sprach
|
||||
|
||||
* **Das Muster.** iOS kennt keine dauerhafte Speichern/Verwerfen-Leiste auf
|
||||
einer *geschobenen* Einstellungsseite. Apples zwei Muster sind: Aenderungen
|
||||
wirken sofort (Einstellungen-App) oder das Bearbeiten laeuft in einem
|
||||
**modalen Blatt** mit Abbrechen oben links und Fertig oben rechts. Eine
|
||||
klebende Aktionsleiste unten ist ein Web-/Android-Idiom.
|
||||
* **Sie verdeckte Inhalt.** Gemessen: Leiste bei y 577-670, Tab-Leiste ab 700;
|
||||
`elementsFromPoint` direkt dahinter ein `BUTTON.ads-action--primary` in der
|
||||
Kachel. Dazu Seitengrund `rgb(242,242,247)` auf weisser Kachel - ein grauer
|
||||
Balken quer durch die Karte.
|
||||
* **Sie war die Ursache eines Datenverlusts** (Abschnitt CU): wer nur die
|
||||
Erstzulassung aenderte, bekam gar keinen Speichern-Knopf zu sehen. Eine
|
||||
Leiste, die eine Eingabe erst gueltig macht, kann sie auch verschlucken.
|
||||
* **Sie war eine Paritaetsabweichung.** Das Panel hat sie nie gehabt - dort
|
||||
schreibt der `data-einrichten`-Handler bei jeder Aenderung sofort ins Profil.
|
||||
|
||||
### Was jetzt gilt
|
||||
|
||||
Textfelder (Modell, Kennzeichen, Erstzulassung, Ausfuehrung) speichern **beim
|
||||
Verlassen des Feldes**, Auswahlfelder sofort. Genau wie im Panel. Die
|
||||
Erfolgsmeldung "Gespeichert." entfaellt mit - eine sofort wirkende Aenderung
|
||||
braucht keine Bestaetigung, und das Panel zeigt auch keine.
|
||||
|
||||
**`speichern()` bleibt fuer den Regler "Fahrt beenden".** Ein Schieberegler bei
|
||||
jedem Zwischenschritt zu sichern hiesse ein Dutzend Schreibvorgaenge je
|
||||
Bedienung; er behaelt deshalb seinen eigenen Bestaetigungsschritt mit
|
||||
Verwerfen/Speichern in der Kachel (`dm-speicherleiste--kachel`). Das ist die
|
||||
einzige verbliebene Stelle mit dieser Klasse.
|
||||
|
||||
### Zweimal am eigenen Testfehler vorbeigeschrammt
|
||||
|
||||
Der erste Nachweis schlug fehl - Wert nicht gespeichert. Ursache war nicht der
|
||||
Code, sondern mein Test: ein programmatisches `e.focus()` + Wertsetzer +
|
||||
`e.blur()` loest React's `onBlur` hier nicht verlaesslich aus. Mit echten
|
||||
Eingabeereignissen (Dreifachklick, Tippen, Klick ins naechste Feld) steht der
|
||||
Wert sofort in `fahrzeugprofil.json`.
|
||||
|
||||
Dasselbe Muster wie beim Koordinatenfehler in Abschnitt CS: **ein fehlgeschlagener
|
||||
synthetischer Test ist kein Fehlerbeweis.** Erst mit echten Ereignissen pruefen,
|
||||
dann urteilen.
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"version":"2026.9.5.20","sha256":"65eb2b8e3c4fafbc3ef3aeaeb2daca86c136a209067d3f779b5eb65f4cc637ec","bytes":324590,"gebaut":"2026-09-05T09:38:57Z"}
|
||||
{"version":"2026.9.5.21","sha256":"db8589425bb50d359c7711d845844f41285c48bc0cebd016c54e6b717cf39787","bytes":324552,"gebaut":"2026-09-05T10:40:06Z"}
|
||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"domain": "audi_dashboard",
|
||||
"name": "Audi Dashboard",
|
||||
"version": "2026.9.5.20",
|
||||
"version": "2026.9.5.21",
|
||||
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
||||
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
||||
"codeowners": [
|
||||
|
||||
Reference in New Issue
Block a user