Übersicht: Wert und Kontext trennen statt "9,0 tkm/ 04/27"

Auf Nachfrage, wie Apple HIG und die Audi-CI die beiden Kacheln loesen
wuerden - beide laufen auf dieselbe Antwort hinaus.

Die Oelwechsel-Kachel presste zwei verschiedene Groessen in ein
Zahlenfeld: Restkilometer und Prognosedatum, getrennt durch einen
Schraegstrich. Zwei Einheiten, davon eine erfundene ("tkm" schreibt
niemand - die Mein-Audi-Servicebox nannte dieselbe Zahl schon immer
"9.029 km"), zwei Schraegstriche mit zwei Bedeutungen, und auf 375px
zweizeilig umgebrochen.

Apple HIG "Layout" ("don't obscure essential information by crowding it
with nonessential details") und "Typography" (Hierarchie ueber Groesse
und Farbe) verlangen die Trennung; die Audi-CI definiert .fig
ausdruecklich als "large numeric figures" - eine Zahl, nicht zwei.

- Wert gross (9.029 km), Kontext leise darunter (vsl. 04/2027)
- Echte Einheit statt "tkm"
- Kachel ist jetzt ein Knopf mit Chevron zur Service-Seite; vorher eine
  Sackgasse, obwohl es die Seite gibt
- mmjj() dadurch verwaist, ersetzt durch mmjjjj() mit vollem Jahr
- HU-/Inspektionszweig faellt mit dem Oelwechselzweig zusammen, weil sie
  nach der Trennung dieselbe Form haben

companion-app hatte diesen Fehler nicht (dort standen die Termine schon
als Label/Wert/Zusatz). Dort stattdessen der benachbarte
Gruppierungsfehler behoben: die Service-Zeilen hingen innerhalb der
Kilometerstand-Kachel und fuehrten nirgendwohin - jetzt eigene,
antippbare Kachel zur Service-Seite.

