Einzelfahrt: Orte aus den Koordinaten, "Min." statt "min" (2026.9.1.20)

START UND ZIEL STANDEN AUF "UNBEKANNT"

start_address/end_address stehen in HANDFELDER - das Backend fuellt sie nie von
selbst, sie kommen nur aus einer von Hand angelegten oder bearbeiteten Fahrt.
Die Koordinaten dagegen traegt das Screening ein: 12 von 14 Fahrten im
Testbestand hatten Start- und Zielposition, aber genau eine hatte eine Adresse.

Beide Oberflaechen loesen sie jetzt beim Oeffnen der Einzelfahrt auf, ueber
denselben Cache wie die Standortansicht - ein Abruf je Ort, danach nie wieder.
Bewusst NICHT in der Liste: ein Vorratsabruf fuer alle Fahrten waere genau das,
worum Nominatim in seinen Nutzungsbedingungen bittet, es nicht zu tun.

Bis zur Antwort steht "wird ermittelt ...", ohne Koordinaten oder ohne Treffer
"unbekannt". Das Panel prueft vor dem Eintragen, ob noch dieselbe Fahrt offen
ist - wer waehrend des Abrufs weiterblaettert, soll nicht die Adresse der
vorigen Fahrt in der neuen sehen.

Live: Start "Schottenau, 85072 Eichstaett", Ziel "Am Anger 7a, 85111
Adelschlag".

"MIN." STATT "MIN"

Deutsche Abkuerzung, wie die Oberflaeche sie an anderer Stelle laengst
verwendet ("Geparkt seit 2 Tg. 16 Std. 12 Min."). In dauerText() bzw. dauer(),
also ueberall wo eine Dauer erscheint. Zwei Testerwartungen mitgezogen.

Verifiziert: Panel als Modul, tsc --noEmit und vite build sauber, 165/165
Tests gruen, im Browser nachgesehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 19:38:09 +02:00
parent 97a1482bb5
commit 0f680780a4
8 changed files with 147 additions and 14 deletions
+2 -2
View File
@@ -24,11 +24,11 @@ describe("Zahlen im deutschen Format", () => {
describe("dauer", () => {
it("zeigt unter einer Stunde nur Minuten", () => {
expect(dauer(2700)).toBe("45 min")
expect(dauer(2700)).toBe("45 Min.")
})
it("zeigt darüber Stunden und Minuten zweistellig", () => {
expect(dauer(8040)).toBe("2 h 14 min")
expect(dauer(8040)).toBe("2 h 14 Min.")
})
it("zeigt einen Strich statt „0 min", () => {
+4 -2
View File
@@ -125,8 +125,10 @@ export function dauer(sekunden: number | null | undefined): string {
const minutenGesamt = Math.round(sekunden / 60)
const stunden = Math.floor(minutenGesamt / 60)
const minuten = minutenGesamt % 60
if (stunden === 0) return `${minuten} min`
return `${stunden} h ${String(minuten).padStart(2, "0")} min`
// "Min." statt "min": deutsche Abkuerzung, wie sie die Oberflaeche sonst
// auch verwendet ("Geparkt seit 2 Tg. 16 Std. 12 Min.").
if (stunden === 0) return `${minuten} Min.`
return `${stunden} h ${String(minuten).padStart(2, "0")} Min.`
}
/**
+48 -2
View File
@@ -15,6 +15,7 @@ import { ActionButton, Feld, Fig, Pill, Tile } from "@audi-dash/ui"
import type { Fahrt } from "../api"
import { useDaten } from "../daten/DatenKontext"
import { koordinatenAufloesen } from "../daten/geokodierung"
import { datum, dauer, de, streckeTeile, uhrzeit } from "../format"
import type { SeitenName } from "../navigation"
import { Leerzustand, Wertzeile, Werteliste, bestaetigen } from "./bausteine"
@@ -41,6 +42,51 @@ export function FahrtDetail({ id, geheZu }: { id: string | undefined; geheZu: (n
// render()).
const [artVorgriff, setzeArtVorgriff] = useState<"privat" | "arbeitsweg" | null>(null)
/* Ortsangaben zu Start und Ziel.
Das Backend fuellt start_address/end_address NIE von selbst - sie stehen in
HANDFELDER und kommen nur aus einer von Hand angelegten oder bearbeiteten
Fahrt. Die Koordinaten dagegen traegt das Screening ein. Deshalb stand hier
bisher "unbekannt", obwohl die Position bekannt war (vom Eigentuemer
gemeldet, 01.09.2026).
Aufgeloest wird erst beim Oeffnen der Fahrt und ueber denselben Cache wie
die Standortansicht - ein Abruf je Ort, danach nie wieder. Ein Vorratsabruf
fuer alle Fahrten der Liste waere genau das, worum Nominatim in seinen
Nutzungsbedingungen bittet, es nicht zu tun. */
const [orte, setzeOrte] = useState<{ start: string | null; ziel: string | null }>({
start: null,
ziel: null,
})
const startLat = fahrt?.start_lat
const startLon = fahrt?.start_lon
const endLat = fahrt?.end_lat
const endLon = fahrt?.end_lon
useEffect(() => {
let abgemeldet = false
setzeOrte({ start: null, ziel: null })
const holen = async (lat?: number | null, lon?: number | null) =>
lat == null || lon == null ? null : await koordinatenAufloesen(lat, lon)
void (async () => {
const start = await holen(startLat, startLon)
if (!abgemeldet) setzeOrte((o) => ({ ...o, start }))
const ziel = await holen(endLat, endLon)
if (!abgemeldet) setzeOrte((o) => ({ ...o, ziel }))
})()
return () => {
abgemeldet = true
}
}, [startLat, startLon, endLat, endLon])
const ortText = (
adresse: string | null | undefined,
aufgeloest: string | null,
lat?: number | null,
lon?: number | null,
) => adresse ?? aufgeloest ?? (lat == null || lon == null ? "unbekannt" : "wird ermittelt …")
const startOrt = ortText(fahrt?.start_address, orte.start, startLat, startLon)
const zielOrt = ortText(fahrt?.end_address, orte.ziel, endLat, endLon)
// Faellt der Vorgriff mit dem echten Wert zusammen, wird er ueberfluessig.
// Bleibt er stehen, wuerde eine spaetere Aenderung von aussen (Panel,
// zweites Geraet) von einem laengst erledigten Tipp ueberdeckt.
@@ -132,12 +178,12 @@ export function FahrtDetail({ id, geheZu }: { id: string | undefined; geheZu: (n
<Wertzeile
label="Start"
wert={`${datum(fahrt.ts_start)} · ${uhrzeit(fahrt.ts_start)} Uhr`}
zusatz={fahrt.start_address ?? "unbekannt"}
zusatz={startOrt}
/>
<Wertzeile
label="Ziel"
wert={`${datum(fahrt.ts_end)} · ${uhrzeit(fahrt.ts_end)} Uhr`}
zusatz={fahrt.end_address ?? "unbekannt"}
zusatz={zielOrt}
/>
<Wertzeile
label="Startkilometer"