Strecken gestaffelt anzeigen (2026.9.1.18)

Die gewonnene Genauigkeit kam nicht an: de() formatiert ohne Nachkommastelle,
aus 6,896 km wurde in beiden Oberflaechen "7 km". Den ganzen Tag auf zehn Meter
genau gemessen und im letzten Schritt weggerundet.

Staffelung, vom Eigentuemer festgelegt: unter 1 km Meter ("400 m"), bis
99,9 km eine Nachkommastelle ("6,9 km"), ab 100 km ganze Kilometer ("104 km").
streckeTeile()/streckeText() in beiden Codebasen, wortgleich.

Gestaffelt wird nach dem GERUNDETEN Wert: 0,9996 km sind gerundet 1000 m und
gehoeren in die km-Stufe, sonst stuende dort "1.000 m". streckeTeile() gibt
Wert und Einheit getrennt zurueck, weil die Detailansicht beide in getrennten
Elementen setzt - ohne das stuende unter "400" weiterhin "km".

Im Panel heisst die Funktion streckeText, nicht strecke: den Namen gibt es dort
schon fuer die Ortsangaben einer Fahrt. node --check hat die Kollision gefunden.

Angewandt auf Einzelfahrt und Jahres-/Monatssummen. Nicht auf Kilometerstaende,
Serviceintervalle oder Reichweite - das sind Zaehlerstaende und Prognosen.

VERIFIZIERT: 12 Faelle gegen die Staffelung samt beider Grenzen, 165/165 Tests
gruen, Panel als Modul, tsc --noEmit und vite build sauber, im Browser
nachgesehen: die Fahrt vom 01.09. steht jetzt mit 6,9 km statt "7 km".

