diff --git a/AGENTS.md b/AGENTS.md
index 407da29..1ed7691 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,6 +1,6 @@
# AGENTS.md — Project state, review findings, open items, and working rules
-**Last updated: 2026-08-17** (merged the `umsetzung-datametric360` branch — companion app phases
+**Last updated: 2026-08-18** (merged the `umsetzung-datametric360` branch — companion app phases
1–10 done, see `UMSETZUNGSPLAN.md`; 2026-08-13: cleaned up remaining EU Data Act residue, fixed
oversized toggle switches, and fixed a desktop-layout audit (settings button / rings logo / popups
overflowing past the capped content column) — see `DESIGN_AUDIT_2026-08-13.md`; 2026-08-16: fixed
@@ -47,7 +47,12 @@ where the image placeholder's grey background had turned invisible-white; separa
panel) was brought back in sync with everything the panel gained since 2026-08-11 - see the
"Gap found while auditing..." entry near the end of section C; finally, the Übersicht's
Kilometerstand/Service tiles were redesigned after the owner asked how Apple HIG and Audi CI would
-solve them - both answer the same way, and the merged "9,0 tkm/ 04/27" number field is gone).
+solve them - both answer the same way, and the merged "9,0 tkm/ 04/27" number field is gone;
+2026-08-18: the owner rejected a follow-up mockup for the "Mein Audi" service preview and
+"Anstehende Termine" boxes ("Lost information") - no code from that proposal was built; instead the
+existing Nächster-Service box on the Übersicht had its "bis zum/zur ..." wording replaced by the
+real Audi CI oil-change/inspection icons (user-supplied SVGs) placed left of the figure, in both
+codebases, per the owner's explicit request).
This file is the entry point for every new agent
session: what this repo is, what is finished, what is missing, and how to work here. Detail lives in
the linked documents — this file points, it does not duplicate.
@@ -1476,6 +1481,133 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed
{opacity:.55}` als Tipp-Rückmeldung ergänzt, da der übliche `.tilebtn`-Hintergrundwechsel
hier nicht geht), synchron in `installationspaket/`. Entwurf und Begründung als Artefakt:
„Zwei Kacheln, drei Antworten“.
+- [x] Nächster-Service-Box: CI-Icon statt "bis zum/zur ..."-Wortlaut (2026-08-18) — der Nutzer
+ lehnte zunächst einen Folge-Entwurf ab, der dieselbe HIG/CI-Systematik auf die "Mein Audi"-
+ Service-Vorschau und "Anstehende Termine" auf der Service-Seite anwenden sollte ("I don't like
+ it" → per Rückfrage: "Lost information", also zu viel weggelassen) — dieser Vorschlag wurde
+ **nicht** umgesetzt, keine Codeänderung daraus übernommen. Stattdessen ein präziser, neuer
+ Wunsch für die bereits bestehende Nächster-Service-Box auf der Übersicht: das Wort
+ ("bis zum Ölwechsel"/"bis zur Inspektion") vor der Kennzahl entfällt zugunsten der echten Audi-
+ CI-Icons `inspection-l/-s`/`oil-change-l/-s` (vom Nutzer als SVG geliefert, aus
+ `C:\Users\tobia\Downloads\Icons 2026-08-09 13_04_03\`, verbatim übernommen wie die übrigen
+ CI-Icons dieses Projekts — l im 48er-, s im 24er-Grid), links der Zahl platziert; auf
+ Nutzerwunsch anschließend nochmal um 30% vergrößert (24px → 31px). Umgesetzt in beiden
+ Codebasen: Panel bekam `CI.oilChangeL/-S`/`CI.inspectionL/-S` (neben den bestehenden CI-
+ Icon-Konstanten) und ein neues `.fig-zeile`-Wrapper-Div (`display:flex;align-items:center`)
+ um Icon + `.fig` in `vHome()`'s Serviceblock; die Nebenzeile zeigt jetzt nur noch
+ `vsl. DATUM`. Companion-app bekam die identischen Pfade als neue `SymbolOelwechsel`/
+ `SymbolInspektion`-Komponenten in `symbole.tsx` (eigener `RahmenFlaeche`-Wrapper, da diese
+ zwei — anders als die generischen Tab-Icons dort — gefüllte Flächen-Icons sind, kein
+ stroke-Wrapper) und eine neue `.dm-servicefig`-CSS-Klasse in `screens.css`; `Uebersicht.tsx`
+ wählt das Symbol über eine kleine `SERVICE_SYMBOL`-Tabelle nach `service.art`. **Wichtig:** die
+ Icons blieben bewusst außerhalb von `design-system/` (Lizenzregel oben — echte Audi-CI-Assets
+ dürfen nur in Panel/companion-app, nicht in das markenfreie `@audi-dash/ui`) — die Verdrahtung
+ liegt deshalb direkt im jeweiligen Screen, nicht in der gemeinsamen `Fig`-Komponente. Die
+ Hauptuntersuchung hat kein eigenes Icon (kein Restkilometer-Ziel, `restKm` ist dort immer
+ `null`) und behält `bisText()` als einzige Kennzeichnung — das deckt sich in beiden Codebasen
+ genau mit der Bedingung, unter der ein Icon überhaupt existiert. `npm run typecheck`/
+ `npm run test` (100/100)/`npm run build` sauber. Live im Docker-Testcontainer geprüft (Version
+ `1787012001`, synchron in `installationspaket/`): Ölwechsel-Icon rendert links der "8.999 km"-
+ Kennzahl, Unterzeile zeigt nur noch "vsl. 18.04.2027", keine neuen Konsolenfehler.
+- [x] Nächster-Service-Box: Hauptuntersuchung-Datum in Zahlengröße + Art-Name statt "bis zur ..."
+ (2026-08-18) — Folgewunsch zum Icon-Umbau: das Datum im Zahlenfeld war für die
+ Hauptuntersuchung (kein Restkilometer-Ziel) bisher kleiner gesetzt (30px statt 40px), extra
+ damit lange Daten wie "15.04.2027" auf 375px nicht umbrechen; der Nutzer wollte stattdessen
+ dieselbe 40px-Größe wie bei Ölwechsel/Inspektion, plus darunter statt "bis zur
+ Hauptuntersuchung" nur noch das Wort "Hauptuntersuchung" selbst. Vor der Umsetzung am
+ laufenden Panel nachgemessen (nicht angenommen): "15.04.2027" misst bei 40px/Audi Type Wide
+ 182px, der Kachel-Container hat 322px — die ursprüngliche Wrap-Sorge, die zur 30px-Regel
+ geführt hatte, bestätigt sich bei den tatsächlichen Container-Maßen nicht. `zahlGroesse` ist
+ jetzt fest 40 (Panel `vHome()` und companion-app `Uebersicht.tsx`, `Fig size={40}`
+ unabhängig von `restKm`). Da `bisText()`/`ART_BIS` (Panel) und `bisText()`/`BIS_TEXT`
+ (companion-app `service.ts`) dadurch ihre einzige Aufrufstelle verloren, wurden beide als
+ Orphans entfernt (inkl. des zugehörigen companion-app-Tests in `service.test.ts` — von 100 auf
+ 99 Tests, kein Funktionsverlust, nur der jetzt toten Funktion beraubt). Live im
+ Docker-Testcontainer per DOM-Patch simuliert (Testfixture hat aktuell Ölwechsel als
+ nächstgelegenen Termin, nicht die Hauptuntersuchung) und bei 390px Breite screenshotet:
+ "06.08.2028" einzeilig bei 40px, "Hauptuntersuchung" darunter, kein Icon (wie vorgesehen — es
+ gibt kein CI-Icon für die Hauptuntersuchung). `npm run typecheck`/`npm run test`
+ (99/99)/`npm run build` in companion-app sauber. Deployed als Panel-Version `1787012002`,
+ synchron in `installationspaket/`.
+- [x] "Anstehende Termine"-Box (Service-Seite) umgebaut: drei sichtbar getrennte Blöcke
+ (2026-08-18) — auf Wunsch des Nutzers ein neuer Entwurf (Apple HIG + Audi CI, wie beim
+ Übersicht-Umbau), mit expliziten Vorgaben: Herstellervorgabe bleibt für Ölwechsel und
+ Inspektion möglich, "voraussichtlich ..." bleibt für beide erhalten, alle drei Arten werden
+ sichtbar getrennt, die Hauptuntersuchung bekommt bewusst weder Herstellervorgabe noch
+ Prognosezeile. Durchlief drei Mockup-Runden als Artefakt ("Drei Termine, drei Blöcke") vor der
+ Umsetzung: **Runde 1** abgelehnt ("Major Information is missing"). **Runde 2** ergänzte ein
+ vollständiges Feld-Inventar (Tabelle: jedes Element aus `vService()` mit Fundstelle im
+ Vorschlag), integrierte den vereinbarten Werkstatttermin in den Hauptvergleich statt als
+ Anhang, zeigte den "kein Eintrag im Servicebuch"-Zustand. **Runde 3** (auf drei
+ Inline-Kommentare des Nutzers im Artefakt): Ölwechsel-Icon von 20px auf 26px angehoben (füllt
+ in seinem 24×24-Raster nur eine 24×10-Fläche statt der fast vollen Höhe des
+ Inspektion-Icons — per `getBBox()` nachgemessen, kein exakter Ausgleich möglich ohne das Icon
+ unverhältnismäßig breit zu machen), Herstellervorgabe-Zeile auch am Ölwechsel-Beispiel gezeigt
+ (vorher nur bei Inspektion), der Hauptuntersuchungs-Block von einem Icon-Platzhalter-Abstand
+ auf echt linksbündig geändert. Danach vom Nutzer freigegeben ("Ok, implement it").
+ **Umsetzung, beide Codebasen:**
+ Panel (`vService()`, `audi-dashboard-app.js`): `
` durch drei
+ `
` ersetzt (Kopfzeile mit Icon + Art-Name + optionalem
+ `.tg-chip` für einen vereinbarten Werkstatttermin, optionale `.tg-vorgabe`-Zeile, `.tg-wert`
+ als 22px/Gewicht-300-Zahl, optionale `.tg-prognose`-Fußzeile) — Auswahllogik für Wert/
+ Fußzeile/Herstellervorgabe unverändert aus dem bisherigen Code übernommen, nur die Markup-
+ Struktur geändert. `ART_ICON`/`ART_ICON_GROESSE` (26px Ölwechsel, 20px Inspektion) von
+ `vHome()` auf Modulebene gehoben, da jetzt von zwei Stellen genutzt — `vHome()`s eigene
+ Icon-Größe (31px, beide gleich) bewusst unverändert gelassen, das ist eine offene, dem Nutzer
+ gestellte Rückfrage. Der Werkstatt-Chip nutzt `var(--red)` nur als Text-/Rahmenfarbe, keine
+ Füllung — deckt sich mit der bereits bestehenden Projektregel "Rot ist Akzent, nie Fläche"
+ (siehe iOS-Overlay-Fix vom 2026-08-13). Deployed als Version `1787013000`, synchron in
+ `installationspaket/`, live im Docker-Testcontainer per direkter DOM-Abfrage geprüft (JS statt
+ Screenshot, da die Browser-Pane in dieser Sitzung zeitweise nicht kompositierte): alle drei
+ Blöcke rendern mit korrektem Text, Icons in 26px/20px, Hauptuntersuchung ohne Icon, keine
+ neuen Konsolenfehler.
+ companion-app (`Service.tsx`): dieselbe Drei-Blöcke-Struktur, aber eine **echte
+ Neustrukturierung, kein 1:1-Kopieren** — die bisherige Kachel hieß "Nächster Ölwechsel" und
+ mischte Ölwechsel-, Inspektions- und Hauptuntersuchungs-Zeilen fachfremd in einer gemeinsamen
+ `Werteliste`; jetzt "Anstehende Termine" wie im Panel, mit denselben drei `.dm-termingruppe`-
+ Blöcken. Neue `SymbolOelwechsel`/`SymbolInspektion`-Komponenten in `symbole.tsx` (bereits für
+ die Übersicht angelegt, hier wiederverwendet). Neuer Export `letzterInspektion()` in
+ `service.ts` (Gegenstück zu `letzterOelwechsel()`) für den "kein Eintrag"-Zustand der
+ Inspektion. **Zwei bewusste, dokumentierte Vereinfachungen gegenüber dem Panel** (vorbestehende
+ Lücken, nicht durch diese Änderung verursacht): (1) companion-app hat keine "Termin
+ vereinbaren"-Funktion und keine `vereinbart`-Datenfelder für Service-Termine — der Werkstatt-
+ Chip entfällt deshalb ersatzlos, es gibt nichts anzuzeigen. (2) companion-app hat kein
+ Gegenstück zu `intervalle()`/dem generischen Herstellerintervall-Fallback des Panels; die
+ Herstellervorgabe-Zeile wird stattdessen direkt aus `fahrzeug.oel.modus` abgeleitet (Ölwechsel:
+ nur bei `modus === "hersteller"`, Inspektion: immer, da sie kein einstellbares Intervall hat —
+ deckt sich inhaltlich mit dem Panel-Verhalten, auch ohne dieselbe Funktion zu teilen). Beide
+ Punkte sind funktional gleichwertig zum bisherigen companion-app-Stand, nicht neu eingeführte
+ Lücken. `npm run typecheck` sauber, `npm run test` 99/99, `npm run build` erfolgreich; die
+ bestehenden "Alle Seiten rendern mit Beispieldaten"-Tests rendern die Service-Seite weiterhin
+ ohne Absturz. Kein Live-Screenshot in dieser Sitzung (bräuchte einen laufenden Dev-Server samt
+ Backend-Anbindung) — strukturell verifiziert über Typecheck/Tests/Build.
+ **Zwei Nachbesserungen direkt nach der ersten Live-Prüfung, beide vom Nutzer gemeldet:**
+ (1) "Herstellervorgabe" fehlte beim Ölwechsel, obwohl der angezeigte Wert (29.100 km /
+ 25.07.2028) vom Fahrzeug selbst gemeldet wird (`hatMeldung`/`fahrzeug.oelwechselFaelligTs`).
+ Root Cause: die Zeile war bisher ausschließlich an `iv.hersteller` (Panel) bzw.
+ `fahrzeug.oel.modus === "hersteller"` (companion-app) gekoppelt — dem *App-eigenen*
+ Intervall-Modus aus "Einrichten". Der Bordcomputer kennt diesen App-Modus aber gar nicht;
+ eine Fahrzeugmeldung stammt immer aus dem werkseigenen Wartungsprogramm und ist damit per
+ Definition Herstellervorgabe, unabhängig vom gewählten App-Modus. Das Testprofil hat den
+ Ölwechsel aktuell auf `modus: "eigen"` stehen (10.000 km/12 Monate statt 30.000 km/24
+ Monate) — das war korrektes Verhalten der alten Bedingung, nur die Bedingung selbst war zu
+ eng. Fix: `herstellervorgabe = hatMeldung || iv.hersteller` (Panel) bzw.
+ `oelHatMeldung || fahrzeug.oel.modus === "hersteller"` (companion-app) — dieselbe alte
+ Bedingung bleibt für den Fall ohne Fahrzeugmeldung erhalten (App-eigener Fallback auf das
+ Herstellerintervall), kommt jetzt aber zusätzlich bei einer echten Fahrzeugmeldung zum
+ Tragen. (2) Die Prognose-Fußzeile zeigte "8.999 km, voraussichtlich am 18.04.2027" auch dann,
+ wenn in "Einrichten" tatsächlich Herstellervorgabe gewählt ist — dort rechnet die eigene
+ Prognose ohnehin schon mit dem Herstellerintervall, die Restkilometerzahl wäre also nur eine
+ zweite, verwirrende km-Angabe neben dem Fahrzeug-Wert direkt darüber. Fix: die "X km,"-
+ Vorsilbe entfällt, sobald `CAR.oel.modus`/`fahrzeug.oel.modus === "hersteller"` ist — nur beim
+ eigenen (kürzeren) Intervall bleibt sie stehen, weil sie dort echte Zusatzinformation ist
+ (matcht damit exakt, wie die Inspektion-Fußzeile schon immer aussah, da sie kein einstellbares
+ Intervall hat). Mit dem Testprofil (`modus: "eigen"`) bleibt die Kilometerzahl also weiterhin
+ sichtbar — korrekt, das ist genau der Fall, für den sie gedacht ist; umschalten auf
+ Herstellervorgabe in "Einrichten" würde sie ausblenden. Beide Fixes deployed als Panel-Version
+ `1787013001`, synchron in `installationspaket/`, live per DOM-Abfrage geprüft: Ölwechsel-Block
+ zeigt jetzt "Herstellervorgabe", keine neuen Konsolenfehler. companion-app: `npm run typecheck`
+ sauber, `npm run test` 99/99, `npm run build` erfolgreich.
- [x] **Parity rule eingeführt (2026-08-17):** der Nutzer stellte klar, dass Panel und
`companion-app/` immer denselben funktionalen Stand haben sollen — auch wenn eine Anfrage nur
das Panel nennt, das ist reiner Testkomfort (Docker-Instanz), keine Scope-Entscheidung. Regel
diff --git a/companion-app/src/daten/service.test.ts b/companion-app/src/daten/service.test.ts
index 96f9847..95fca7c 100644
--- a/companion-app/src/daten/service.test.ts
+++ b/companion-app/src/daten/service.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest"
-import { bisText, inspektionPrognose, letzterOelwechsel, naechsterService, oelwechselPrognose } from "./service"
+import { inspektionPrognose, letzterOelwechsel, naechsterService, oelwechselPrognose } from "./service"
import type { Fahrzeug } from "./profilAdapter"
function fahrzeug(odo: number, modus = "hersteller"): Fahrzeug {
@@ -142,10 +142,4 @@ describe("naechsterService", () => {
it("liefert nichts ohne Servicebuch und ohne gepflegte Hauptuntersuchung", () => {
expect(naechsterService(fahrzeug(46000), [], jetzt)).toBeNull()
})
-
- it("bisText nennt den richtigen Artikel je Art", () => {
- expect(bisText("Ölwechsel")).toBe("bis zum Ölwechsel")
- expect(bisText("Inspektion")).toBe("bis zur Inspektion")
- expect(bisText("Hauptuntersuchung")).toBe("bis zur Hauptuntersuchung")
- })
})
diff --git a/companion-app/src/daten/service.ts b/companion-app/src/daten/service.ts
index 56e2c46..2d88f16 100644
--- a/companion-app/src/daten/service.ts
+++ b/companion-app/src/daten/service.ts
@@ -51,6 +51,11 @@ export function letzterOelwechsel(buch: readonly Buchhaltung[]): Buchhaltung | n
return letzterEintrag(buch, /öl|oel/i)
}
+/** Der jüngste Servicebucheintrag, dessen Art auf eine Inspektion hindeutet. */
+export function letzterInspektion(buch: readonly Buchhaltung[]): Buchhaltung | null {
+ return letzterEintrag(buch, /inspektion/i)
+}
+
/** Gemeinsamer Kern für Ölwechsel- und Inspektionsprognose - beide rechnen
identisch, nur mit anderem Intervall und anderer Suche im Servicebuch. */
function servicePrognose(
@@ -134,19 +139,6 @@ export interface NaechsterService {
datum: string
}
-/* "der Ölwechsel", aber "die Inspektion"/"die Hauptuntersuchung" - ohne diese
- Zuordnung stünde auf der Übersicht "bis zum Inspektion". Gleiche Tabelle wie
- ART_BIS im Panel (audi-dashboard-app.js). */
-const BIS_TEXT: Record = {
- "Ölwechsel": "bis zum Ölwechsel",
- "Inspektion": "bis zur Inspektion",
- "Hauptuntersuchung": "bis zur Hauptuntersuchung",
-}
-
-export function bisText(art: Serviceart): string {
- return BIS_TEXT[art]
-}
-
/**
* Der zeitlich nächste anstehende Service über alle drei Arten hinweg —
* Gegenstück zu `naechsterTermin()` im Panel.
diff --git a/companion-app/src/screens/Service.tsx b/companion-app/src/screens/Service.tsx
index fd8f696..5c06c6c 100644
--- a/companion-app/src/screens/Service.tsx
+++ b/companion-app/src/screens/Service.tsx
@@ -13,9 +13,10 @@ import { useState } from "react"
import { ActionButton, Feld, Tile } from "@audi-dash/ui"
import { useDaten } from "../daten/DatenKontext"
-import { inspektionPrognose, oelwechselPrognose } from "../daten/service"
+import { inspektionPrognose, letzterInspektion, letzterOelwechsel, oelwechselPrognose } from "../daten/service"
import { datum, de, isoTag } from "../format"
import type { SeitenName } from "../navigation"
+import { SymbolInspektion, SymbolOelwechsel } from "../symbole"
import { Leerzustand, NaviKachel, Wertzeile, Werteliste, bestaetigen } from "./bausteine"
import { kalenderDateiLaden } from "./kalender"
@@ -44,53 +45,106 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
const prognose = oelwechselPrognose(fahrzeug, buch)
const inspektionsprognose = inspektionPrognose(fahrzeug, buch)
+ // Ölwechsel und Inspektion je einzeln, statt einer gemeinsamen Wertzeilen-
+ // Liste unter "Nächster Ölwechsel" (die Inspektion/Hauptuntersuchung dort
+ // fachfremd mitschleppte) - deckungsgleich mit vService() im Panel: drei
+ // sichtbar getrennte Blöcke, jeder mit eigenem Icon, optionaler
+ // Herstellervorgabe-Zeile und optionaler Prognose-Fußzeile.
+ const oelHatEintrag = letzterOelwechsel(buch) != null
+ const oelHatMeldung = fahrzeug.oelwechselFaelligTs != null || fahrzeug.oelwechselFaelligKm != null
+ const oelWert = oelHatMeldung
+ ? [
+ fahrzeug.oelwechselFaelligKm != null ? `${de(Math.abs(fahrzeug.oelwechselFaelligKm))} km` : null,
+ fahrzeug.oelwechselFaelligTs ? datum(fahrzeug.oelwechselFaelligTs) : null,
+ ]
+ .filter(Boolean)
+ .join(" / ")
+ : prognose
+ ? `${de(prognose.restKm)} km · ${datum(prognose.datum)}`
+ : null
+ // Bei Herstellervorgabe rechnet die eigene Prognose ohnehin schon mit dem
+ // Herstellerintervall - die Restkilometerzahl daneben wäre nur eine zweite,
+ // verwirrende km-Angabe neben dem Fahrzeug-Wert oben. Nur beim eigenen
+ // (kürzeren) Intervall ist sie eine echte Zusatzinformation.
+ const oelFussnote = oelHatMeldung && prognose
+ ? fahrzeug.oel.modus === "hersteller"
+ ? `voraussichtlich am ${datum(prognose.datum)}`
+ : `${de(prognose.restKm)} km, voraussichtlich am ${datum(prognose.datum)}`
+ : null
+ // Der Ölwechsel folgt in "Einrichten" wahlweise der Herstellervorgabe oder
+ // einem kürzeren eigenen Intervall (fahrzeug.oel.modus) - die Inspektion hat
+ // kein einstellbares Intervall, rechnet also immer mit der Herstellervorgabe
+ // (siehe inspektionPrognose()'s eigener Kommentar dazu). Eine Fahrzeugmeldung
+ // stammt aber immer aus dem werkseigenen Wartungsprogramm des Bordcomputers -
+ // der kennt das eigene App-Intervall gar nicht -, zählt also unabhängig vom
+ // gewählten Modus als Herstellervorgabe.
+ const oelHerstellervorgabe = oelHatMeldung || fahrzeug.oel.modus === "hersteller"
+
+ const inspHatEintrag = letzterInspektion(buch) != null
+ const inspHatMeldung = fahrzeug.inspektionFaelligTs != null || fahrzeug.inspektionFaelligKm != null
+ const inspWert = inspHatMeldung
+ ? [
+ fahrzeug.inspektionFaelligKm != null ? `${de(Math.abs(fahrzeug.inspektionFaelligKm))} km` : null,
+ fahrzeug.inspektionFaelligTs ? datum(fahrzeug.inspektionFaelligTs) : null,
+ ]
+ .filter(Boolean)
+ .join(" / ")
+ : inspektionsprognose
+ ? datum(inspektionsprognose.datum)
+ : null
+ const inspFussnote = inspektionsprognose ? `voraussichtlich am ${datum(inspektionsprognose.datum)}` : null
+
+ const huDatum = fahrzeug.hu ? new Date(fahrzeug.hu) : null
+ const huGueltig = !!huDatum && !Number.isNaN(huDatum.getTime())
+
return (
<>
- Nächster Ölwechsel
- Anstehende Termine
+
+
- Die eigene Prognose rechnet ab dem letzten Ölwechsel im Servicebuch neu und nutzt die
- seither tatsächlich gefahrene Strecke. Sie ersetzt die Meldung des Fahrzeugs nicht,
- sondern ergänzt sie.
+ Die eigene Prognose rechnet ab dem letzten Eintrag der jeweiligen Art im Servicebuch neu
+ und nutzt die seither tatsächlich gefahrene Strecke. Sie ersetzt die Meldung des
+ Fahrzeugs nicht, sondern ergänzt sie.
diff --git a/companion-app/src/screens/Uebersicht.tsx b/companion-app/src/screens/Uebersicht.tsx
index c09ea31..3553777 100644
--- a/companion-app/src/screens/Uebersicht.tsx
+++ b/companion-app/src/screens/Uebersicht.tsx
@@ -11,14 +11,24 @@ import { useState } from "react"
import { Fig, ProgressBar, StatusRow, Tile } from "@audi-dash/ui"
import { useDaten } from "../daten/DatenKontext"
-import { bisText, naechsterService } from "../daten/service"
+import { naechsterService } from "../daten/service"
import { datum, datumZeit, dauer, de, deOderStrich, eur } from "../format"
import type { SeitenName } from "../navigation"
import { Leerzustand, NaviKachel } from "./bausteine"
import { Bild } from "./Bild"
import type { ServicebuchEintrag } from "./Service"
+import { SymbolInspektion, SymbolOelwechsel } from "../symbole"
import { Typenschild, schildErkennen } from "./Typenschild"
+// "bis zum Ölwechsel"/"bis zur Inspektion" entfaellt zugunsten des CI-Icons
+// links der Kennzahl - deckungsgleich mit ART_ICON im Panel (vHome()). Die
+// Hauptuntersuchung hat kein Restkilometer-Ziel und zeigt stattdessen den
+// Art-Namen selbst unter dem (gleich großen) Datum.
+const SERVICE_SYMBOL: Partial> = {
+ "Ölwechsel": SymbolOelwechsel,
+ "Inspektion": SymbolInspektion,
+}
+
export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string) => void }) {
const { fahrzeug, einstellungen, fahrten, tankvorgaenge, jetztAktualisieren, statusStand } =
useDaten()
@@ -143,15 +153,26 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
{service ? (
<>
-
+ {(() => {
+ const Symbol = SERVICE_SYMBOL[service.art]
+ const fig = (
+
+ )
+ return Symbol ? (
+
+
+ {fig}
+
+ ) : (
+ fig
+ )
+ })()}
- {service.restKm !== null
- ? `${bisText(service.art)} · vsl. ${datum(service.datum)}`
- : bisText(service.art)}
+ {service.restKm !== null ? `voraussichtlich am ${datum(service.datum)}` : service.art}
>
) : (
diff --git a/companion-app/src/stile/screens.css b/companion-app/src/stile/screens.css
index b01901a..b871012 100644
--- a/companion-app/src/stile/screens.css
+++ b/companion-app/src/stile/screens.css
@@ -214,6 +214,17 @@
line-height: 1;
color: var(--fg3);
}
+/* Serviceart-Icon links der Kennzahl statt des Wortes "bis zum/zur ..." -
+ deckungsgleich mit .fig-zeile im Panel (audi-dashboard.css). */
+.dm-servicefig {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.dm-servicefig svg {
+ color: var(--fg2);
+ flex: none;
+}
.dm-servicezeile {
display: flex;
align-items: baseline;
@@ -229,6 +240,57 @@
color: var(--fg2);
}
+/* Service-Seite, "Anstehende Termine": drei sichtbar getrennte Bloecke statt
+ einer Wertzeilen-Liste - deckungsgleich mit .termingruppe im Panel
+ (audi-dashboard.css, vService()). Trennung ueber Abstand und eine Haarlinie
+ zwischen den Bloecken, nicht ueber verschachtelte Kacheln (Audi-CI). */
+.dm-termingruppe {
+ padding: 18px 0;
+ border-top: 1px solid var(--line);
+}
+.dm-termingruppe:first-of-type {
+ padding-top: 2px;
+ border-top: 0;
+}
+.dm-tgkopf {
+ display: flex;
+ align-items: center;
+ gap: 9px;
+ min-width: 0;
+}
+.dm-tgkopf svg {
+ color: var(--fg2);
+ flex: none;
+}
+.dm-tgkopf span {
+ font-size: 16.5px;
+ color: var(--fg);
+ font-weight: 400;
+}
+.dm-tgvorgabe {
+ font-size: 11.5px;
+ color: var(--fg2);
+ margin-top: 8px;
+}
+.dm-tgwert {
+ font-weight: 300;
+ font-size: 22px;
+ color: var(--fg);
+ letter-spacing: -0.005em;
+ margin-top: 4px;
+}
+.dm-tgwert--leer {
+ font-weight: 400;
+ font-size: 15px;
+ color: var(--fg3);
+ margin-top: 8px;
+}
+.dm-tgprognose {
+ font-size: 12px;
+ color: var(--fg2);
+ margin-top: 6px;
+}
+
.dm-tanktext {
display: flex;
justify-content: space-between;
diff --git a/companion-app/src/symbole.tsx b/companion-app/src/symbole.tsx
index c4d9263..034dc0f 100644
--- a/companion-app/src/symbole.tsx
+++ b/companion-app/src/symbole.tsx
@@ -147,6 +147,48 @@ export function SymbolMehr({ groesse }: SymbolProps) {
)
}
+/**
+ * oil-change-s/inspection-s aus dem Audi-CI-Icon-Satz (vom Nutzer als SVG
+ * geliefert, verbatim übernommen — 24er-Grid, wie im Panel als CI.oilChangeS/
+ * CI.inspectionS). Anders als die generischen Rahmen()-Symbole oben sind das
+ * gefüllte Flächen-Icons, deshalb kein stroke-Wrapper.
+ */
+function RahmenFlaeche({ groesse = 24, kinder }: SymbolProps & { kinder: ReactElement }) {
+ return (
+
+ )
+}
+
+export function SymbolOelwechsel({ groesse }: SymbolProps) {
+ return (
+
+ }
+ />
+ )
+}
+
+export function SymbolInspektion({ groesse }: SymbolProps) {
+ return (
+
+ }
+ />
+ )
+}
+
export function SymbolWarnung({ groesse }: SymbolProps) {
return (
',
editS: '',
+ /* oil-change-l/-s, inspection-l/-s aus dem Audi-CI-Icon-Satz (vom Nutzer als
+ SVG geliefert, verbatim uebernommen) - l im 48er-, s im 24er-Grid wie
+ poi-l/poi-s. Die -s-Variante steht links neben der Kennzahl im
+ Nächster-Service-Block (vHome()) statt des vorherigen "bis zum/zur..."
+ Textes - die Serviceart wird jetzt ueber das Icon erkannt, nicht ueber
+ Worte. */
+ oilChangeL: '',
+ oilChangeS: '',
+ inspectionL: '',
+ inspectionS: '',
};
function ciSVG(pfad, px = 16) {
return ``;
@@ -1089,14 +1099,15 @@ function teaser() {
function naechsterTermin() { return termine().slice().sort((a, b) => a.ziel - b.ziel)[0]; }
-/* "der Ölwechsel", aber "die Inspektion"/"die Hauptuntersuchung" - ohne diese
- Zuordnung stünde auf der Übersicht "bis zum Inspektion". */
-const ART_BIS = {
- "Ölwechsel": "bis zum Ölwechsel",
- "Inspektion": "bis zur Inspektion",
- "Hauptuntersuchung": "bis zur Hauptuntersuchung",
-};
-const bisText = (art) => ART_BIS[art] || `bis zum nächsten Service (${art})`;
+// CI-Icon je Serviceart statt eines Wort-Zusatzes ("bis zum Ölwechsel" o.ä.) -
+// nur Ölwechsel/Inspektion haben eines, die Hauptuntersuchung nicht. Verwendet
+// von vHome() (Nächster-Service-Block) und vService() (Anstehende Termine).
+// Das Ölwechsel-Icon ist bei gleicher Boxgröße optisch kleiner als das
+// Inspektion-Icon (füllt in seinem eigenen 24x24-Raster nur eine 24x10-Fläche
+// statt fast der vollen Höhe) - deshalb eigene, größere Boxgröße statt einer
+// gemeinsamen Konstante.
+const ART_ICON = { "Ölwechsel": CI.oilChangeS, "Inspektion": CI.inspectionS };
+const ART_ICON_GROESSE = { "Ölwechsel": 26, "Inspektion": 20 };
function intervalle(t) {
const oel = CAR.oel, b = t.basis;
@@ -1281,17 +1292,26 @@ function vHome() {
const hauptwert = restKm !== null
? `${de(restKm)}km`
: (datum ? dedat(datum) : "–");
- // Ohne Restkilometer steht das Datum selbst im Zahlenfeld - dann kleiner,
- // damit "15.04.2027" auf 375px nicht umbricht.
- const zahlGroesse = restKm !== null ? 40 : 30;
+ // Ohne Restkilometer steht das Datum selbst im Zahlenfeld - auf Nutzerwunsch
+ // jetzt in derselben Groesse wie die km-Zahl (vorher 30px, damit "15.04.2027"
+ // auf 375px nicht umbricht - am laufenden Panel geprueft, dass es bei 40px
+ // weiterhin einzeilig bleibt).
+ const zahlGroesse = 40;
+ // Icon statt "bis zum Ölwechsel"/"bis zur Inspektion" (nur fuer die beiden
+ // Arten, die eines haben). Die Hauptuntersuchung hat kein Restkilometer-Ziel
+ // und zeigt stattdessen den Art-Namen selbst unter dem Datum.
+ const artIcon = ART_ICON[t.art];
const nebenzeile = restKm !== null && datum
- ? `${bisText(t.art)} · vsl. ${dedat(datum)}`
- : bisText(t.art);
+ ? `voraussichtlich am ${dedat(datum)}`
+ : t.art;
+ const figMarkup = artIcon
+ ? `
- ${termine().map((t, i, arr) => {
- const letzte = i === arr.length - 1 ? 'style="border-bottom:0"' : "";
- const iv = intervalle(t);
- if (!iv) return `
-
${t.art}
kein Eintrag im Servicebuch
`;
- const anzeige = iv.hersteller || iv.eigen;
- const fm = fahrzeugMeldungRoh(t.art);
- const fmDatum = fm.ts ? dedat(new Date(fm.ts)) : null;
- // Forecast-Zeile aus dem kundeneigenen Intervall in "Einrichten" mit
- // eigenständiger Reset-nach-Service-Logik (servicePrognose()): beim
- // Ölwechsel mit Restkilometern, bei der Inspektion auf Wunsch nur mit
- // dem Datum. Hauptuntersuchung bleibt bewusst ohne.
- const eigenOel = t.art === "Ölwechsel" ? oelwechselPrognose() : null;
- const eigenInsp = t.art === "Inspektion" ? inspektionPrognose() : null;
- const hatMeldung = fm.km != null || fmDatum;
- // Die Prognose steht als eigene Zeile ueber die volle Kachelbreite
- // unter dt/dd (.row-fussnote), nicht mehr in der rechten Spalte: dort
- // standen nur ~217px zur Verfuegung, in die "9.029 km, voraussichtlich
- // am 12.04.2027" zwangslaeufig zweizeilig umbrach.
- const fussnote = hatMeldung && eigenOel
- ? `${de(eigenOel.restKm)} km, voraussichtlich am ${dedat(eigenOel.datum)}`
- : eigenInsp ? `voraussichtlich am ${dedat(eigenInsp.datum)}` : "";
- return `
- ${termine().map((t, i, arr) => {
- const letzte = i === arr.length - 1 ? 'style="border-bottom:0"' : "";
- const iv = intervalle(t);
- if (!iv) return `
-
${t.art}
kein Eintrag im Servicebuch
`;
- const anzeige = iv.hersteller || iv.eigen;
- const fm = fahrzeugMeldungRoh(t.art);
- const fmDatum = fm.ts ? dedat(new Date(fm.ts)) : null;
- // Forecast-Zeile aus dem kundeneigenen Intervall in "Einrichten" mit
- // eigenständiger Reset-nach-Service-Logik (servicePrognose()): beim
- // Ölwechsel mit Restkilometern, bei der Inspektion auf Wunsch nur mit
- // dem Datum. Hauptuntersuchung bleibt bewusst ohne.
- const eigenOel = t.art === "Ölwechsel" ? oelwechselPrognose() : null;
- const eigenInsp = t.art === "Inspektion" ? inspektionPrognose() : null;
- const hatMeldung = fm.km != null || fmDatum;
- // Die Prognose steht als eigene Zeile ueber die volle Kachelbreite
- // unter dt/dd (.row-fussnote), nicht mehr in der rechten Spalte: dort
- // standen nur ~217px zur Verfuegung, in die "9.029 km, voraussichtlich
- // am 12.04.2027" zwangslaeufig zweizeilig umbrach.
- const fussnote = hatMeldung && eigenOel
- ? `${de(eigenOel.restKm)} km, voraussichtlich am ${dedat(eigenOel.datum)}`
- : eigenInsp ? `voraussichtlich am ${dedat(eigenInsp.datum)}` : "";
- return `