diff --git a/OFFEN.md b/OFFEN.md index 51798e6..c3931cf 100644 --- a/OFFEN.md +++ b/OFFEN.md @@ -104,11 +104,14 @@ Beispielzahlen in der Statistik und ein überholter TODO-Kommentar. (Abschnitt DJ) ist jetzt auch im **Panel** live gesehen und gemessen - Übersicht und „Mein Audi", Tagmodus, Fassung 2026.9.6.9. -*Offen bleibt allein:* das Erstzulassungsdatum im Testcontainer steht -vorübergehend auf **01.03.2020**, damit der Fall überhaupt auftritt. Das -Original liegt unter `/config/audi_dashboard/fahrzeugprofil.json.ansicht` und -ist zurückzustellen, sobald der Eigentümer sagt, dass er fertig geschaut hat. -Der **Nachtmodus** ist nur gerechnet, nicht gesehen. +Das Erstzulassungsdatum im Testcontainer war dafür vorübergehend auf +01.03.2020 zurückdatiert und ist am 07.09.2026 auf **01.03.2025** +zurückgestellt; die Sicherung `fahrzeugprofil.json.ansicht` ist entfernt. + +*Offen bleibt allein:* der **Nachtmodus** ist nur gerechnet, nicht gesehen. + +Die Beschreibung zum Nachziehen in einem Spin-off steht in +`PORTIERUNG_UEBERSCHRITTEN.md`. ## 4. Gemeldet, aber nicht reproduzierbar diff --git a/PORTIERUNG_UEBERSCHRITTEN.md b/PORTIERUNG_UEBERSCHRITTEN.md new file mode 100644 index 0000000..9de13f6 --- /dev/null +++ b/PORTIERUNG_UEBERSCHRITTEN.md @@ -0,0 +1,238 @@ +# Überschrittene Servicetermine — Beschreibung zum Nachziehen + +Stand 07.09.2026, Fassung `2026.9.6.9`, Commit `33c2ffa` (und Vorläufer). +Gedacht für ein Spin-off dieser App: alles, was gebraucht wird, um dieselbe +Markierung dort noch einmal zu bauen — einschließlich der Gründe, damit die +Entscheidungen nicht neu erraten werden müssen. + +Betroffen sind **zwei Codebasen**, die nach der Paritätsregel immer denselben +funktionalen Stand haben: das Home-Assistant-Panel +(`custom_components/audi_dashboard/frontend/`) und die Companion-App +(`companion-app/src/`). Wer nur eine davon nachzieht, hat einen latenten Fehler. + +--- + +## 1. Was die Markierung leistet + +Ein Servicetermin, dessen Fälligkeitstag erreicht oder überschritten ist, wird +an drei Stellen hervorgehoben: + +| Ort | Darstellung | +|---|---| +| Service-Blatt | die betroffene Termingruppe blass gelb hinterlegt, Marke „überschritten" im Kopf | +| „Mein Audi", Service-Kachel | **nur die betroffene Zeile** blass gelb, Marke links vom Wert | +| Übersicht, Kachel „Nächster Service" | Kennzahl in kräftigem Orange, Marke rechts daneben | + +Zwei Randbedingungen, die der Eigentümer ausdrücklich gesetzt hat: + +* Auf der Übersicht darf die Marke die Kachel **nicht höher machen** — sie steht + deshalb in derselben Zeile wie die Kennzahl, nicht darunter. +* In „Mein Audi" wird **nicht die ganze Kachel** hinterlegt, sondern nur das + überschrittene Feld. Eine Zwischenfassung hatte die Kachel als Ganzes getönt; + das wurde zurückgewiesen. + +--- + +## 2. Die Fachregel: entschieden wird am Datum + +`terminUeberschritten(...kandidaten)` — im Panel in `audi-dashboard-app.js`, in +der App exportiert aus `src/daten/service.ts`. + +**Regel:** Nimm das **erste lesbare Datum** der übergebenen Kandidaten und melde +`true`, sobald es nicht mehr in der Zukunft liegt. Der Fälligkeitstag selbst +zählt mit. + +Drei Punkte, die beim Nachbau leicht falsch gemacht werden: + +1. **Nicht an den Restkilometern entscheiden.** Die Kilometerseite trägt je nach + Quelle ein anderes Vorzeichen: die Fahrzeugsensoren melden negativ, solange + noch Strecke bleibt (gemessen: `oil_change_distance` = −28600 bei Fälligkeit + 2028), und die Prognosefunktion klemmt ihre Restkilometer mit + `Math.max(0, …)`. Welches Vorzeichen ein überschrittener Fahrzeugwert trägt, + ist an den vorliegenden Daten **nicht beobachtbar**. Der km-Fall fällt ohnehin + unter die Datumsregel, weil die Prognose das Datum auf heute setzt, sobald die + Restkilometer auf 0 laufen. +2. **Das erste lesbare Datum nehmen, nicht das früheste.** Es ist genau das + Datum, das an der Stelle auch angezeigt wird — so kann die Markierung nicht + von dem abweichen, was daneben steht. +3. **Volle ISO-Zeitstempel mitlesen.** Die Fahrzeugmeldung liefert + `"2027-10-21T00:00:00+00:00"`, und sie ist bei Ölwechsel und Inspektion die + Hauptquelle. Der verankerte Textparser lehnt Zeitstempel ab; ohne einen + eigenen Zweig dafür gälte die Hauptquelle stillschweigend **nie** als + überschritten. Im Panel macht das `zeitstempelAlsDatum()` mit einem auf + `JJJJ-MM-TT` plus `T` verankerten Ausdruck; in der App kann `alsZeitpunkt()` + das bereits. Der Fehler wurde vom Test gefangen, nicht im Betrieb — er wäre + still geblieben. + +Bewusst eng gefasst statt einem allgemeinen `new Date()`: sonst deutete der +Rückfall auch Zeichenketten, die der Textparser gerade abgelehnt hat. + +--- + +## 3. Die Farben: jedes Element an seiner eigenen WCAG-Schwelle + +Das ist der Teil, der zwei Anläufe gebraucht hat, und der Grund dafür ist +übertragbar: **kräftiges Orange ist bei 4,5:1 nicht zu haben.** Apples +systemOrange `#FF9500` kommt auf Weiß auf 2,20:1; jedes Orange, das 4,5:1 +schafft, ist so dunkel, dass es als Braun gelesen wird. Zwei Anläufe mit einer +einzigen Textfarbe (`#A15800`, dann Apples `#C55300`) wurden beide als „braun +statt gelb" zurückgewiesen — zu Recht. + +Die Lösung ist nicht ein besserer Kompromisston, sondern **die Trennung der +Rollen**, jede an ihrer eigenen Schwelle: + +| Rolle | Größe | Schwelle | Tag | Nacht | +|---|---|---|---|---| +| `--warn` (Fläche, 12 % hinterlegt) | — | — | `#FF9F0A` | `#FFD60A` | +| `--warn-fig` (Kennzahl) | 32 px = Großtext | 3:1 | `#DD6000` | `#FF9F0A` | +| `--warn-marke-grund` (Pille) | — | — | `#C55300` | `#FFA056` | +| `--warn-marke-schrift` | 10,5 px = Kleintext | 4,5:1 | `#FFFFFF` | `#171B21` | +| `--warn-text` (Schrift auf hellem Grund) | klein | 4,5:1 | `#C55300` | `#FFA056` | + +Gemessen (WCAG 2.1, Hülle = 12 % `--warn` auf dem jeweiligen Kachelgrund): + +* `#DD6000` → 3,65:1 auf Weiß, 3,34:1 auf der Hülle (Schwelle 3:1) ✔ +* Weiß auf `#C55300` → 4,55:1 (Schwelle 4,5:1) ✔ +* `#FF9F0A` auf `#171B21` → 8,41:1 ✔ +* `#171B21` auf `#FFA056` → 8,57:1 ✔ + +**Der entscheidende Kniff bei der Marke:** keine dünne Schrift, sondern eine +**gefüllte Pille**. Derselbe Ton liest sich als Fläche eindeutig als Orange, +während er als 1 px starker Buchstabenstrich bräunlich wirkt. Weiße Schrift auf +`#C55300` erfüllt die 4,5:1 exakt so, wie es umgekehrt nicht ginge. + +Tokens liegen in `frontend/audi-dashboard-ios.css` (Panel) und +`design-system/src/tokens/tokens.css` (App) — beide Sätze müssen gleich sein. + +**Zur Frage HIG oder Audi CI:** die Fläche ist Apple (iOS-Palette, 2026-08-13 +übernommen); das Audi-CI-Gelb `#ffaa00` wurde damals abgelöst und liefert als +Schrift nur 1,91:1. Die Schriftwerte sind **gerechnet, nicht übernommen** — +Apples eigener Kontrastwert `#A16A00` liefert auf der 12 %-Hülle nur 4,19:1. +Nebenbefund: Apples HIG-Seite führt inzwischen die neue („unified") Palette; +einen einzelnen Wert daraus zu übernehmen wäre inkonsistent gewesen. + +--- + +## 4. CSS + +Gleiche Regeln in beiden Codebasen, nur andere Klassennamen +(Panel `audi-dashboard.css` / App `src/stile/screens.css`): + +| Panel | App | +|---|---| +| `.termingruppe.ueberschritten` | `.dm-termingruppe--ueberschritten` | +| `.row.ueberschritten` | `.dm-wertzeile--ueberschritten` | +| `.ueberschritten-marke` | `.dm-ueberschritten-marke` | +| `.serviceblock.ueberschritten .fig` | `.dm-serviceblock--ueberschritten .ads-fig` | +| `.fig-zeile` | `.dm-servicefig` | + +Die Tönung: + +```css +background: color-mix(in srgb, var(--warn) 12%, transparent); +border-radius: var(--r-tile); +padding-left: 12px; padding-right: 12px; +margin-left: -12px; margin-right: -12px; +``` + +Die negativen Außenabstände sind der Punkt: sie lassen die Tönung bis an den +Kachelrand laufen, obwohl die Zeile selbst im Innenabstand der Kachel sitzt. +Prüfbar am gemessenen Breitenunterschied — getönte Zeile 752 px gegenüber 728 px +der übrigen, also genau 2 × 12 px. + +Die Pille: + +```css +font-size: 10.5px; font-weight: 600; +color: var(--warn-marke-schrift); background: var(--warn-marke-grund); +border-radius: var(--r-pill); padding: 3px 8px; +white-space: nowrap; flex: 0 0 auto; +display: inline-block; vertical-align: middle; +``` + +Platzierung: `.fig-zeile .ueberschritten-marke { margin-left: 10px }` (Übersicht, +rechts der Kennzahl in derselben Zeile) und +`.row .ueberschritten-marke { margin-right: 8px }` („Mein Audi", links des Werts). + +Dazu `.termingruppe.ueberschritten, .termingruppe.ueberschritten + .termingruppe +{ border-top: 0 }` — sonst schneidet die Trennlinie durch die getönte Fläche. + +--- + +## 5. Markup + +**Übersicht** — die Marke muss in dieselbe Zeile wie die Kennzahl, sonst wächst +die Kachel. Die Zeile wird deshalb **immer** gebaut, auch ohne Marke: + +```js +const figMarkup = `