Parity-Regel: Uebersicht-Service-Block in companion-app portiert
Nutzer stellte klar: Panel und companion-app sollen immer denselben
funktionalen Stand haben, auch wenn eine Anfrage nur das Panel nennt -
das ist reiner Testkomfort (Docker-Instanz laesst sich am schnellsten
ansehen), keine Scope-Entscheidung. Regel als verbindlicher Absatz in
AGENTS.md verankert, direkt unter der bestehenden Maintenance-Regel.
Erste Anwendung im selben Zug: die eben im Panel gebaute
"Naechster Service"-Kachel nach companion-app/ portiert.
- daten/service.ts: neues naechsterService() als Gegenstueck zu
naechsterTermin() im Panel - waehlt ueber Oelwechsel-/Inspektions-
Prognose und die von Hand gepflegte Hauptuntersuchung hinweg den
zeitlich naechsten Termin. bisText() liefert denselben Artikel wie
ART_BIS im Panel ("bis zum Oelwechsel" / "bis zur Inspektion").
- screens/Uebersicht.tsx: die bisherigen Kacheln "Reichweite"/
"Kilometerstand" nebeneinander plus eine separate "Service"-Kachel
mit Werteliste weichen einer Reichweiten-Kachel plus einer
Service-Kachel mit .dm-serviceblock (Knopf, nur der obere Teil) und
.dm-servicezeile (reine Anzeige) darunter - dieselbe Control/Content-
Trennung wie im Panel.
- stile/screens.css: .dm-serviceblock/.dm-servicezeile ergaenzt.
Tests: 4 neue in service.test.ts (naechsterService waehlt das frueher
faellige Datum, nimmt die Hauptuntersuchung auf, liefert nichts ohne
Servicebuch/HU, bisText-Artikel je Art), 1 neuer in screens.test.tsx,
der mit einem vi.fn() als geheZu wirklich belegt, dass ein Klick auf
den Serviceblock navigiert und ein Klick auf die Kilometerstand-Zeile
es nicht tut - dafuer bekam zeige() in screens.test.tsx erst einen
injizierbaren geheZu-Parameter (vorher hart auf () => {} verdrahtet).
npm run typecheck sauber, npm run test 100/100 (von 95), npm run build
erfolgreich.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -58,6 +58,18 @@ the linked documents — this file points, it does not duplicate.
|
||||
> adjust the status table, bump the "Last updated" date). This file must always be written in
|
||||
> **English**, even though the rest of the project is German.
|
||||
|
||||
> **Parity rule (binding, stated by the owner 2026-08-17):** the HA panel
|
||||
> (`homeassistant/www/audi-dashboard-app.js`) and the companion app (`companion-app/`) must
|
||||
> **always be at the same functional state**. When a request names only the panel, that is *not* a
|
||||
> scoping decision — the owner simply tests there because the Docker instance makes it quick to see.
|
||||
> Port every user-visible change to both in the **same session**, and say so in the report. The two
|
||||
> are independent implementations (vanilla JS + shadow DOM vs. React + `@audi-dash/ui`), so a port
|
||||
> is a re-implementation, not a copy: keep the *behaviour and wording* identical, let each side use
|
||||
> its own idiom, and skip only what genuinely cannot apply (documented per case — e.g. panel-only
|
||||
> CSS bugs, or HA-panel-only concepts like the entity Setup menu). If a port is impossible or
|
||||
> unreasonably large, say so explicitly instead of leaving it silently undone. This rule exists
|
||||
> because the two had already drifted six days apart once (see section A).
|
||||
|
||||
**How this file is loaded:** Claude Code does not read `AGENTS.md` natively; the root `CLAUDE.md`
|
||||
imports it via `@AGENTS.md` (official recommended pattern). Other agents (Codex, Cursor, Copilot)
|
||||
read `AGENTS.md` directly. Edit content here, not in `CLAUDE.md`.
|
||||
@@ -1459,11 +1471,29 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed
|
||||
jede Odometer-Aktualisierung verschiebt sie), das Tagesdatum also mehr Genauigkeit verspricht,
|
||||
als der Wert hat. Live geprüft bei 375px und 1280px: Block und Wertezeile fluchten mit der
|
||||
Kachelkante (16→359 bzw. 564→1236), Chevron bündig rechts, Tippfläche 88px hoch, keine neuen
|
||||
Konsolenfehler. Deployed als Version `1787006000`, synchron in `installationspaket/`.
|
||||
Entwurf und Begründung als Artefakt: „Zwei Kacheln, drei Antworten“.
|
||||
**Noch offen:** die Änderung existiert nur im Panel, nicht in `companion-app/` — dort steht
|
||||
die Übersicht weiter mit dem alten Aufbau (siehe den Drift-Eintrag weiter oben; die beiden
|
||||
Codebasen laufen sonst wieder auseinander).
|
||||
Konsolenfehler. Deployed als Version `1787007000` (zwischenzeitlich auf Nutzerwunsch von
|
||||
`.tile.flat` auf eine normale `.tile` mit Hintergrund umgestellt — `.serviceblock:active
|
||||
{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] **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
|
||||
als verbindlicher Absatz direkt unter der Maintenance-Regel oben in dieser Datei verankert.
|
||||
Als erste Anwendung sofort im selben Zug die Übersicht-Neugestaltung von oben nach
|
||||
`companion-app/` portiert (siehe `Uebersicht.tsx`): `naechsterService()` in `daten/service.ts`
|
||||
neu — Gegenstück zu `naechsterTermin()` im Panel, wählt über Ölwechsel-/Inspektions-Prognose
|
||||
und die von Hand gepflegte Hauptuntersuchung hinweg den zeitlich nächsten Termin, `bisText()`
|
||||
liefert denselben Artikel wie `ART_BIS` im Panel. Die beiden bisherigen Kacheln
|
||||
"Reichweite"/"Kilometerstand" nebeneinander plus eine separate "Service"-Kachel mit
|
||||
Werteliste wichen einer Reichweiten-Kachel plus einer Service-Kachel mit `.dm-serviceblock`
|
||||
(Knopf, nur der obere Teil) und `.dm-servicezeile` (reine Anzeige) darunter — dieselbe
|
||||
Control/Content-Trennung wie im Panel. `npm run typecheck` blieb sauber, `npm run test` auf
|
||||
100/100 (von 95 — vier neue Tests für `naechsterService()`/`bisText()` in `service.test.ts`,
|
||||
einer in `screens.test.tsx`, der mit einem `vi.fn()` als `geheZu` wirklich belegt, dass ein
|
||||
Klick auf den Serviceblock navigiert und ein Klick auf die Kilometerstand-Zeile es nicht tut
|
||||
— dafür musste `zeige()` in `screens.test.tsx` erst einen injizierbaren `geheZu`-Parameter
|
||||
bekommen, vorher war er hart auf `() => {}` verdrahtet), `npm run 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`
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import { inspektionPrognose, letzterOelwechsel, oelwechselPrognose } from "./service"
|
||||
import { bisText, inspektionPrognose, letzterOelwechsel, naechsterService, oelwechselPrognose } from "./service"
|
||||
import type { Fahrzeug } from "./profilAdapter"
|
||||
|
||||
function fahrzeug(odo: number, modus = "hersteller"): Fahrzeug {
|
||||
@@ -112,3 +112,40 @@ describe("inspektionPrognose", () => {
|
||||
expect(inspektionPrognose(fahrzeug(46000), [], jetzt)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe("naechsterService", () => {
|
||||
const jetzt = new Date("2026-08-11T12:00:00")
|
||||
|
||||
it("wählt über Ölwechsel und Inspektion hinweg das zeitlich frühere Datum", () => {
|
||||
// Beide unabhängig mit derselben Formel berechnet, statt ein erwartetes
|
||||
// Datum von Hand vorzurechnen - so bleibt der Test robust gegen spätere
|
||||
// Änderungen an den Intervallen, prüft aber trotzdem die Auswahllogik.
|
||||
const oel = oelwechselPrognose(fahrzeug(46000), BUCH, jetzt)
|
||||
const insp = inspektionPrognose(fahrzeug(46000), BUCH, jetzt)
|
||||
const erwarteteArt = oel!.datum <= insp!.datum ? "Ölwechsel" : "Inspektion"
|
||||
|
||||
const s = naechsterService(fahrzeug(46000), BUCH, jetzt)
|
||||
expect(s).not.toBeNull()
|
||||
expect(s!.art).toBe(erwarteteArt)
|
||||
expect(s!.datum).toBe(erwarteteArt === "Ölwechsel" ? oel!.datum : insp!.datum)
|
||||
})
|
||||
|
||||
it("nimmt die Hauptuntersuchung auf, obwohl sie keine eigene Prognose hat", () => {
|
||||
const f = { ...fahrzeug(46000), hu: "2026-09-01" }
|
||||
const s = naechsterService(f, [], jetzt)
|
||||
expect(s).not.toBeNull()
|
||||
expect(s!.art).toBe("Hauptuntersuchung")
|
||||
expect(s!.restKm).toBeNull()
|
||||
expect(s!.datum).toBe("2026-09-01")
|
||||
})
|
||||
|
||||
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")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -120,3 +120,66 @@ export function inspektionPrognose(
|
||||
): Oelprognose | null {
|
||||
return servicePrognose(fahrzeug, letzterEintrag(buch, /inspektion/i), 30000, 24, jetzt)
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------ Nächster Service */
|
||||
|
||||
export type Serviceart = "Ölwechsel" | "Inspektion" | "Hauptuntersuchung"
|
||||
|
||||
export interface NaechsterService {
|
||||
art: Serviceart
|
||||
/** Restkilometer bis zur Fälligkeit. null bei der Hauptuntersuchung — die
|
||||
hat kein Kilometerziel, nur einen Termin. */
|
||||
restKm: number | null
|
||||
/** ISO-Tagesdatum der voraussichtlichen Fälligkeit. */
|
||||
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<Serviceart, string> = {
|
||||
"Ö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.
|
||||
*
|
||||
* Ölwechsel und Inspektion kommen aus der eigenen Prognose (Servicebuch plus
|
||||
* tatsächliche Fahrleistung), die Hauptuntersuchung aus dem im Profil
|
||||
* gepflegten Termin: sie hat kein Kilometerziel, das Fahrzeug meldet sie nicht,
|
||||
* und hochrechnen lässt sie sich auch nicht.
|
||||
*/
|
||||
export function naechsterService(
|
||||
fahrzeug: Fahrzeug,
|
||||
buch: readonly Buchhaltung[],
|
||||
jetzt = new Date(),
|
||||
): NaechsterService | null {
|
||||
const kandidaten: NaechsterService[] = []
|
||||
|
||||
const oel = oelwechselPrognose(fahrzeug, buch, jetzt)
|
||||
if (oel) kandidaten.push({ art: "Ölwechsel", restKm: oel.restKm, datum: oel.datum })
|
||||
|
||||
const insp = inspektionPrognose(fahrzeug, buch, jetzt)
|
||||
if (insp) kandidaten.push({ art: "Inspektion", restKm: insp.restKm, datum: insp.datum })
|
||||
|
||||
// fahrzeug.hu wird von Hand gepflegt und kann leer oder unlesbar sein.
|
||||
const hu = new Date(fahrzeug.hu)
|
||||
if (fahrzeug.hu && !Number.isNaN(hu.getTime())) {
|
||||
kandidaten.push({
|
||||
art: "Hauptuntersuchung",
|
||||
restKm: null,
|
||||
datum: hu.toISOString().slice(0, 10),
|
||||
})
|
||||
}
|
||||
|
||||
if (kandidaten.length === 0) return null
|
||||
// ISO-Tagesdaten, deshalb reicht der Zeichenkettenvergleich.
|
||||
return kandidaten.sort((a, b) => a.datum.localeCompare(b.datum))[0] ?? null
|
||||
}
|
||||
|
||||
@@ -11,10 +11,12 @@ 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 { datum, datumZeit, dauer, de, deOderStrich, eur } from "../format"
|
||||
import type { SeitenName } from "../navigation"
|
||||
import { Leerzustand, NaviKachel, Wertzeile, Werteliste } from "./bausteine"
|
||||
import { Leerzustand, NaviKachel } from "./bausteine"
|
||||
import { Bild } from "./Bild"
|
||||
import type { ServicebuchEintrag } from "./Service"
|
||||
import { Typenschild, schildErkennen } from "./Typenschild"
|
||||
|
||||
export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string) => void }) {
|
||||
@@ -40,6 +42,9 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
|
||||
.join(" ")
|
||||
.trim()
|
||||
|
||||
const buch = (fahrzeug.service["buch"] ?? []) as ServicebuchEintrag[]
|
||||
const service = naechsterService(fahrzeug, buch)
|
||||
|
||||
const letzteFahrt = fahrten
|
||||
.slice()
|
||||
.sort((a, b) => new Date(b.ts_start).getTime() - new Date(a.ts_start).getTime())[0]
|
||||
@@ -103,62 +108,60 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
|
||||
onClick={() => geheZu("sicherheit")}
|
||||
/>
|
||||
|
||||
<div className="dm-raster">
|
||||
<Tile>
|
||||
<span className="ads-eyebrow">Reichweite</span>
|
||||
<Fig value={deOderStrich(fahrzeug.reichweite)} unit="km" size={40} />
|
||||
<ProgressBar percent={Math.max(0, Math.min(100, fahrzeug.tankPct))} />
|
||||
<div className="dm-tanktext">
|
||||
<span>{de(fahrzeug.tankPct)} % im Tank</span>
|
||||
<span>
|
||||
rund {de((einstellungen.tankvolumen * fahrzeug.tankPct) / 100, 1)} von{" "}
|
||||
{de(einstellungen.tankvolumen)} l
|
||||
</span>
|
||||
</div>
|
||||
</Tile>
|
||||
<Tile>
|
||||
<span className="ads-eyebrow">Reichweite</span>
|
||||
<Fig value={deOderStrich(fahrzeug.reichweite)} unit="km" size={40} />
|
||||
<ProgressBar percent={Math.max(0, Math.min(100, fahrzeug.tankPct))} />
|
||||
<div className="dm-tanktext">
|
||||
<span>{de(fahrzeug.tankPct)} % im Tank</span>
|
||||
<span>
|
||||
rund {de((einstellungen.tankvolumen * fahrzeug.tankPct) / 100, 1)} von{" "}
|
||||
{de(einstellungen.tankvolumen)} l
|
||||
</span>
|
||||
</div>
|
||||
</Tile>
|
||||
|
||||
<Tile>
|
||||
<span className="ads-eyebrow">Kilometerstand</span>
|
||||
<Fig
|
||||
value={fahrzeug.odoBekannt ? de(fahrzeug.odo) : "—"}
|
||||
unit="km"
|
||||
size={40}
|
||||
/>
|
||||
</Tile>
|
||||
</div>
|
||||
{/* Kilometerstand und Service standen bis 2026-08-17 als zwei gleich
|
||||
große Kacheln nebeneinander — gleiche Zahlengröße, gleiche Farbe,
|
||||
obwohl die beiden Zahlen Grundverschiedenes meinen: ein gemessener
|
||||
Ist-Wert gegen einen täglich schrumpfenden Countdown.
|
||||
|
||||
{/* 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={
|
||||
Jetzt eine führende Kennzahl, darunter der Kilometerstand als ruhige
|
||||
Wertezeile — Audi-CI (Hierarchie über Größe und Farbe) plus Apple HIGs
|
||||
"differentiate controls from content": nur der obere Teil ist der
|
||||
Knopf zur Service-Seite, die Kilometerstand-Zeile bleibt reine
|
||||
Anzeige. Deckungsgleich mit vHome() im Panel. */}
|
||||
<Tile>
|
||||
<button
|
||||
type="button"
|
||||
className="dm-serviceblock"
|
||||
onClick={() => geheZu("service")}
|
||||
>
|
||||
<span className="ads-eyebrow">Nächster Service:</span>
|
||||
<span className="dm-serviceblock__chevron" aria-hidden="true">
|
||||
›
|
||||
</span>
|
||||
{service ? (
|
||||
<>
|
||||
<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
|
||||
}
|
||||
<Fig
|
||||
value={service.restKm !== null ? de(service.restKm) : datum(service.datum)}
|
||||
{...(service.restKm !== null ? { unit: "km" } : {})}
|
||||
size={service.restKm !== null ? 40 : 30}
|
||||
/>
|
||||
<span className="dm-fussnote">
|
||||
{service.restKm !== null
|
||||
? `${bisText(service.art)} · vsl. ${datum(service.datum)}`
|
||||
: bisText(service.art)}
|
||||
</span>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<span className="dm-fussnote">kein Eintrag im Servicebuch</span>
|
||||
)}
|
||||
</button>
|
||||
<div className="dm-servicezeile">
|
||||
<span className="dm-servicezeile__k">Kilometerstand</span>
|
||||
<Fig value={fahrzeug.odoBekannt ? de(fahrzeug.odo) : "—"} unit="km" size={20} />
|
||||
</div>
|
||||
</Tile>
|
||||
|
||||
<div className="dm-raster">
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react"
|
||||
import { beforeEach, describe, expect, it } from "vitest"
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
import { DatenAnbieter } from "../daten/DatenKontext"
|
||||
import { Shell } from "../Shell"
|
||||
@@ -17,19 +17,19 @@ import { bildschirmbreiteSetzen } from "../tests/aufbau"
|
||||
|
||||
const ALLE_SEITEN = Object.keys(TITEL) as SeitenName[]
|
||||
|
||||
function zeige(name: SeitenName, id?: string) {
|
||||
function zeige(name: SeitenName, id?: string, geheZu: (name: SeitenName, id?: string) => void = () => {}) {
|
||||
const api = beispielApi()
|
||||
return render(
|
||||
<div className="ads-root" data-theme="nacht">
|
||||
<DatenAnbieter api={api}>
|
||||
<Shell
|
||||
route={id === undefined ? { name } : { name, id }}
|
||||
geheZu={() => {}}
|
||||
geheZu={geheZu}
|
||||
tabBeschriftung
|
||||
>
|
||||
<SeiteFuer
|
||||
route={id === undefined ? { name } : { name, id }}
|
||||
geheZu={() => {}}
|
||||
geheZu={geheZu}
|
||||
tabBeschriftung
|
||||
setzeTabBeschriftung={() => {}}
|
||||
beiAbmeldung={() => {}}
|
||||
@@ -82,6 +82,23 @@ describe("Inhalte kommen wirklich aus den Daten", () => {
|
||||
expect(screen.getByText("385")).toBeTruthy()
|
||||
})
|
||||
|
||||
it("Übersicht: der Serviceblock navigiert, die Kilometerstand-Zeile nicht", async () => {
|
||||
// Reproduziert die eigentliche HIG-Vorgabe hinter dem Layout
|
||||
// ("differentiate controls from content"), nicht nur das Aussehen:
|
||||
// nur der obere Teil der Kachel ist ein Knopf.
|
||||
const geheZu = vi.fn()
|
||||
const { container } = zeige("home", undefined, geheZu)
|
||||
await waitFor(() => expect(screen.getByText("Nächster Service:")).toBeTruthy())
|
||||
|
||||
fireEvent.click(screen.getByText("Nächster Service:"))
|
||||
expect(geheZu).toHaveBeenCalledWith("service")
|
||||
|
||||
geheZu.mockClear()
|
||||
const zeile = container.querySelector(".dm-servicezeile")
|
||||
if (zeile) fireEvent.click(zeile)
|
||||
expect(geheZu).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("zeigt einen unbekannten Prüfpunkt als unbekannt, nicht als sicher", async () => {
|
||||
const { container } = zeige("sicherheit")
|
||||
await waitFor(() => expect(container.querySelector(".dm-pruefliste")).not.toBeNull())
|
||||
|
||||
@@ -185,6 +185,50 @@
|
||||
padding: var(--sp-4) var(--sp-4) var(--sp-5);
|
||||
}
|
||||
|
||||
/* Übersicht: "Nächster Service" als tippbare Kennzahl innerhalb der Kachel,
|
||||
darunter der Kilometerstand als ruhige Wertezeile. Nur der obere Teil ist
|
||||
ein Knopf, damit "differentiate controls from content" (Apple HIG) gilt -
|
||||
deckungsgleich mit .serviceblock/.servicezeile im Panel
|
||||
(audi-dashboard.css). */
|
||||
.dm-serviceblock {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
background: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
.dm-serviceblock:active {
|
||||
opacity: 0.55;
|
||||
}
|
||||
.dm-serviceblock__chevron {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 0;
|
||||
font-size: 15px;
|
||||
line-height: 1;
|
||||
color: var(--fg3);
|
||||
}
|
||||
.dm-servicezeile {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: var(--sp-4);
|
||||
margin-top: var(--sp-3);
|
||||
padding-top: 13px;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
.dm-servicezeile__k {
|
||||
font-size: 13px;
|
||||
font-weight: 300;
|
||||
color: var(--fg2);
|
||||
}
|
||||
|
||||
.dm-tanktext {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
Reference in New Issue
Block a user