Die angepasste Testerwartung ist selbst ein Beleg: sie verlangte "43 km" fuer
eine Summe von 42,5 km - die alte Anzeige rundete schon dort, wo es etwas zu
zeigen gab.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 19:29:19 +02:00
parent 9d70192da3
commit 9a4999ccbc
9 changed files with 118 additions and 18 deletions
@@ -1 +1 @@
{"version":"2026.9.1.17","sha256":"96fa9737a475fe862c2dd309490f687a3e3e4088a8f356acd3f5c718cc919306","bytes":265731,"gebaut":"2026-09-01T17:08:45Z"}
{"version":"2026.9.1.18","sha256":"9dcad77b962e5ef55446d70031249261f850df3e12077bce31656bdf247dd6fe","bytes":265893,"gebaut":"2026-09-01T17:27:43Z"}
@@ -124,6 +124,38 @@ const $ = (s) => ROOT.querySelector(s);
/* ---------------------------------------------------------------- Format */
const de = (n, d = 0) => Number(n ?? 0).toLocaleString("de-DE", { minimumFractionDigits: d, maximumFractionDigits: d });
/* Eine gefahrene Strecke in der Staffelung, die der Eigentuemer am 01.09.2026
festgelegt hat:
unter 1 km -> Meter, ganzzahlig ("400 m", "999 m")
bis 99,9 km -> eine Nachkommastelle ("1,0 km", "6,9 km")
ab 100 km -> ganze Kilometer ("104 km")
Der Grund fuer die Staffelung: seit der GNSS-Verfeinerung messen wir auf
etwa zehn Meter genau (Fahrt vom 01.09.: 5,015 km GNSS gegen 5,000 km am
Tacho, 0,3 %). Ohne Nachkommastelle wurde davon nichts sichtbar - aus
6,896 km wurde in der Anzeige schlicht "7 km". Umgekehrt waere eine
Nachkommastelle bei dreistelligen Summen nur Unruhe.
Gestaffelt wird nach dem GERUNDETEN Wert, nicht nach dem rohen: 0,9996 km
sind gerundet 1000 m und gehoeren damit schon in die km-Stufe, sonst stuende
dort "1.000 m". Dasselbe an der Grenze zu 100. */
function streckeTeile(km) {
const n = Number(km);
if (km == null || !Number.isFinite(n)) return null;
const meter = Math.round(n * 1000);
if (Math.abs(meter) < 1000) return { wert: de(meter), einheit: "m" };
const fein = Math.round(n * 10) / 10;
if (Math.abs(fein) < 100) return { wert: de(fein, 1), einheit: "km" };
return { wert: de(Math.round(n)), einheit: "km" };
}
// Der Name streckeText, nicht strecke: strecke(t) gibt es bereits fuer die
// Ortsangaben einer Fahrt ("Ingolstadt -> Muenchen").
const streckeText = (km, ersatz = "") => {
const t = streckeTeile(km);
return t ? `${t.wert} ${t.einheit}` : ersatz;
};
/* Preis je Liter, oder null.
Die Kosten muessen ausdruecklich geprueft werden, nicht nur die Liter: bei
@@ -1430,7 +1462,7 @@ function teaser() {
return `<div class="tile teaser"><span class="label">Zuletzt</span>
${t ? `<button class="leaf" style="padding-left:0" data-go="trip:${t.trip_id}">
<span class="k">Letzte Fahrt<small>${esc(t.start_address || "")} ${esc(t.end_address || "")}</small></span>
<span class="v">${t.distance_km != null ? de(t.distance_km) + " km" : "offen"}<small>${t.status}</small></span>
<span class="v">${streckeText(t.distance_km, "offen")}<small>${t.status}</small></span>
<svg class="chev" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg></button>` : ""}
${f ? `<button class="leaf" style="padding-left:0;border-bottom:0" data-go="fill:${f.tank_id}">
<span class="k">Letzter Tankvorgang<small>${esc(f.station_name || "")}</small></span>
@@ -3549,19 +3581,19 @@ function vTrips() {
jahre.forEach((j, ji) => {
const tj = neuesteZuerst(TRIPS.filter((t) => new Date(t.ts_start).getFullYear() === j), "ts_start");
h += `<button class="acc-h" aria-expanded="${ji === 0}" data-acc="y${j}"><span class="mark"></span>
<span class="k">${j}</span><span class="v">${de(sum(tj, (t) => t.distance_km || 0))} km<br>${tj.length} Fahrten</span></button>
<span class="k">${j}</span><span class="v">${streckeText(sum(tj, (t) => t.distance_km || 0))}<br>${tj.length} Fahrten</span></button>
<div class="acc-b ${ji === 0 ? "on" : ""}" id="y${j}">`;
const monate = [...new Set(tj.map((t) => new Date(t.ts_start).toLocaleDateString("de-DE", { month: "long" })))];
monate.forEach((m, mi) => {
const tm = tj.filter((t) => new Date(t.ts_start).toLocaleDateString("de-DE", { month: "long" }) === m);
h += `<div class="lvl2"><button class="acc-h" aria-expanded="${mi === 0}" data-acc="m${j}${m}">
<span class="mark"></span><span class="k">${m}</span>
<span class="v">${de(sum(tm, (t) => t.distance_km || 0))} km</span></button>
<span class="v">${streckeText(sum(tm, (t) => t.distance_km || 0))}</span></button>
<div class="acc-b ${mi === 0 ? "on" : ""}" id="m${j}${m}">`;
tm.forEach((t) => { h += swipeZeile(`trip:${t.trip_id}`, `<button class="leaf" data-go="trip:${t.trip_id}">
<span class="k">${strecke(t)}
<small>${dedat(new Date(t.ts_start))} · ${artText(t.art)}</small></span>
<span class="v">${t.distance_km != null ? de(t.distance_km) + " km" : "offen"}<small>${fahrtVerbrauch(t) != null ? de(fahrtVerbrauch(t), 1) + " l/100 km" : t.status}</small></span>
<span class="v">${streckeText(t.distance_km, "offen")}<small>${fahrtVerbrauch(t) != null ? de(fahrtVerbrauch(t), 1) + " l/100 km" : t.status}</small></span>
<svg class="chev" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg></button>`); });
h += `</div></div>`;
});
@@ -3588,7 +3620,7 @@ function vTrip(id) {
${mapBox()}
<div class="tile flat" style="margin:18px 0 14px">
<span class="label">Distanz</span>
<div class="fig" style="font-size:52px;margin-top:10px">${t.distance_km != null ? de(t.distance_km) : ""}<span class="unit">km</span></div>
<div class="fig" style="font-size:52px;margin-top:10px">${(streckeTeile(t.distance_km) || { wert: "", einheit: "km" }).wert}<span class="unit">${(streckeTeile(t.distance_km) || { einheit: "km" }).einheit}</span></div>
<div style="margin-top:15px">
<button class="pill" data-art="${t.trip_id}"
style="cursor:pointer;font-family:inherit">${artText(t.art)}</button></div>
@@ -1,7 +1,7 @@
{
"domain": "audi_dashboard",
"name": "Audi Dashboard",
"version": "2026.9.1.17",
"version": "2026.9.1.18",
"documentation": "https://gitea.nothaft.cloud/paul/audi-app/src/branch/main/README.md",
"issue_tracker": "https://gitea.nothaft.cloud/paul/audi-app/issues",
"codeowners": ["@paul"],