Phase 6: Datenanbindung, Ersteinrichtung und Offline-Anzeige

Die App spricht jetzt echt mit Home Assistant. Ein Datenkontext buendelt die
vier Datentoepfe, haengt sich an den WebSocket-Ereignisstrom und reicht
Verbindungszustand und Warteschlange nach aussen; die Screens kennen weder
REST noch WebSocket. Ersteinrichtung prueft die Zugangsdaten, bevor sie sie
speichert, und unterscheidet in der Fehlermeldung zwischen abgelehntem Token
und nicht erreichbarem Server - zwei Faelle, die voellig verschiedene
Reaktionen verlangen.

Zwei echte Fehler, die erst der Test gegen die laufende Instanz zutage
brachte:

1. Die Typen der Datenschicht deklarierten tank_prozent, sicher_abgestellt
   und sicherheit. Das Backend schreibt tankprozent, gesichert und
   sicherheitscheck - die Oberflaeche haette still ueberall undefined
   gelesen, ohne dass irgendetwas fehlgeschlagen waere.
2. Der Profil-Adapter reichte lebende Verweise ins Rohprofil durch. Ein
   Formular haette damit den Rohstand mitveraendert und die Zusage gebrochen,
   den vom Backend fortgeschriebenen Reifen-Kilometerstand nie zu
   ueberschreiben. Abschnitte werden jetzt kopiert.

Ausserdem zwei Parameter-Eigenschaften in der Datenschicht aufgeloest, weil
Node sie im Strip-Modus nicht uebersetzt und genau darueber die Rauchtests
laufen.

Belegt durch neun Pruefungen gegen die laufende Instanz (Lesen, Dienstaufruf,
vollstaendiger Warteschlangenumlauf, WebSocket-Anmeldung) und 21 Unit-Tests.
This commit is contained in:
Paul Nothaft
2026-08-11 11:02:44 +02:00
parent 5c0c2d1898
commit 0103508cea
13 changed files with 1358 additions and 25 deletions
+167
View File
@@ -0,0 +1,167 @@
/*
* Screen-übergreifende Bausteine: Eingabefelder, Hinweisleiste,
* Ersteinrichtung, Leerzustände, Ladeanzeige.
*/
/* -------------------------------------------------------- Eingabefelder */
.dm-eingabe,
.dm-auswahl {
font: inherit;
font-weight: 300;
font-size: 16px;
color: var(--fg);
background: none;
border: 0;
text-align: right;
outline: none;
min-width: 120px;
max-width: 60%;
padding: 0;
}
.dm-eingabe::placeholder {
color: var(--fg3);
}
.dm-auswahl {
text-align: right;
appearance: none;
cursor: pointer;
}
.dm-auswahl option {
color: var(--fg);
background: var(--tile);
}
.dm-notiz {
font: inherit;
font-weight: 300;
font-size: 16px;
color: var(--fg);
background: var(--tile-2);
border: 0;
border-radius: var(--r-func);
padding: 10px 12px;
width: 100%;
min-height: 88px;
resize: vertical;
outline: none;
}
/* --------------------------------------------------------- Hinweisleiste */
.dm-hinweis {
display: flex;
align-items: center;
gap: var(--sp-2);
padding: 9px var(--sp-5);
padding-top: calc(env(safe-area-inset-top, 0px) + 9px);
font-size: 12.5px;
font-weight: 300;
color: var(--fg);
background: var(--tile-2);
border-bottom: 1px solid var(--line);
}
.dm-hinweis--offline {
color: var(--warn);
}
.dm-hinweis svg {
flex: none;
}
/* -------------------------------------------------------- Ersteinrichtung */
.dm-einrichtung {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 32px;
overflow-y: auto;
}
.dm-einrichtung__karte {
width: 100%;
max-width: 420px;
}
.dm-einrichtung__titel {
font-size: 28px;
font-weight: 300;
letter-spacing: -0.02em;
margin: 10px 0 12px;
}
.dm-einrichtung__text {
font-size: 14px;
font-weight: 300;
line-height: 1.55;
color: var(--fg2);
margin: 0 0 22px;
text-wrap: pretty;
}
.dm-einrichtung__fehler {
font-size: 13px;
font-weight: 300;
line-height: 1.5;
color: var(--bad);
margin: 14px 0 0;
}
.dm-einrichtung__knoepfe {
display: flex;
flex-direction: column;
gap: 10px;
margin-top: var(--sp-4);
}
.dm-einrichtung__hinweis {
font-size: 11.5px;
font-weight: 300;
line-height: 1.5;
color: var(--fg3);
margin: 18px 0 0;
}
/* ------------------------------------------------- Leer- und Ladezustände */
.dm-leer {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: var(--sp-2);
padding: 44px 24px;
color: var(--fg2);
}
.dm-leer__titel {
font-size: 16px;
font-weight: 300;
color: var(--fg);
}
.dm-leer__text {
font-size: 13.5px;
font-weight: 300;
line-height: 1.55;
max-width: 34ch;
margin: 0;
text-wrap: pretty;
}
.dm-laden {
display: flex;
align-items: center;
justify-content: center;
gap: var(--sp-2);
padding: 44px 24px;
color: var(--fg2);
font-size: 13.5px;
font-weight: 300;
}