Live geprueft im Panel (Tag und Nacht, 375px): beide Kacheln einzeilig,
Tippen oeffnet Service. companion-app: typecheck sauber, 95/95 Tests,
Build erfolgreich.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 13:54:27 +02:00
parent edf3845375
commit 7fc378f427
6 changed files with 120 additions and 73 deletions
+28 -1
View File
@@ -45,7 +45,9 @@ and hint text from the Arbeitsweg pill, and fixed a daylight-mode-only regressio
where the image placeholder's grey background had turned invisible-white; separately the same day,
`companion-app/` (the actual phone-app codebase, which had silently drifted six days behind the
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).
"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).
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.
@@ -1380,6 +1382,31 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed
the secure-storage adapter is actually fully wired (`ablageNativ.ts`, activated from
`main.tsx`) and only the native `ios/`/`android/` platform folders themselves (deliberately
gitignored, regenerated via `npx cap add`) are the missing piece, and that step needs a Mac.
- [x] Übersicht: Kilometerstand-/Service-Kacheln neu gestaltet (2026-08-17) — Nutzer fragte, wie
Apple HIG und die Audi-CI das lösen würden. Befund: die Ölwechsel-Kachel presste zwei
verschiedene Größen in **ein** Zahlenfeld (`teile.join("/ ")``9,0 tkm/ 04/27`) — zwei
Einheiten, davon eine erfundene (**„tkm" schreibt niemand**, und die Mein-Audi-Servicebox
nannte dieselbe Zahl schon immer „9.029 km", d. h. dieselbe Tatsache war an zwei Stellen
unterschiedlich formatiert), zwei Schrägstriche mit zwei Bedeutungen, und auf 375 px
zweizeilig umgebrochen. Beide Regelwerke laufen hier auf dieselbe Auflösung hinaus, live
nachgelesen: Apple HIG *Layout* („Make essential information easy to find by giving it
sufficient space… don't obscure it by crowding it with nonessential details") und
*Typography* („Adjust font weight, size, and color… to help people visualize hierarchy");
die Audi-CI definiert `.fig` (Audi Type Wide 300) ausdrücklich als *„large numeric
figures"* — **eine** Zahl, nicht zwei. Umgesetzt: Wert und Kontext getrennt
(`9.029 km` groß, `vsl. 04/2027` als leisere Zweitzeile), echte Einheit statt „tkm", und
die Kachel ist jetzt ein `button.tile.tilebtn` mit `.go`-Chevron nach `service:x` — vorher
eine Sackgasse, obwohl die Service-Seite existiert. `mmjj()` wurde dadurch verwaist (nur
noch das volle Jahr wird gebraucht) und ist durch `mmjjjj()` ersetzt. Die HU-/Inspektions-
Zweige sind mit dem Ölwechsel-Zweig zusammengefallen, weil sie nach der Trennung dieselbe
Form haben. Live geprüft (Tag und Nacht, 375 px): beide Kacheln einzeilig, Tippen öffnet
Service mit aktivem Zurück-Pfeil.
**companion-app hatte diesen Fehler nicht** — dort standen die Termine schon als
Label/Wert/Zusatz-Zeilen. Stattdessen dort der benachbarte Gruppierungsfehler behoben: die
zwei Service-Zeilen hingen *innerhalb* der Kilometerstand-Kachel (zwei Termine unter einer
Überschrift, die nichts mit ihnen zu tun hat) und führten nirgendwohin. Jetzt eigene,
antippbare `Tile variant="button" chevron``service`. Geprüft: typecheck sauber, 95/95
Tests, Build erfolgreich.
- [ ] Fix remaining documentation drift (statistics claim, README gaps, obsolete TODO comment) —
text-only changes; INSTALL.md's WLAN/TommiG1 drift and stale variable names were fixed
2026-08-12 (see section B); `DESIGN_REVIEW_2026-08-13.md` and `REVIEW_main_2026-08-13.md`
+34 -24
View File
@@ -124,33 +124,43 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
unit="km"
size={40}
/>
<Werteliste
kinder={
<>
<Wertzeile
label="Nächster Ölwechsel"
wert={fahrzeug.oelwechselFaelligTs ? datum(fahrzeug.oelwechselFaelligTs) : "—"}
zusatz={
fahrzeug.oelwechselFaelligKm != null
? `in ${de(Math.abs(fahrzeug.oelwechselFaelligKm))} km`
: undefined
}
/>
<Wertzeile
label="Nächste Inspektion"
wert={fahrzeug.inspektionFaelligTs ? datum(fahrzeug.inspektionFaelligTs) : "—"}
zusatz={
fahrzeug.inspektionFaelligKm != null
? `in ${de(Math.abs(fahrzeug.inspektionFaelligKm))} km`
: undefined
}
/>
</>
}
/>
</Tile>
</div>
{/* Service stand bis 2026-08-17 als Werteliste INNERHALB der
Kilometerstand-Kachel - zwei Termine unter einer Überschrift, die
nichts mit ihnen zu tun hat. Apple HIG "Layout" verlangt, verwandte
Dinge zu gruppieren und Unverwandtes zu trennen; außerdem führte von
hier kein Weg zur Service-Seite, obwohl es sie gibt. Jetzt eigene,
antippbare Kachel - dieselbe Auflösung wie im Panel (vHome). */}
<Tile variant="button" chevron onClick={() => geheZu("service")}>
<span className="ads-eyebrow">Service</span>
<Werteliste
kinder={
<>
<Wertzeile
label="Nächster Ölwechsel"
wert={fahrzeug.oelwechselFaelligTs ? datum(fahrzeug.oelwechselFaelligTs) : "—"}
zusatz={
fahrzeug.oelwechselFaelligKm != null
? `in ${de(Math.abs(fahrzeug.oelwechselFaelligKm))} km`
: undefined
}
/>
<Wertzeile
label="Nächste Inspektion"
wert={fahrzeug.inspektionFaelligTs ? datum(fahrzeug.inspektionFaelligTs) : "—"}
zusatz={
fahrzeug.inspektionFaelligKm != null
? `in ${de(Math.abs(fahrzeug.inspektionFaelligKm))} km`
: undefined
}
/>
</>
}
/>
</Tile>
<div className="dm-raster">
{letzteFahrt ? (
<NaviKachel
@@ -324,11 +324,13 @@ function heute() { return new Date(); }
const dTag = 86400000;
/* Ein unlesbares Datum ergibt einen Strich, keine Rechenreste. dat() erwartet
TT.MM.JJJJ; steht im Profil etwas anderes (es wird laut INSTALL.md von Hand
gepflegt, z. B. versehentlich im ISO-Format), lieferte mmjj() sonst
"NaN/N" - ein Wert, der wie eine Angabe aussieht und keine ist. */
gepflegt, z. B. versehentlich im ISO-Format), lieferte mmjjjj() sonst
"NaN/NaN" - ein Wert, der wie eine Angabe aussieht und keine ist. */
const gueltigesDatum = (d) => d instanceof Date && !Number.isNaN(d.getTime());
const mmjj = (d) => (gueltigesDatum(d)
? String(d.getMonth() + 1).padStart(2, "0") + "/" + String(d.getFullYear()).slice(2)
/* Volles Jahr, weil das Datum überall als eigene Zeile steht und nicht mehr
in ein Zahlenfeld gequetscht wird (früher mmjj() mit "04/27"). */
const mmjjjj = (d) => (gueltigesDatum(d)
? String(d.getMonth() + 1).padStart(2, "0") + "/" + d.getFullYear()
: "");
const dedat = (d) => (gueltigesDatum(d)
? d.toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", year: "numeric" })
@@ -1248,25 +1250,28 @@ function vHome() {
<div class="fig" style="font-size:29px;margin-top:14px">${CAR.odoBekannt ? de(CAR.odo) : ""}<span class="unit">km</span></div></div>
${(() => { if (!t) return `<div class="tile"><span class="label">Service</span><div class="wert" style="margin-top:14px">kein Eintrag</div></div>`;
const artLabel = t.art === "Hauptuntersuchung" ? "Haupt-<br>untersuchung" : esc(t.art);
if (t.art === "Ölwechsel") {
const restKm = t.km !== null && CAR.odoBekannt ? Math.abs(t.km - CAR.odo) : null;
const datum = t.kmDatum || t.datum;
const teile = [];
if (restKm !== null) teile.push(de(restKm / 1000, 1) + " tkm");
if (datum) teile.push(mmjj(datum));
return `<div class="tile"><span class="label">${artLabel}</span>
<div class="fig" style="font-size:29px;margin-top:14px">${teile.join("/ ")}</div></div>`;
}
const teileHU = [];
if (t.zuerst === "km") {
teileHU.push(de(t.km - CAR.odo) + " km");
if (t.kmDatum) teileHU.push(mmjj(t.kmDatum));
} else {
teileHU.push(mmjj(t.datum));
if (t.km !== null && CAR.odoBekannt) teileHU.push(de(Math.abs(t.km - CAR.odo)) + " km");
}
return `<div class="tile"><span class="label">${artLabel}</span>
<div class="fig" style="font-size:29px;margin-top:14px">${teileHU.join("/ ")}</div></div>`; })()}
const restKm = t.km !== null && CAR.odoBekannt ? Math.abs(t.km - CAR.odo) : null;
const datum = t.kmDatum || t.datum;
/* Ein Wert pro Zahlenfeld, der Rest als leisere Zweitzeile darunter.
Vorher stand hier "9,0 tkm/ 04/27" - zwei Einheiten in einem Slot,
eine davon erfunden ("tkm" schreibt niemand, und die Mein-Audi-Kachel
nannte dieselbe Zahl schon immer "9.029 km"), auf 375px zweizeilig
umgebrochen. Apple HIG "Layout" ("don't obscure [essential
information] by crowding it with nonessential details") und
"Typography" (Hierarchie über Größe/Farbe) verlangen die Trennung -
deckungsgleich mit der Audi-CI, wo .fig genau EINE große Zahl trägt.
Kachel ist jetzt ein Knopf: hinter ihr liegt die Service-Seite, das
war bislang eine Sackgasse ohne Hinweis. */
const hauptwert = restKm !== null
? `${de(restKm)}<span class="unit">km</span>`
: (datum ? mmjjjj(datum) : "");
const nebenzeile = restKm !== null && datum ? `vsl. ${mmjjjj(datum)}` : "";
return `<button class="tile tilebtn" data-go="service:x">
<span class="label">${artLabel}</span>
<svg class="go" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg>
<div class="fig" style="font-size:29px;margin-top:14px">${hauptwert}</div>
${nebenzeile ? `<span class="label" style="margin-top:6px">${nebenzeile}</span>` : ""}
</button>`; })()}
</div>
${standortKachel()}
${teaser()}`;
@@ -1 +1 @@
{"version": 1787009000}
{"version": 1787100000}
+28 -23
View File
@@ -324,11 +324,13 @@ function heute() { return new Date(); }
const dTag = 86400000;
/* Ein unlesbares Datum ergibt einen Strich, keine Rechenreste. dat() erwartet
TT.MM.JJJJ; steht im Profil etwas anderes (es wird laut INSTALL.md von Hand
gepflegt, z. B. versehentlich im ISO-Format), lieferte mmjj() sonst
"NaN/N" - ein Wert, der wie eine Angabe aussieht und keine ist. */
gepflegt, z. B. versehentlich im ISO-Format), lieferte mmjjjj() sonst
"NaN/NaN" - ein Wert, der wie eine Angabe aussieht und keine ist. */
const gueltigesDatum = (d) => d instanceof Date && !Number.isNaN(d.getTime());
const mmjj = (d) => (gueltigesDatum(d)
? String(d.getMonth() + 1).padStart(2, "0") + "/" + String(d.getFullYear()).slice(2)
/* Volles Jahr, weil das Datum überall als eigene Zeile steht und nicht mehr
in ein Zahlenfeld gequetscht wird (früher mmjj() mit "04/27"). */
const mmjjjj = (d) => (gueltigesDatum(d)
? String(d.getMonth() + 1).padStart(2, "0") + "/" + d.getFullYear()
: "");
const dedat = (d) => (gueltigesDatum(d)
? d.toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", year: "numeric" })
@@ -1248,25 +1250,28 @@ function vHome() {
<div class="fig" style="font-size:29px;margin-top:14px">${CAR.odoBekannt ? de(CAR.odo) : ""}<span class="unit">km</span></div></div>
${(() => { if (!t) return `<div class="tile"><span class="label">Service</span><div class="wert" style="margin-top:14px">kein Eintrag</div></div>`;
const artLabel = t.art === "Hauptuntersuchung" ? "Haupt-<br>untersuchung" : esc(t.art);
if (t.art === "Ölwechsel") {
const restKm = t.km !== null && CAR.odoBekannt ? Math.abs(t.km - CAR.odo) : null;
const datum = t.kmDatum || t.datum;
const teile = [];
if (restKm !== null) teile.push(de(restKm / 1000, 1) + " tkm");
if (datum) teile.push(mmjj(datum));
return `<div class="tile"><span class="label">${artLabel}</span>
<div class="fig" style="font-size:29px;margin-top:14px">${teile.join("/ ")}</div></div>`;
}
const teileHU = [];
if (t.zuerst === "km") {
teileHU.push(de(t.km - CAR.odo) + " km");
if (t.kmDatum) teileHU.push(mmjj(t.kmDatum));
} else {
teileHU.push(mmjj(t.datum));
if (t.km !== null && CAR.odoBekannt) teileHU.push(de(Math.abs(t.km - CAR.odo)) + " km");
}
return `<div class="tile"><span class="label">${artLabel}</span>
<div class="fig" style="font-size:29px;margin-top:14px">${teileHU.join("/ ")}</div></div>`; })()}
const restKm = t.km !== null && CAR.odoBekannt ? Math.abs(t.km - CAR.odo) : null;
const datum = t.kmDatum || t.datum;
/* Ein Wert pro Zahlenfeld, der Rest als leisere Zweitzeile darunter.
Vorher stand hier "9,0 tkm/ 04/27" - zwei Einheiten in einem Slot,
eine davon erfunden ("tkm" schreibt niemand, und die Mein-Audi-Kachel
nannte dieselbe Zahl schon immer "9.029 km"), auf 375px zweizeilig
umgebrochen. Apple HIG "Layout" ("don't obscure [essential
information] by crowding it with nonessential details") und
"Typography" (Hierarchie über Größe/Farbe) verlangen die Trennung -
deckungsgleich mit der Audi-CI, wo .fig genau EINE große Zahl trägt.
Kachel ist jetzt ein Knopf: hinter ihr liegt die Service-Seite, das
war bislang eine Sackgasse ohne Hinweis. */
const hauptwert = restKm !== null
? `${de(restKm)}<span class="unit">km</span>`
: (datum ? mmjjjj(datum) : "");
const nebenzeile = restKm !== null && datum ? `vsl. ${mmjjjj(datum)}` : "";
return `<button class="tile tilebtn" data-go="service:x">
<span class="label">${artLabel}</span>
<svg class="go" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg>
<div class="fig" style="font-size:29px;margin-top:14px">${hauptwert}</div>
${nebenzeile ? `<span class="label" style="margin-top:6px">${nebenzeile}</span>` : ""}
</button>`; })()}
</div>
${standortKachel()}
${teaser()}`;
@@ -1 +1 @@
{"version": 1787009000}
{"version": 1787100000}