Fahrt-Formular: Ankunftszeit statt Dauer, Art gross, neutrale Pille

Fuenf gemeldete Punkte plus ein Theme-Bug beim Nachpruefen:

- "Dauer" ist kein Eingabefeld mehr - stattdessen Ankunftszeit, die Dauer
  wird aus Start/Ankunft berechnet und live angezeigt (fahrtZeitraum()/
  dauerText()), inklusive Fahrten ueber Mitternacht.
- Art-Werte werden grossgeschrieben angezeigt (Privat/Arbeitsweg), der
  gespeicherte Wert bleibt klein (artText()-Helfer).
- Arbeitsweg-Pille ist nicht mehr rot - sieht jetzt aus wie Privat.
- "tippen zum Umschalten" unter der Pille entfernt.
- Bild-Platzhalter (Einstellungen) war im Tag-Theme unsichtbar: --tile wird
  dort vom iOS-Overlay auf reines Weiss gesetzt, identisch mit Canvas/
  Kacheln. Auf --ios-fill umgestellt, in beiden Themes sichtbar grau.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 11:49:55 +02:00
parent 2d76f10c1c
commit 126033e4dd
9 changed files with 191 additions and 37 deletions
+45 -1
View File
@@ -38,7 +38,11 @@ route — see section C; a twelfth round the same day removed `HECKKLAPPENSCHLOS
half-cut-off image placeholder, gave the Inspektion its own "voraussichtlich am …" forecast, made
trips fully editable (new `audi_dashboard_fahrt_aktualisieren`, "Neue Fahrt" now offers every field
the detail page shows), aligned the page title with the tiles on phone width, and closed two
dead-ends in the bootstrap retry loop behind the reported hanging "Lädt …" screen).
dead-ends in the bootstrap retry loop behind the reported hanging "Lädt …" screen; a thirteenth
round the same day replaced the trip form's editable "Dauer" with Start-/Ankunftszeit (duration is
now computed, not entered), capitalized the Art labels shown to the user, dropped the red styling
and hint text from the Arbeitsweg pill, and fixed a daylight-mode-only regression from round twelve
where the image placeholder's grey background had turned invisible-white).
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.
@@ -1278,6 +1282,46 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed
war jedes Mal binnen ~2s da) - die Sackgassen sind im Code belegt, dass sie *die* Ursache
beim Nutzer waren, ist damit aber nicht bewiesen. Tritt es erneut auf: Browser-Konsole
erfragen, bevor weiter geraten wird.
- [x] Thirteenth round (2026-08-17, same day) — five more user-reported items on the trip form and
one theme bug found while verifying it:
(1) **"Dauer" replaced by "Ankunftszeit"; duration is no longer editable.** `fahrtFelder()`
now has Startzeit + Ankunftszeit inputs and a plain (non-input) duration display next to
them, computed by `fahrtZeitraum()`/`dauerText()` and kept live via a new
`data-fahrtzeitfeld` input handler (same "patch the one node, don't call render()" pattern
the existing `data-tankpreisfeld` handler uses, for the same reason: a full re-render would
steal focus from the field being typed in). An arrival time not later than the start time is
treated as crossing midnight (`+1 Tag`), matching how `_fahrt_beenden()` already handles a
real overnight trip. `fahrtFormularWerte()` derives `ts_start`/`ts_end` from the same
calculation - the backend never sees a separate duration field, so it can't disagree with
the times. Verified live: 09:00→10:45 shows "1 Std. 45 Min.", 23:30→00:15 shows "45 Min."
(crossed midnight correctly), and editing an existing trip's arrival time on the Einzelfahrt
page persisted the recalculated `duration_s` (7200s for a 10:00→12:00 edit, checked in
`fahrten.jsonl`).
(2) **Art values now display capitalized** ("Privat"/"Arbeitsweg") wherever shown to the user
- the trip-edit `<select>`, the trip-list row, and the Einzelfahrt pill - via a new
`artText()` helper; the stored value stays lowercase (`"privat"`/`"arbeitsweg"`), since it's
compared in several places (the pill, the "Arbeitsweg" statistic, the CSV export) and already
exists that way in every trip recorded so far. The CSV export itself was deliberately left
showing the raw value - a data file, not UI copy.
(3) **Arbeitsweg pill no longer red.** `.pill.work` (`color:var(--red)` / a translucent red
fill in the iOS overlay) is gone from both stylesheets along with the now-unused `work` class
on the button; the pill uses the same neutral styling regardless of Art, matching the request
that Arbeitsweg should look like Privat, not draw attention to itself.
(4) **"tippen zum Umschalten" hint removed** from under the Einzelfahrt pill, per explicit
request - the pill is still clickable, just without the caption.
(5) **Found while re-verifying the placeholder fix from round twelve: it broke daylight mode.**
`.bildbox.bild-fehlt .platzhalter{background:var(--tile)}` (added round twelve) reads fine in
Nacht theme, where `--tile` is a faint white overlay on a near-black canvas - but the iOS
overlay's Tag-theme block redefines `--tile:#FFFFFF`, identical to the canvas/most tiles
around it, so the placeholder box became invisible in daylight mode (measured live:
`background-color: rgb(255,255,255)` on both the placeholder and its surroundings). Switched
to `--ios-fill` (`rgba(120,120,128,.16)` in Tag theme, `.24` in Nacht) - the same token
`.feld select`/`.aktion` already use to mark "this is a surface", visibly grey in both themes.
Verified live in Tag theme: `background-color: rgba(120,120,128,.16)`, clearly a distinct box
against the white canvas (see screenshot taken during verification).
All five deployed to `audi_ha_test` (version `1787009000`) and synced to `installationspaket/`;
console showed only the pre-existing service-worker/404 noise from the missing vehicle photos,
nothing new.
- [ ] 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`
@@ -2473,7 +2473,38 @@ function vReifen() {
Felder decken genau das ab, was die Einzelfahrt-Seite anzeigt; Verbrauch,
Status und km-Quelle bleiben außen vor, die meldet das Fahrzeug bzw. leitet
das Backend ab. */
/* Gespeichert bleibt der kleingeschriebene Schlüssel - er wird an mehreren
Stellen verglichen (Pille auf der Einzelfahrt, Statistik "Arbeitsweg", CSV)
und steht so auch in allen bereits erfassten Fahrten. Angezeigt wird er
groß, deshalb artText() statt eines geänderten Werts. */
const FAHRT_ARTEN = ["privat", "arbeitsweg"];
const artText = (a) => (a ? a.charAt(0).toUpperCase() + a.slice(1) : "");
/* "1 Std. 15 Min." statt "75 min" - bei mehrstündigen Fahrten liest sich die
reine Minutenzahl schlecht. */
function dauerText(sekunden) {
if (sekunden == null || !isFinite(sekunden)) return "";
const min = Math.max(0, Math.round(sekunden / 60));
const std = Math.floor(min / 60);
return std ? `${std} Std. ${min % 60} Min.` : `${min} Min.`;
}
/* Start und Ankunft eines Formulars als echte Zeitpunkte. Liegt die Ankunft
vor der Abfahrt, wurde über Mitternacht gefahren - dann zählt der Folgetag.
null, solange Datum oder eine der beiden Uhrzeiten fehlt. */
function fahrtZeitraum(praefix) {
const datum = $(`#${praefix}Datum`).value, ab = $(`#${praefix}Start`).value, an = $(`#${praefix}Ende`).value;
if (!datum || !ab || !an) return null;
const start = new Date(`${datum}T${ab}:00`);
let ende = new Date(`${datum}T${an}:00`);
if (isNaN(start) || isNaN(ende)) return null;
if (ende <= start) ende = new Date(ende.getTime() + dTag);
return { start, ende };
}
function fahrtDauerText(praefix) {
const z = fahrtZeitraum(praefix);
return z ? dauerText((z.ende - z.start) / 1000) : "";
}
/* Datum für ein <input type="date"> in Ortszeit. Bewusst nicht über
toISOString(), das rechnet nach UTC um und liefert kurz nach Mitternacht
@@ -2489,16 +2520,30 @@ function zeitfeld(d) {
function fahrtFelder(praefix, t) {
const start = t && t.ts_start ? new Date(t.ts_start) : new Date();
const dauer = t && t.duration_s != null ? Math.round(t.duration_s / 60) : "";
const ende = t && t.ts_end ? new Date(t.ts_end) : null;
const art = t && t.art ? t.art : FAHRT_ARTEN[0];
const wert = (v) => (v == null ? "" : v);
const startzeit = t ? zeitfeld(start) : "08:00", ankunftzeit = ende ? zeitfeld(ende) : "08:30";
// Die Dauer ist bewusst kein Eingabefeld: sie ergibt sich aus Start und
// Ankunft und würde sich sonst mit ihnen widersprechen können. Aus
// t.duration_s statt neu berechnet, damit eine über Mitternacht laufende
// bestehende Fahrt (Ankunft "kleiner" als Start) korrekt bleibt; bei einer
// neuen Fahrt (t === null) ergibt sich der Startwert aus denselben
// Vorgabezeiten wie die Felder selbst. Der data-fahrtzeitfeld-Handler
// rechnet bei jeder Änderung neu.
let dauerSekunden = t && t.duration_s != null ? t.duration_s : null;
if (dauerSekunden == null) {
const [sh, sm] = startzeit.split(":").map(Number), [eh, em] = ankunftzeit.split(":").map(Number);
dauerSekunden = ((eh * 60 + em) - (sh * 60 + sm) + (eh * 60 + em <= sh * 60 + sm ? 1440 : 0)) * 60;
}
const dauer = dauerText(dauerSekunden);
return `
<div class="feld"><label for="${praefix}Datum">Datum</label><input type="date" id="${praefix}Datum" value="${datumsfeld(start)}"></div>
<div class="feld"><label for="${praefix}Start">Startzeit</label><input type="time" id="${praefix}Start" value="${t ? zeitfeld(start) : "08:00"}"></div>
<div class="feld"><label for="${praefix}Dauer">Dauer</label>
<span class="mitEinheit"><input type="number" min="0" id="${praefix}Dauer" value="${dauer}" placeholder="0"><span>min</span></span></div>
<div class="feld"><label for="${praefix}Datum">Datum</label><input type="date" id="${praefix}Datum" value="${datumsfeld(start)}" data-fahrtzeitfeld="${praefix}"></div>
<div class="feld"><label for="${praefix}Start">Startzeit</label><input type="time" id="${praefix}Start" value="${startzeit}" data-fahrtzeitfeld="${praefix}"></div>
<div class="feld"><label for="${praefix}Ende">Ankunftszeit</label><input type="time" id="${praefix}Ende" value="${ankunftzeit}" data-fahrtzeitfeld="${praefix}"></div>
<div class="feld"><label>Dauer</label><span id="${praefix}DauerAnzeige" style="color:var(--fg2)">${dauer}</span></div>
<div class="feld"><label for="${praefix}Art">Art</label>
<select id="${praefix}Art">${FAHRT_ARTEN.map((a) => `<option ${a === art ? "selected" : ""}>${a}</option>`).join("")}</select></div>
<select id="${praefix}Art">${FAHRT_ARTEN.map((a) => `<option value="${a}" ${a === art ? "selected" : ""}>${artText(a)}</option>`).join("")}</select></div>
<div class="feld"><label for="${praefix}Startort">Startort</label><input id="${praefix}Startort" value="${esc(wert(t && t.start_address))}" placeholder="unbekannt"></div>
<div class="feld"><label for="${praefix}Zielort">Zielort</label><input id="${praefix}Zielort" value="${esc(wert(t && t.end_address))}" placeholder="unbekannt"></div>
<div class="feld"><label for="${praefix}OdoStart">Startkilometer</label><input type="number" id="${praefix}OdoStart" value="${wert(t && t.odo_start)}" placeholder="wird ergänzt"></div>
@@ -2510,11 +2555,10 @@ function fahrtFelder(praefix, t) {
function fahrtFormularWerte(praefix) {
const zahl = (id) => { const v = $(`#${praefix}${id}`).value; return v === "" ? null : parseFloat(v.replace(",", ".")); };
const text = (id) => $(`#${praefix}${id}`).value.trim() || null;
const dauer = parseInt($(`#${praefix}Dauer`).value, 10) || 0;
const tsStart = new Date(`${$(`#${praefix}Datum`).value}T${$(`#${praefix}Start`).value}:00`);
const z = fahrtZeitraum(praefix);
return {
ts_start: tsStart.toISOString(),
ts_end: new Date(tsStart.getTime() + dauer * 60000).toISOString(),
ts_start: z ? z.start.toISOString() : null,
ts_end: z ? z.ende.toISOString() : null,
art: $(`#${praefix}Art`).value,
start_ort: text("Startort"), ziel_ort: text("Zielort"),
odo_start: zahl("OdoStart"), odo_end: zahl("OdoEnde"), distanz: zahl("Distanz"),
@@ -2556,7 +2600,7 @@ function vTrips() {
<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))} · ${esc(t.art)}</small></span>
<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>
<svg class="chev" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg></button>`); });
h += `</div></div>`;
@@ -2586,9 +2630,8 @@ function vTrip(id) {
<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 style="margin-top:15px">
<button class="pill ${t.art === "arbeitsweg" ? "work" : ""}" data-art="${t.trip_id}"
style="cursor:pointer;font-family:inherit">${esc(t.art)}</button></div>
<span class="label" style="margin-top:12px">tippen zum Umschalten</span>
<button class="pill" data-art="${t.trip_id}"
style="cursor:pointer;font-family:inherit">${artText(t.art)}</button></div>
</div>
<div class="tile"><dl class="rows">
<div class="row"><dt>Startort</dt><dd>${esc(t.start_address || "unbekannt")}<small>${dedat(new Date(t.ts_start))}</small></dd></div>
@@ -4128,6 +4171,15 @@ function ereignisseVerdrahten() {
if (liste) liste.innerHTML = entitaetZeilenMarkup(sucheFeld, key, setupSuchtext, wert);
return;
}
// Gleicher Grund wie beim tankpreisfeld-Handler darunter: nur die
// Anzeige patchen, kein render() - das würde das gerade bearbeitete Feld
// neu erzeugen und den Fokus verlieren.
const zeitfeldPraefix = e.target.dataset.fahrtzeitfeld;
if (zeitfeldPraefix) {
const anzeige = ROOT.getElementById(`${zeitfeldPraefix}DauerAnzeige`);
if (anzeige) anzeige.textContent = fahrtDauerText(zeitfeldPraefix);
return;
}
const praefix = e.target.dataset.tankpreisfeld;
if (!praefix) return;
const liter = parseFloat((ROOT.getElementById(`${praefix}Liter`).value || "0").replace(",", "."));
@@ -196,7 +196,6 @@ main#view{--seitenrand:16px;padding:0 var(--seitenrand) 30px;scroll-behavior:smo
.pill{font-size:12px;letter-spacing:.01em;text-transform:none;padding:5px 11px;
border:0;background:var(--ios-fill);color:var(--fg2)}
.pill.work{background:rgba(245,5,55,.16);color:var(--ios-tint)}
.status{padding:14px 0 2px}
.status .t{font-size:16px}
@@ -333,7 +332,12 @@ main#view{--seitenrand:16px;padding:0 var(--seitenrand) 30px;scroll-behavior:smo
/* Fahrzeugbild randlos wie in einer Fahrzeug-App: der Platzhalter bleibt
sichtbar, aber ohne technischen Strichrahmen. */
.szene{margin-bottom:20px}
.bildbox.bild-fehlt .platzhalter{border:0;background:var(--tile);border-radius:var(--r-tile)}
/* --tile statt --ios-fill waere hier naheliegend, faellt im Tag-Theme aber
auf reines Weiss (#FFFFFF) - ununterscheidbar von Canvas und den meisten
Kacheln drumherum, die Box verschwand. --ios-fill ist in beiden Themes ein
deutlich sichtbares transluzentes Grau, dieselbe Konvention wie
.feld select/.aktion ("das hier ist eine Flaeche"). */
.bildbox.bild-fehlt .platzhalter{border:0;background:var(--ios-fill);border-radius:var(--r-tile)}
.platzhalter-datei{display:none}
/* Zeilen mit Wert plus Statuspunkt rechts (Sicherheitscheck) ruhiger setzen */
@@ -1 +1 @@
{"version": 1787000000}
{"version": 1787009000}
@@ -676,7 +676,6 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
border-radius: var(--r-pill);
padding: 6px 12px;
}
.pill.work { color: var(--red); border-color: var(--red); }
/* ------------------------------------------------------------ Akkordeon */
.acc-h {
+66 -14
View File
@@ -2473,7 +2473,38 @@ function vReifen() {
Felder decken genau das ab, was die Einzelfahrt-Seite anzeigt; Verbrauch,
Status und km-Quelle bleiben außen vor, die meldet das Fahrzeug bzw. leitet
das Backend ab. */
/* Gespeichert bleibt der kleingeschriebene Schlüssel - er wird an mehreren
Stellen verglichen (Pille auf der Einzelfahrt, Statistik "Arbeitsweg", CSV)
und steht so auch in allen bereits erfassten Fahrten. Angezeigt wird er
groß, deshalb artText() statt eines geänderten Werts. */
const FAHRT_ARTEN = ["privat", "arbeitsweg"];
const artText = (a) => (a ? a.charAt(0).toUpperCase() + a.slice(1) : "");
/* "1 Std. 15 Min." statt "75 min" - bei mehrstündigen Fahrten liest sich die
reine Minutenzahl schlecht. */
function dauerText(sekunden) {
if (sekunden == null || !isFinite(sekunden)) return "";
const min = Math.max(0, Math.round(sekunden / 60));
const std = Math.floor(min / 60);
return std ? `${std} Std. ${min % 60} Min.` : `${min} Min.`;
}
/* Start und Ankunft eines Formulars als echte Zeitpunkte. Liegt die Ankunft
vor der Abfahrt, wurde über Mitternacht gefahren - dann zählt der Folgetag.
null, solange Datum oder eine der beiden Uhrzeiten fehlt. */
function fahrtZeitraum(praefix) {
const datum = $(`#${praefix}Datum`).value, ab = $(`#${praefix}Start`).value, an = $(`#${praefix}Ende`).value;
if (!datum || !ab || !an) return null;
const start = new Date(`${datum}T${ab}:00`);
let ende = new Date(`${datum}T${an}:00`);
if (isNaN(start) || isNaN(ende)) return null;
if (ende <= start) ende = new Date(ende.getTime() + dTag);
return { start, ende };
}
function fahrtDauerText(praefix) {
const z = fahrtZeitraum(praefix);
return z ? dauerText((z.ende - z.start) / 1000) : "";
}
/* Datum für ein <input type="date"> in Ortszeit. Bewusst nicht über
toISOString(), das rechnet nach UTC um und liefert kurz nach Mitternacht
@@ -2489,16 +2520,30 @@ function zeitfeld(d) {
function fahrtFelder(praefix, t) {
const start = t && t.ts_start ? new Date(t.ts_start) : new Date();
const dauer = t && t.duration_s != null ? Math.round(t.duration_s / 60) : "";
const ende = t && t.ts_end ? new Date(t.ts_end) : null;
const art = t && t.art ? t.art : FAHRT_ARTEN[0];
const wert = (v) => (v == null ? "" : v);
const startzeit = t ? zeitfeld(start) : "08:00", ankunftzeit = ende ? zeitfeld(ende) : "08:30";
// Die Dauer ist bewusst kein Eingabefeld: sie ergibt sich aus Start und
// Ankunft und würde sich sonst mit ihnen widersprechen können. Aus
// t.duration_s statt neu berechnet, damit eine über Mitternacht laufende
// bestehende Fahrt (Ankunft "kleiner" als Start) korrekt bleibt; bei einer
// neuen Fahrt (t === null) ergibt sich der Startwert aus denselben
// Vorgabezeiten wie die Felder selbst. Der data-fahrtzeitfeld-Handler
// rechnet bei jeder Änderung neu.
let dauerSekunden = t && t.duration_s != null ? t.duration_s : null;
if (dauerSekunden == null) {
const [sh, sm] = startzeit.split(":").map(Number), [eh, em] = ankunftzeit.split(":").map(Number);
dauerSekunden = ((eh * 60 + em) - (sh * 60 + sm) + (eh * 60 + em <= sh * 60 + sm ? 1440 : 0)) * 60;
}
const dauer = dauerText(dauerSekunden);
return `
<div class="feld"><label for="${praefix}Datum">Datum</label><input type="date" id="${praefix}Datum" value="${datumsfeld(start)}"></div>
<div class="feld"><label for="${praefix}Start">Startzeit</label><input type="time" id="${praefix}Start" value="${t ? zeitfeld(start) : "08:00"}"></div>
<div class="feld"><label for="${praefix}Dauer">Dauer</label>
<span class="mitEinheit"><input type="number" min="0" id="${praefix}Dauer" value="${dauer}" placeholder="0"><span>min</span></span></div>
<div class="feld"><label for="${praefix}Datum">Datum</label><input type="date" id="${praefix}Datum" value="${datumsfeld(start)}" data-fahrtzeitfeld="${praefix}"></div>
<div class="feld"><label for="${praefix}Start">Startzeit</label><input type="time" id="${praefix}Start" value="${startzeit}" data-fahrtzeitfeld="${praefix}"></div>
<div class="feld"><label for="${praefix}Ende">Ankunftszeit</label><input type="time" id="${praefix}Ende" value="${ankunftzeit}" data-fahrtzeitfeld="${praefix}"></div>
<div class="feld"><label>Dauer</label><span id="${praefix}DauerAnzeige" style="color:var(--fg2)">${dauer}</span></div>
<div class="feld"><label for="${praefix}Art">Art</label>
<select id="${praefix}Art">${FAHRT_ARTEN.map((a) => `<option ${a === art ? "selected" : ""}>${a}</option>`).join("")}</select></div>
<select id="${praefix}Art">${FAHRT_ARTEN.map((a) => `<option value="${a}" ${a === art ? "selected" : ""}>${artText(a)}</option>`).join("")}</select></div>
<div class="feld"><label for="${praefix}Startort">Startort</label><input id="${praefix}Startort" value="${esc(wert(t && t.start_address))}" placeholder="unbekannt"></div>
<div class="feld"><label for="${praefix}Zielort">Zielort</label><input id="${praefix}Zielort" value="${esc(wert(t && t.end_address))}" placeholder="unbekannt"></div>
<div class="feld"><label for="${praefix}OdoStart">Startkilometer</label><input type="number" id="${praefix}OdoStart" value="${wert(t && t.odo_start)}" placeholder="wird ergänzt"></div>
@@ -2510,11 +2555,10 @@ function fahrtFelder(praefix, t) {
function fahrtFormularWerte(praefix) {
const zahl = (id) => { const v = $(`#${praefix}${id}`).value; return v === "" ? null : parseFloat(v.replace(",", ".")); };
const text = (id) => $(`#${praefix}${id}`).value.trim() || null;
const dauer = parseInt($(`#${praefix}Dauer`).value, 10) || 0;
const tsStart = new Date(`${$(`#${praefix}Datum`).value}T${$(`#${praefix}Start`).value}:00`);
const z = fahrtZeitraum(praefix);
return {
ts_start: tsStart.toISOString(),
ts_end: new Date(tsStart.getTime() + dauer * 60000).toISOString(),
ts_start: z ? z.start.toISOString() : null,
ts_end: z ? z.ende.toISOString() : null,
art: $(`#${praefix}Art`).value,
start_ort: text("Startort"), ziel_ort: text("Zielort"),
odo_start: zahl("OdoStart"), odo_end: zahl("OdoEnde"), distanz: zahl("Distanz"),
@@ -2556,7 +2600,7 @@ function vTrips() {
<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))} · ${esc(t.art)}</small></span>
<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>
<svg class="chev" viewBox="0 0 6 10"><polyline points="1,1 5,5 1,9"/></svg></button>`); });
h += `</div></div>`;
@@ -2586,9 +2630,8 @@ function vTrip(id) {
<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 style="margin-top:15px">
<button class="pill ${t.art === "arbeitsweg" ? "work" : ""}" data-art="${t.trip_id}"
style="cursor:pointer;font-family:inherit">${esc(t.art)}</button></div>
<span class="label" style="margin-top:12px">tippen zum Umschalten</span>
<button class="pill" data-art="${t.trip_id}"
style="cursor:pointer;font-family:inherit">${artText(t.art)}</button></div>
</div>
<div class="tile"><dl class="rows">
<div class="row"><dt>Startort</dt><dd>${esc(t.start_address || "unbekannt")}<small>${dedat(new Date(t.ts_start))}</small></dd></div>
@@ -4128,6 +4171,15 @@ function ereignisseVerdrahten() {
if (liste) liste.innerHTML = entitaetZeilenMarkup(sucheFeld, key, setupSuchtext, wert);
return;
}
// Gleicher Grund wie beim tankpreisfeld-Handler darunter: nur die
// Anzeige patchen, kein render() - das würde das gerade bearbeitete Feld
// neu erzeugen und den Fokus verlieren.
const zeitfeldPraefix = e.target.dataset.fahrtzeitfeld;
if (zeitfeldPraefix) {
const anzeige = ROOT.getElementById(`${zeitfeldPraefix}DauerAnzeige`);
if (anzeige) anzeige.textContent = fahrtDauerText(zeitfeldPraefix);
return;
}
const praefix = e.target.dataset.tankpreisfeld;
if (!praefix) return;
const liter = parseFloat((ROOT.getElementById(`${praefix}Liter`).value || "0").replace(",", "."));
+6 -2
View File
@@ -196,7 +196,6 @@ main#view{--seitenrand:16px;padding:0 var(--seitenrand) 30px;scroll-behavior:smo
.pill{font-size:12px;letter-spacing:.01em;text-transform:none;padding:5px 11px;
border:0;background:var(--ios-fill);color:var(--fg2)}
.pill.work{background:rgba(245,5,55,.16);color:var(--ios-tint)}
.status{padding:14px 0 2px}
.status .t{font-size:16px}
@@ -333,7 +332,12 @@ main#view{--seitenrand:16px;padding:0 var(--seitenrand) 30px;scroll-behavior:smo
/* Fahrzeugbild randlos wie in einer Fahrzeug-App: der Platzhalter bleibt
sichtbar, aber ohne technischen Strichrahmen. */
.szene{margin-bottom:20px}
.bildbox.bild-fehlt .platzhalter{border:0;background:var(--tile);border-radius:var(--r-tile)}
/* --tile statt --ios-fill waere hier naheliegend, faellt im Tag-Theme aber
auf reines Weiss (#FFFFFF) - ununterscheidbar von Canvas und den meisten
Kacheln drumherum, die Box verschwand. --ios-fill ist in beiden Themes ein
deutlich sichtbares transluzentes Grau, dieselbe Konvention wie
.feld select/.aktion ("das hier ist eine Flaeche"). */
.bildbox.bild-fehlt .platzhalter{border:0;background:var(--ios-fill);border-radius:var(--r-tile)}
.platzhalter-datei{display:none}
/* Zeilen mit Wert plus Statuspunkt rechts (Sicherheitscheck) ruhiger setzen */
@@ -1 +1 @@
{"version": 1787000000}
{"version": 1787009000}
-1
View File
@@ -676,7 +676,6 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; }
border-radius: var(--r-pill);
padding: 6px 12px;
}
.pill.work { color: var(--red); border-color: var(--red); }
/* ------------------------------------------------------------ Akkordeon */
.acc-h {