Reifen/Version/Wartungsplan: erste Runde des Sammel-Feedbacks behoben
- Archiv-Knopf jetzt in Zeile mit "Montiert" (stale margin-top entfernt), bekommt einen sichtbaren Hintergrund - companion-app: Wechseltermin-Datumsfeld (appearance-Reset gegen natives Safari-Chrome, Breite auf Inhalt geschrumpft) - Mein Audi/Reifen-Box: "montiert: Sommerräder" links statt Marke/Modell, "offen" durch "-" ersetzt - "Servicebuch" ueberall in "Wartungsplan" umbenannt (nur Anzeigetext, beide Codebasen) - Version-Kachel: tote Fahrzeugdaten-/Position-/Dashboard-Zeilen entfernt (inkl. der nie aktualisierten CONFIG.version-Konstante) Version 2026.8.28.1, live im Testcontainer verifiziert. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
@@ -3939,6 +3939,86 @@ reading source): the battery list's `<div class="leaf">` now computes `cursor: a
|
|||||||
a genuinely navigable trip row (`<button class="leaf">`) still correctly computes `cursor: pointer` -
|
a genuinely navigable trip row (`<button class="leaf">`) still correctly computes `cursor: pointer` -
|
||||||
confirming the fix is properly scoped, not a blanket removal.
|
confirming the fix is properly scoped, not a blanket removal.
|
||||||
|
|
||||||
|
## AB. First round of a large owner batch: Reifen/Version/Wartungsplan fixes (2026.8.28.1)
|
||||||
|
|
||||||
|
Owner sent 12 items in one message. Worked through the quick, well-specified ones first; the two larger
|
||||||
|
ones (combined export/import popup, Setup-menu sensor field redesign) are being scoped with the owner before
|
||||||
|
implementation - see the open items this creates below.
|
||||||
|
|
||||||
|
- **Archiv-Knopf nicht in Zeile mit "Montiert"** (Reifen-Seite, panel only - no companion-app equivalent):
|
||||||
|
root cause was `.montiert`'s own `margin-top:var(--sp-4)` (added 2026-08-16 round eight, when the pill sat
|
||||||
|
alone under the headline block) - once the archive icon button joined it on the same flex row (section R),
|
||||||
|
that leftover margin threw off `align-items:center`'s vertical centering between the two children, even
|
||||||
|
though both were genuinely siblings in the same row. Moved the spacing to the wrapping row div instead of
|
||||||
|
one child. Verified live: both centers compute to the identical `584.08px` Y-coordinate.
|
||||||
|
- **Archiv-Icon soll ein Knopf mit Hintergrund sein**: new `.archivbtn` class (panel), permanent
|
||||||
|
`background: var(--tile-2)` instead of the generic `.iconbtn`'s hover/active-only background.
|
||||||
|
- **companion-app: Wechseltermin-Datumsfeld sah aus wie ein gestreckter Kreis statt einer Box**: root cause
|
||||||
|
is the standard cross-browser gap - `input[type="date"]` needs an explicit `appearance:none` reset or
|
||||||
|
Safari/WKWebView (this app's iOS shell) keeps its own heavily-rounded native chrome regardless of author
|
||||||
|
CSS on `background`/`border`/`border-radius`. Neither `.dm-eingabe` nor the shared `@audi-dash/ui` `Feld`
|
||||||
|
component reset it. Fixed locally in `companion-app/src/stile/screens.css` (not in the shared design-system
|
||||||
|
package - no other field type shows this, scoped to `input[type="date"].dm-eingabe` only) with
|
||||||
|
`-webkit-appearance:none`/`appearance:none` plus `width:fit-content` (owner's explicit ask: shrink to the
|
||||||
|
actual date text instead of the shared `Feld`'s `min-width:132px`). **Not live-verified** - no running
|
||||||
|
companion-app session with backend auth this round; verified via `tsc --noEmit` and the full suite only,
|
||||||
|
same limitation as several other companion-app-only changes this session.
|
||||||
|
- **Mein Audi/Reifen-Box restructured**: was `Reifen · Sommer montiert` as the tile header, then
|
||||||
|
`<dt>Continental SportContact</dt><dd>275/30 R20 97Y XL</dd>` as the first row. Header simplified to just
|
||||||
|
"Reifen"; the row's left side now reads `montiert: Sommerräder` (owner's exact wording, `${r.aktiv}räder`)
|
||||||
|
instead of the brand/model, keeping the size (`275/30 R20 97Y XL`) as the right-hand value, per the owner's
|
||||||
|
explicit "montiert... shall be on the left side". companion-app's equivalent "Mein Audi" tile
|
||||||
|
(`MeinAudi.tsx`) is structurally a single-line `NaviKachel` (`${aktiv} · ${km} km`), not the panel's
|
||||||
|
multi-row `.rows` list - it never had "Sommer montiert" or brand/model text to begin with, so nothing to
|
||||||
|
port here (documented structural difference, not a skipped fix).
|
||||||
|
- **"offen" caption removed from the Wechseltermin row**: `vereinbart ? "vereinbart" : "offen"` → `... : "–"`
|
||||||
|
- owner: "the '-' is sufficient". Same structural note as above - companion-app's tile has no equivalent
|
||||||
|
caption to change.
|
||||||
|
- **"Servicebuch" renamed to "Wartungsplan" everywhere it's user-visible**, both codebases - tile/page
|
||||||
|
headings, empty-state text, confirm-delete dialogs, CSV export button label, the "Mein Audi" Service
|
||||||
|
NaviKachel's summary text. Deliberately NOT renamed: the `ServicebuchEintrag` TS type, the `Servicebuch()`
|
||||||
|
component/function names (panel and companion-app), the `sbuch` route key, and every code comment - same
|
||||||
|
precedent as the 2026-08-19 "Zusammen"→"Summe" rename (display-label fix, not a data-model rename).
|
||||||
|
- **"Version" tile cleaned up**: removed the "Fahrzeugdaten"/"Position" rows (both hardcoded to the literal
|
||||||
|
string "FMM003", carried no live information) and the "Dashboard" row, whose value came from `CONFIG.version`
|
||||||
|
- a frontend constant hardcoded to `"1.0"` at `profilZuConfig()` (confirmed via grep: no other reader),
|
||||||
|
never updated since. Removed that dead constant along with its row rather than leaving an orphan. Only
|
||||||
|
"Installiert" (the real `APP_VERSION` from the backend) remains.
|
||||||
|
|
||||||
|
Verified: `node --check` clean on the panel, `tsc --noEmit` clean and full suite green (146/146) on
|
||||||
|
companion-app. Manifest bumped to `2026.8.28.1` (new day, per this project's `YYYY.M.D.N` scheme - the prior
|
||||||
|
session's work had reached `.27.22`). `audi_ha_test` restarted, confirmed clean via log
|
||||||
|
(`Audi Dashboard 2026.8.28.1 eingerichtet`). Live-verified via the panel's own shadow DOM: Reifen tile shows
|
||||||
|
`montiert: Sommerräder | 275/30 R20 97Y XL` and `15.10.2026 | –`; Version tile shows only "Installiert"; the
|
||||||
|
Archiv button and "Montiert" share one Y-center exactly, with a visible background disc behind the icon
|
||||||
|
(screenshot taken).
|
||||||
|
|
||||||
|
**Open, not yet started - scoped with the owner before implementation:**
|
||||||
|
- **Item 8 (informational, answered in chat, not yet acted on)**: both the live path
|
||||||
|
(`tankerkennung.py`→`ablage.distanz_seit_letzter_tankung()`) and the import path
|
||||||
|
(`historienimport.py`'s `_distanz_zum_vorherigen()`) compute "distance since last refuel" as an odometer
|
||||||
|
delta (current/refuel `KM_SENSOR` reading minus the previous refuel's own recorded `odometer_km`) - a
|
||||||
|
derived calculation, not read from any vehicle sensor. Owner pointed out a real FMM003 sensor,
|
||||||
|
`sensor.testzone_fmm003_calculated_distance_covered_after_the_latest_refueling` (device-side, presumably
|
||||||
|
more accurate/authoritative than our own subtraction), that reports this directly and "can be used instead
|
||||||
|
of the calculation" - not yet wired in, would mirror the `TANK_LITER_SENSOR` precedent (section Q): new
|
||||||
|
optional sensor role, preferred over the calculation when mapped, calculation stays as the fallback when
|
||||||
|
unmapped. Owner to confirm before this is built.
|
||||||
|
- **Item 9**: combine the "Fahrzeugprofil" tile (`data-profil="export"`/`"import"`, JSON) and "Daten ausgeben"
|
||||||
|
tile (`data-csv="fahrten"`/`"tanken"`/`"service"`, CSV export only, no import) into one area with two
|
||||||
|
buttons - "Datensatz sichern" opening a popup with 4 individual export actions (Fahrzeugprofil,
|
||||||
|
Fahrten/Tankvorgänge/Wartungsplan as CSV), "Datensatz laden" the same 4 in reverse. The pre-existing,
|
||||||
|
separate "Backup" tile (`data-backup="jetzt"`/`"export"`/`"import"`, a combined profil+fahrten+tank JSON
|
||||||
|
bundle plus the scheduled-backup mechanism) was NOT named by the owner and stays untouched. Three of the
|
||||||
|
four export actions and one of the four import actions already exist and just need moving into the new
|
||||||
|
popup; CSV **import** for Fahrten/Tankvorgänge/Wartungsplan does not exist anywhere yet and needs new
|
||||||
|
backend parsing (inverse of the existing CSV export format: `;`-delimited, de-DE number format).
|
||||||
|
- **Item 11**: Setup-Menü sensor role fields - replace the current description text under each field's
|
||||||
|
headline (e.g. "on = Fahrt läuft", "Optional, vom CAN...") with the live sensor's current output value and
|
||||||
|
unit inline next to the headline (`[on/off]`, `[%]`, `[l]`, `[km]`...), and show the matched entity's ID
|
||||||
|
suffix (the part after `fmm003_...`) as "the expected sensor" - not yet investigated in the code
|
||||||
|
(`entitaeten.py`'s `FELDER` catalog / the Setup popup's row-rendering in the panel).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Working conventions (observed — keep them)
|
## Working conventions (observed — keep them)
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ export const TITEL: Record<SeitenName, string> = {
|
|||||||
battliste: "Messwerte",
|
battliste: "Messwerte",
|
||||||
service: "Service",
|
service: "Service",
|
||||||
werkstatt: "Werkstatt",
|
werkstatt: "Werkstatt",
|
||||||
sbuch: "Servicebuch",
|
sbuch: "Wartungsplan",
|
||||||
reifen: "Reifen",
|
reifen: "Reifen",
|
||||||
vers: "Versicherung & Steuer",
|
vers: "Versicherung & Steuer",
|
||||||
beitrag: "Beitrag",
|
beitrag: "Beitrag",
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export function MeinAudi({ geheZu }: { geheZu: (name: SeitenName, id?: string) =
|
|||||||
}
|
}
|
||||||
beiKlick={() => geheZu("battverlauf")}
|
beiKlick={() => geheZu("battverlauf")}
|
||||||
/>
|
/>
|
||||||
<NaviKachel titel="Service" wert="Termine und Servicebuch" beiKlick={() => geheZu("service")} />
|
<NaviKachel titel="Service" wert="Termine und Wartungsplan" beiKlick={() => geheZu("service")} />
|
||||||
<NaviKachel
|
<NaviKachel
|
||||||
titel="Reifen"
|
titel="Reifen"
|
||||||
wert={`${fahrzeug.reifen.aktiv} · ${deOderStrich(aktiverSatz.km)} km`}
|
wert={`${fahrzeug.reifen.aktiv} · ${deOderStrich(aktiverSatz.km)} km`}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
|||||||
{oelFussnote && <div className="dm-tgprognose">{oelFussnote}</div>}
|
{oelFussnote && <div className="dm-tgprognose">{oelFussnote}</div>}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Servicebuch</div>
|
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Wartungsplan</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -156,7 +156,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
|||||||
{inspFussnote && <div className="dm-tgprognose">{inspFussnote}</div>}
|
{inspFussnote && <div className="dm-tgprognose">{inspFussnote}</div>}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Servicebuch</div>
|
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Wartungsplan</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -167,7 +167,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
|||||||
{huGueltig ? (
|
{huGueltig ? (
|
||||||
<div className="dm-tgwert">{datum(fahrzeug.hu)}</div>
|
<div className="dm-tgwert">{datum(fahrzeug.hu)}</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Servicebuch</div>
|
<div className="dm-tgwert dm-tgwert--leer">kein Eintrag im Wartungsplan</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<p className="dm-fussnote">
|
<p className="dm-fussnote">
|
||||||
Die eigene Prognose rechnet ab dem letzten Eintrag der jeweiligen Art im Servicebuch neu
|
Die eigene Prognose rechnet ab dem letzten Eintrag der jeweiligen Art im Wartungsplan neu
|
||||||
und nutzt die seither tatsächlich gefahrene Strecke. Sie ersetzt die Meldung des
|
und nutzt die seither tatsächlich gefahrene Strecke. Sie ersetzt die Meldung des
|
||||||
Fahrzeugs nicht, sondern ergänzt sie.
|
Fahrzeugs nicht, sondern ergänzt sie.
|
||||||
</p>
|
</p>
|
||||||
@@ -196,7 +196,7 @@ export function Service({ geheZu }: { geheZu: (name: SeitenName, id?: string) =>
|
|||||||
beiKlick={() => geheZu("werkstatt")}
|
beiKlick={() => geheZu("werkstatt")}
|
||||||
/>
|
/>
|
||||||
<NaviKachel
|
<NaviKachel
|
||||||
titel="Servicebuch"
|
titel="Wartungsplan"
|
||||||
wert={`${de(buch.length)} Eintrag${buch.length === 1 ? "" : "e"}`}
|
wert={`${de(buch.length)} Eintrag${buch.length === 1 ? "" : "e"}`}
|
||||||
beiKlick={() => geheZu("sbuch")}
|
beiKlick={() => geheZu("sbuch")}
|
||||||
/>
|
/>
|
||||||
@@ -324,7 +324,7 @@ export function Servicebuch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const entfernen = async (index: number) => {
|
const entfernen = async (index: number) => {
|
||||||
if (!bestaetigen("Diesen Servicebuch-Eintrag löschen?")) return
|
if (!bestaetigen("Diesen Wartungsplan-Eintrag löschen?")) return
|
||||||
await schreiben(buch.filter((_, i) => i !== index))
|
await schreiben(buch.filter((_, i) => i !== index))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +397,7 @@ export function Servicebuch() {
|
|||||||
|
|
||||||
{buch.length === 0 && !neuOffen && (
|
{buch.length === 0 && !neuOffen && (
|
||||||
<Leerzustand
|
<Leerzustand
|
||||||
titel="Servicebuch ist leer"
|
titel="Wartungsplan ist leer"
|
||||||
text="Trag hier ein, was am Fahrzeug gemacht wurde. Aus den Ölwechseln entsteht die eigene Wartungsprognose."
|
text="Trag hier ein, was am Fahrzeug gemacht wurde. Aus den Ölwechseln entsteht die eigene Wartungsprognose."
|
||||||
aktion={{ beschriftung: "Eintrag hinzufügen", beiKlick: () => setzeNeuOffen(true) }}
|
aktion={{ beschriftung: "Eintrag hinzufügen", beiKlick: () => setzeNeuOffen(true) }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ export function Uebersicht({ geheZu }: { geheZu: (name: SeitenName, id?: string)
|
|||||||
</span>
|
</span>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<span className="dm-fussnote">kein Eintrag im Servicebuch</span>
|
<span className="dm-fussnote">kein Eintrag im Wartungsplan</span>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<div className="dm-servicezeile">
|
<div className="dm-servicezeile">
|
||||||
|
|||||||
@@ -24,6 +24,22 @@
|
|||||||
color: var(--fg3);
|
color: var(--fg3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* type="date": ohne appearance-Reset behält Safari/WKWebView (die iOS-Hülle
|
||||||
|
dieser App) sein eigenes, stark abgerundetes natives Eingabe-Chrome statt
|
||||||
|
der Box-Optik aus @audi-dash/ui's Feld - beim Nutzer sichtbar als
|
||||||
|
"gestreckter Kreis statt Box" (Reifen.tsx, Wechseltermin). @audi-dash/ui
|
||||||
|
selbst bleibt unangetastet (kein appearance-Reset dort, andere Felder
|
||||||
|
zeigen den Fehler nicht) - der Reset sitzt hier, spezifisch für type=date,
|
||||||
|
höhere Selektor-Spezifität als das geteilte ".ads-feld input" schlägt
|
||||||
|
dessen feste min-width:132px, die hier breiter ist als "27.08.2026"
|
||||||
|
braucht. */
|
||||||
|
input[type="date"].dm-eingabe {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
min-width: 0;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
/* Eine Zeile aus einer bearbeitbaren Liste (z. B. Selbstbeteiligung im
|
/* Eine Zeile aus einer bearbeitbaren Liste (z. B. Selbstbeteiligung im
|
||||||
Vertrag) - zwei Eingaben plus Löschen-Knopf in einer Reihe, deckungsgleich
|
Vertrag) - zwei Eingaben plus Löschen-Knopf in einer Reihe, deckungsgleich
|
||||||
mit .feld/.zeile-loeschen im Panel (audi-dashboard.css). */
|
mit .feld/.zeile-loeschen im Panel (audi-dashboard.css). */
|
||||||
|
|||||||
@@ -116,7 +116,6 @@ function profilZuConfig(p) {
|
|||||||
fahrzeuguntertitel: p.fahrzeug.untertitel,
|
fahrzeuguntertitel: p.fahrzeug.untertitel,
|
||||||
kennzeichen: p.fahrzeug.kennzeichen,
|
kennzeichen: p.fahrzeug.kennzeichen,
|
||||||
startbild: p.einstellungen.uebersichtsbild,
|
startbild: p.einstellungen.uebersichtsbild,
|
||||||
version: "1.0",
|
|
||||||
tankvolumen: p.fahrzeug.tankvolumen_liter,
|
tankvolumen: p.fahrzeug.tankvolumen_liter,
|
||||||
nachtVon: p.einstellungen.nacht_von,
|
nachtVon: p.einstellungen.nacht_von,
|
||||||
nachtBis: p.einstellungen.nacht_bis,
|
nachtBis: p.einstellungen.nacht_bis,
|
||||||
@@ -1394,7 +1393,7 @@ function terminText(t) {
|
|||||||
const restKmAbs = restKm !== null ? Math.abs(restKm) : null;
|
const restKmAbs = restKm !== null ? Math.abs(restKm) : null;
|
||||||
const haupt = t.zuerst === "km" && restKm !== null ? "in " + de(restKmAbs) + " km" : dedat(t.datum);
|
const haupt = t.zuerst === "km" && restKm !== null ? "in " + de(restKmAbs) + " km" : dedat(t.datum);
|
||||||
const neben = t.zuerst === "km" ? "voraussichtlich " + dedat(t.kmDatum) + " · " + dedat(t.datum)
|
const neben = t.zuerst === "km" ? "voraussichtlich " + dedat(t.kmDatum) + " · " + dedat(t.datum)
|
||||||
: (t.km !== null ? "in " + de(restKmAbs) + " km" : (t.art === "Hauptuntersuchung" ? "" : "aus dem Servicebuch fortgeschrieben"));
|
: (t.km !== null ? "in " + de(restKmAbs) + " km" : (t.art === "Hauptuntersuchung" ? "" : "aus dem Wartungsplan fortgeschrieben"));
|
||||||
return { haupt, neben, restKm };
|
return { haupt, neben, restKm };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1468,7 +1467,7 @@ function vHome() {
|
|||||||
</div>`;
|
</div>`;
|
||||||
if (!t) return `<div class="tile" style="margin-bottom:22px">
|
if (!t) return `<div class="tile" style="margin-bottom:22px">
|
||||||
<span class="label">Nächster Service:</span>
|
<span class="label">Nächster Service:</span>
|
||||||
<div class="wert" style="margin-top:8px">kein Eintrag im Servicebuch</div>
|
<div class="wert" style="margin-top:8px">kein Eintrag im Wartungsplan</div>
|
||||||
${kmZeile}
|
${kmZeile}
|
||||||
</div>`;
|
</div>`;
|
||||||
// naechsterTermin() waehlt bereits ueber alle drei Arten hinweg (Oelwechsel,
|
// naechsterTermin() waehlt bereits ueber alle drei Arten hinweg (Oelwechsel,
|
||||||
@@ -1656,13 +1655,13 @@ function vAudi() {
|
|||||||
<dd>${eur(CAR.versicherung.beitrag + CAR.steuer.betrag)} €</dd></div>
|
<dd>${eur(CAR.versicherung.beitrag + CAR.steuer.betrag)} €</dd></div>
|
||||||
</dl></button>
|
</dl></button>
|
||||||
<button class="tile tilebtn" data-go="reifen:x">
|
<button class="tile tilebtn" data-go="reifen:x">
|
||||||
<span class="label">Reifen · ${r.aktiv} montiert</span>
|
<span class="label">Reifen</span>
|
||||||
<svg class="go" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg>
|
<svg class="go" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg>
|
||||||
<dl class="rows" style="margin-top:10px">
|
<dl class="rows" style="margin-top:10px">
|
||||||
<div class="row"><dt>${esc(a.marke)} ${esc(a.modell)}</dt><dd>${esc(a.mass)}</dd></div>
|
<div class="row"><dt>montiert: ${r.aktiv}räder</dt><dd>${esc(a.mass)}</dd></div>
|
||||||
<div class="row"><dt>Laufleistung</dt><dd>${a.km != null ? de(a.km) + " km" : "unbekannt"}</dd></div>
|
<div class="row"><dt>Laufleistung</dt><dd>${a.km != null ? de(a.km) + " km" : "unbekannt"}</dd></div>
|
||||||
<div class="row" style="border-bottom:0"><dt>Wechseltermin</dt>
|
<div class="row" style="border-bottom:0"><dt>Wechseltermin</dt>
|
||||||
<dd>${dedat(new Date(r.wechsel.datum))}<small>${r.wechsel.vereinbart ? "vereinbart" : "offen"}</small></dd></div>
|
<dd>${dedat(new Date(r.wechsel.datum))}<small>${r.wechsel.vereinbart ? "vereinbart" : "–"}</small></dd></div>
|
||||||
</dl></button>`;
|
</dl></button>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2039,7 +2038,7 @@ function vService() {
|
|||||||
const iv = intervalle(t);
|
const iv = intervalle(t);
|
||||||
if (!iv && !hatMeldung) return `<div class="termingruppe">
|
if (!iv && !hatMeldung) return `<div class="termingruppe">
|
||||||
<div class="tg-kopf">${kopf}</div>
|
<div class="tg-kopf">${kopf}</div>
|
||||||
<div class="tg-wert leer">kein Eintrag im Servicebuch</div>
|
<div class="tg-wert leer">kein Eintrag im Wartungsplan</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
const anzeige = iv ? (iv.hersteller || iv.eigen) : null;
|
const anzeige = iv ? (iv.hersteller || iv.eigen) : null;
|
||||||
// Forecast-Zeile aus dem kundeneigenen Intervall in "Einrichten" mit
|
// Forecast-Zeile aus dem kundeneigenen Intervall in "Einrichten" mit
|
||||||
@@ -2119,13 +2118,13 @@ function vService() {
|
|||||||
href="https://maps.google.com/?q=${encodeURIComponent(sv.autohaus.adresse)}"
|
href="https://maps.google.com/?q=${encodeURIComponent(sv.autohaus.adresse)}"
|
||||||
style="display:block;text-decoration:none;text-align:center">In Karten öffnen</a>
|
style="display:block;text-decoration:none;text-align:center">In Karten öffnen</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile"><span class="label">Servicebuch</span>
|
<div class="tile"><span class="label">Wartungsplan</span>
|
||||||
${sv.buch.length ? sv.buch.map((e, i) => swipeZeile(`sbuch:${i}`, `<button class="leaf" style="padding-left:0" data-go="sbuch:${i}">
|
${sv.buch.length ? sv.buch.map((e, i) => swipeZeile(`sbuch:${i}`, `<button class="leaf" style="padding-left:0" data-go="sbuch:${i}">
|
||||||
<span class="k">${esc(e.art)}<small>${esc(e.werkstatt)}</small></span>
|
<span class="k">${esc(e.art)}<small>${esc(e.werkstatt)}</small></span>
|
||||||
<span class="v">${e.datum}<small>${de(e.km)} km${e.kosten != null ? " · " + eur(e.kosten) + " €" : ""}</small></span>
|
<span class="v">${e.datum}<small>${de(e.km)} km${e.kosten != null ? " · " + eur(e.kosten) + " €" : ""}</small></span>
|
||||||
<svg class="chev" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg></button>`)).join("") : `<div class="leer" style="margin-top:12px">
|
<svg class="chev" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg></button>`)).join("") : `<div class="leer" style="margin-top:12px">
|
||||||
<strong>Noch keine Einträge</strong>
|
<strong>Noch keine Einträge</strong>
|
||||||
<p>Aus dem Servicebuch berechnet die App die Fälligkeit von Inspektion, Ölwechsel und Hauptuntersuchung. Der erste Eintrag ist die Grundlage für jede Prognose.</p>
|
<p>Aus dem Wartungsplan berechnet die App die Fälligkeit von Inspektion, Ölwechsel und Hauptuntersuchung. Der erste Eintrag ist die Grundlage für jede Prognose.</p>
|
||||||
</div>`}
|
</div>`}
|
||||||
<button class="aktion primaer" data-sbneu>Eintrag hinzufügen</button>
|
<button class="aktion primaer" data-sbneu>Eintrag hinzufügen</button>
|
||||||
</div>`;
|
</div>`;
|
||||||
@@ -2653,14 +2652,11 @@ function vEinst() {
|
|||||||
Tabellenprogrammen aufgehen.</span>
|
Tabellenprogrammen aufgehen.</span>
|
||||||
<button class="aktion" data-csv="fahrten">Fahrten als CSV</button>
|
<button class="aktion" data-csv="fahrten">Fahrten als CSV</button>
|
||||||
<button class="aktion" data-csv="tanken">Tankvorgänge als CSV</button>
|
<button class="aktion" data-csv="tanken">Tankvorgänge als CSV</button>
|
||||||
<button class="aktion" data-csv="service">Servicebuch als CSV</button>
|
<button class="aktion" data-csv="service">Wartungsplan als CSV</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile"><span class="label">Version</span>
|
<div class="tile"><span class="label">Version</span>
|
||||||
<dl class="rows">
|
<dl class="rows">
|
||||||
<div class="row"><dt>Dashboard</dt><dd>${esc(CONFIG.version)}</dd></div>
|
<div class="row" style="border-bottom:0"><dt>Installiert</dt><dd>${esc(APP_VERSION || "unbekannt")}<small>Home-Assistant-Integration</small></dd></div>
|
||||||
<div class="row"><dt>Installiert</dt><dd>${esc(APP_VERSION || "unbekannt")}<small>Home-Assistant-Integration</small></dd></div>
|
|
||||||
<div class="row"><dt>Fahrzeugdaten</dt><dd>FMM003<small>Zündung · Batterie</small></dd></div>
|
|
||||||
<div class="row" style="border-bottom:0"><dt>Position</dt><dd>FMM003<small>GPS-Tracker</small></dd></div>
|
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
<div class="tile"><span class="label">Integration-Update</span>
|
<div class="tile"><span class="label">Integration-Update</span>
|
||||||
@@ -2958,9 +2954,9 @@ function vReifen() {
|
|||||||
<button type="button" data-bildersetzen="${radDatei}">Foto ersetzen</button>
|
<button type="button" data-bildersetzen="${radDatei}">Foto ersetzen</button>
|
||||||
<button type="button" class="loeschen" data-bildloeschen="${radDatei}">Löschen</button>
|
<button type="button" class="loeschen" data-bildloeschen="${radDatei}">Löschen</button>
|
||||||
</div>` : ""}
|
</div>` : ""}
|
||||||
<div style="display:flex;justify-content:space-between;align-items:center">
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-top:var(--sp-4)">
|
||||||
<button data-satz="${k === "sommer" ? "Sommer" : "Winter"}" class="montiert ${aktiv ? "on" : ""}">Montiert</button>
|
<button data-satz="${k === "sommer" ? "Sommer" : "Winter"}" class="montiert ${aktiv ? "on" : ""}">Montiert</button>
|
||||||
<button class="iconbtn" data-reifenarchivieren="${k}" aria-label="Neue Räder anlegen">${ciSVG(CI.packageS, 18)}</button>
|
<button class="iconbtn archivbtn" data-reifenarchivieren="${k}" aria-label="Neue Räder anlegen">${ciSVG(CI.packageS, 18)}</button>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top:12px">
|
<div style="margin-top:12px">
|
||||||
${feld(k, "marke", "Hersteller")}
|
${feld(k, "marke", "Hersteller")}
|
||||||
@@ -3418,14 +3414,14 @@ function swipeZeile(id, inhaltHtml) {
|
|||||||
const LOESCH_TEXT = {
|
const LOESCH_TEXT = {
|
||||||
trip: "Fahrt löschen?",
|
trip: "Fahrt löschen?",
|
||||||
fill: "Tankvorgang löschen?",
|
fill: "Tankvorgang löschen?",
|
||||||
sbuch: "Servicebuch-Eintrag löschen?",
|
sbuch: "Wartungsplan-Eintrag löschen?",
|
||||||
bild: "Foto löschen?",
|
bild: "Foto löschen?",
|
||||||
batt: "Messwert löschen?",
|
batt: "Messwert löschen?",
|
||||||
};
|
};
|
||||||
const LOESCH_HINWEIS = {
|
const LOESCH_HINWEIS = {
|
||||||
trip: "Die Fahrt wird aus dem Fahrtenbuch entfernt. Das lässt sich nicht widerrufen.",
|
trip: "Die Fahrt wird aus dem Fahrtenbuch entfernt. Das lässt sich nicht widerrufen.",
|
||||||
fill: "Der Tankvorgang und der zugehörige Beleg werden entfernt. Das lässt sich nicht widerrufen.",
|
fill: "Der Tankvorgang und der zugehörige Beleg werden entfernt. Das lässt sich nicht widerrufen.",
|
||||||
sbuch: "Der Eintrag wird aus dem Servicebuch entfernt. Das lässt sich nicht widerrufen.",
|
sbuch: "Der Eintrag wird aus dem Wartungsplan entfernt. Das lässt sich nicht widerrufen.",
|
||||||
bild: "Das Foto wird vom Server gelöscht. Der Platz bleibt bestehen und kann neu belegt werden.",
|
bild: "Das Foto wird vom Server gelöscht. Der Platz bleibt bestehen und kann neu belegt werden.",
|
||||||
batt: "Der Tageseintrag wird aus der Messwertliste entfernt. Das lässt sich nicht widerrufen.",
|
batt: "Der Tageseintrag wird aus der Messwertliste entfernt. Das lässt sich nicht widerrufen.",
|
||||||
};
|
};
|
||||||
@@ -4077,7 +4073,7 @@ function render() {
|
|||||||
else if (route.name === "werkstatt") { head = ["Service", "Autohaus"]; v.innerHTML = vWerkstatt(); }
|
else if (route.name === "werkstatt") { head = ["Service", "Autohaus"]; v.innerHTML = vWerkstatt(); }
|
||||||
else if (route.name === "battverlauf") { head = ["Fahrzeug", "Batteriespannung"]; v.innerHTML = vBatterieverlauf(); bvZeichnen(); }
|
else if (route.name === "battverlauf") { head = ["Fahrzeug", "Batteriespannung"]; v.innerHTML = vBatterieverlauf(); bvZeichnen(); }
|
||||||
else if (route.name === "battliste") { head = ["Batteriespannung", "Messwerte"]; v.innerHTML = vBatterieliste(); }
|
else if (route.name === "battliste") { head = ["Batteriespannung", "Messwerte"]; v.innerHTML = vBatterieliste(); }
|
||||||
else if (route.name === "sbuch") { head = ["Servicebuch", "Eintrag"]; v.innerHTML = vSbuch(); }
|
else if (route.name === "sbuch") { head = ["Wartungsplan", "Eintrag"]; v.innerHTML = vSbuch(); }
|
||||||
else if (route.name === "standort") { head = ["Fahrzeug", "Standort"]; v.innerHTML = vStandort(); }
|
else if (route.name === "standort") { head = ["Fahrzeug", "Standort"]; v.innerHTML = vStandort(); }
|
||||||
else { const t = TABS.find((x) => x.id === route.name); head = [t.eyebrow, t.id === "audi" ? "Mein Audi" : t.title];
|
else { const t = TABS.find((x) => x.id === route.name); head = [t.eyebrow, t.id === "audi" ? "Mein Audi" : t.title];
|
||||||
v.innerHTML = { audi: vAudi, trips: vTrips, stat: vStat, fuel: vFuel }[route.name](); }
|
v.innerHTML = { audi: vAudi, trips: vTrips, stat: vStat, fuel: vFuel }[route.name](); }
|
||||||
|
|||||||
@@ -182,6 +182,12 @@
|
|||||||
}
|
}
|
||||||
.iconbtn:hover { background: var(--tile-2); color: var(--fg); }
|
.iconbtn:hover { background: var(--tile-2); color: var(--fg); }
|
||||||
.iconbtn:active { background: var(--tile-2); color: var(--fg); transform: scale(.92); }
|
.iconbtn:active { background: var(--tile-2); color: var(--fg); transform: scale(.92); }
|
||||||
|
/* "Neue Räder anlegen" auf der Reifen-Seite: bewusst dauerhaft mit
|
||||||
|
sichtbarem Hintergrund statt nur bei Hover/Tap wie .iconbtn sonst - sitzt
|
||||||
|
direkt neben dem gefüllten "Montiert"-Knopf und wirkt ohne eigene Fläche
|
||||||
|
zu unscheinbar, um dort als eigenständiger Knopf erkannt zu werden. */
|
||||||
|
.archivbtn { background: var(--tile-2); color: var(--fg2); }
|
||||||
|
.archivbtn:hover, .archivbtn:active { color: var(--fg); }
|
||||||
/* D4 war urspruenglich: die Ringe sind Marke, kein Knopf. Seit die
|
/* D4 war urspruenglich: die Ringe sind Marke, kein Knopf. Seit die
|
||||||
iOS-Auflage in Telefonbreite das Zahnrad ausblendet, sind sie dort aber der
|
iOS-Auflage in Telefonbreite das Zahnrad ausblendet, sind sie dort aber der
|
||||||
einzige Zugang zu den Einstellungen - und als reines div mit
|
einzige Zugang zu den Einstellungen - und als reines div mit
|
||||||
@@ -1131,7 +1137,7 @@ button.leaf:active { background: var(--tile-2); }
|
|||||||
überlagert (das saß nur dort, solange Überschrift+km diesen Platz noch
|
überlagert (das saß nur dort, solange Überschrift+km diesen Platz noch
|
||||||
nicht beanspruchten). */
|
nicht beanspruchten). */
|
||||||
.montiert {
|
.montiert {
|
||||||
position: relative; margin-top: var(--sp-4);
|
position: relative;
|
||||||
display: inline-flex; align-items: center; justify-content: center;
|
display: inline-flex; align-items: center; justify-content: center;
|
||||||
font-family: inherit; font-size: 12px; letter-spacing: normal;
|
font-family: inherit; font-size: 12px; letter-spacing: normal;
|
||||||
color: var(--fg2);
|
color: var(--fg2);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"domain": "audi_dashboard",
|
"domain": "audi_dashboard",
|
||||||
"name": "Audi Dashboard",
|
"name": "Audi Dashboard",
|
||||||
"version": "2026.8.27.22",
|
"version": "2026.8.28.1",
|
||||||
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
|
||||||
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
|
||||||
"codeowners": ["@paul"],
|
"codeowners": ["@paul"],
|
||||||
|
|||||||
Reference in New Issue
Block a user