diff --git a/AGENTS.md b/AGENTS.md index 4ac58c4..5a53c0f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -804,6 +804,38 @@ wraps the web app for iPhone; a PWA home-screen install is the accepted intermed un-ignoring. **Keep it in sync whenever `pyscript/` or `www/` changes** — that was already the working rule, but it is now visible in the repo when it is forgotten. Deployed to `audi_ha_test` (version `1786766000`). +- [x] Fifth round the same day (2026-08-16) — one-click installer and the Einzelbeleg map: + (1) **`installationspaket/Installieren.cmd` + `install.ps1`** automate steps 1–3 of + `ANLEITUNG.md`. The `.cmd` wrapper exists because double-clicking a `.ps1` opens it in an + editor and the execution policy blocks it; the bypass is per-call, the machine policy is not + touched. Safe to re-run: never overwrites `fahrzeugprofil.json`, `fahrten.jsonl`, + `tankvorgaenge.jsonl`, `entitaeten.json`, `ha_token.txt`; backs `configuration.yaml` up to + `.bak` and replaces its own marker-delimited block instead of appending twice; refuses to + touch the file at all when `pyscript:`/`panel_custom:` are already there from another source + (duplicate top-level keys are invalid YAML — HA would not start); aborts before writing if + the target has no `configuration.yaml`. `-Pruefen` is a dry run. **`install.ps1` is stored + UTF-8 *with* BOM on purpose** — PowerShell 5.1 reads scripts as ANSI otherwise and mangles + every umlaut; the files it *writes* stay BOM-less (YAML). Verified against a fake config + tree: fresh install, re-run idempotency, data preservation, foreign-key refusal, + wrong-directory abort, and the merged YAML parses. + (2) **Tankstellenposition im Einzelbeleg.** The red `circleMarker` is now the CI `poi` pin + (`CI.poiL`/`CI.poiS` from `poi-l.svg`/`poi-s.svg`, via `tankstellenMarkerSVG()`) — deliberately + the plain pin, not `poi-car`, so vehicle and station stay distinguishable. + **Found while testing: nothing in the backend ever writes `station_lat`/`station_lon`**, so + this marker had never actually been reachable. Fixed by geocoding the receipt address in the + frontend (`adresseAufloesen()`, Nominatim `/search` — the forward counterpart to the existing + `standortAdresseAufloesen()`), with results cached in `localStorage` (`audi_dashboard_geocache`) + including negative results, since a station address does not move and Nominatim's terms ask + for caching. Confirmed live: `Shell, SÖLDEN` → 46.9756/11.0111. + (3) **Zentrieren-Knopf** in the Einzelbeleg map card (`mapBoxMitZentrieren()`, `CI.gps` — + byte-identical to the delivered `gps-s.svg`), disabled until the coordinates arrive. `.mapbox` + gained `position:relative` so the control anchors to the card. Only there, per explicit + request — the Standort fullscreen already has its own centre controls. + Deployed to `audi_ha_test` (version `1786769000`). + Note for testing map behaviour in this app: `initMap()` runs on **every** `render()`, and the + app re-renders on each backend push (~30 s), rebuilding the map and re-centring it. Panning + the map from the console and checking later therefore proves nothing — spy on + `L.Map.prototype.flyTo` instead. - [ ] 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` diff --git a/homeassistant/installationspaket/www/audi-dashboard-app.js b/homeassistant/installationspaket/www/audi-dashboard-app.js index da54ea4..42ef544 100644 --- a/homeassistant/installationspaket/www/audi-dashboard-app.js +++ b/homeassistant/installationspaket/www/audi-dashboard-app.js @@ -386,6 +386,8 @@ function ring(pct) { /* ------------------------------------------------------------------ Karte Leaflet weiterhin per CDN geladen (siehe Kopfkommentar). */ let MAP = null, TILE = null, LEAFLET_LADEND = null; +// Zielpunkt des Zentrieren-Knopfs im Einzelbeleg (siehe mapBoxMitZentrieren). +let BELEG_STATION_POS = null; /* Bewusst themeunabhaengig hell: Kartendienste (Google Maps & Co.) zeigen die Standard-Strassenkarte immer im hellen Stil, weil Strassen, Beschriftungen und Wegfarben darauf lesbar bleiben. Die frueher bei Nacht eingeblendeten @@ -441,6 +443,21 @@ function leafletLaden() { function mapBox() { return `
`; } +/* Wie mapBox(), aber mit Zentrieren-Knopf - nur im Einzelbeleg. Der Knopf + holt die Tankstelle zurück in die Mitte, nachdem man auf der Karte + herumgezogen hat. Er sitzt im .mapbox (position:relative), damit er über + der Karte schwebt, und ist bis zum Eintreffen der Koordinaten deaktiviert - + die kommen bei Belegen ohne station_lat erst aus dem Nominatim-Abruf + (siehe adresseAufloesen). */ +function mapBoxMitZentrieren() { + return `
+
+
+ +
+
`; +} + /* Kartenmittelpunkt ohne eigene Positionsdaten (z. B. Fahrt ohne GPS-Punkte): die Werkstatt-Koordinaten aus dem Profil statt einer im Code fest hinterlegten Region - die liegen ohnehin meist in der Nähe und kommen @@ -487,10 +504,21 @@ async function initMap() { MAP.setView(kartenMittelpunkt(), 11); // kein Track ohne GPS-Daten (§7a-Backend-Lücke) } } else if (route.name === "fill") { + BELEG_STATION_POS = null; // gehört zum vorherigen Beleg, sonst zentriert der Knopf falsch const f = FILLS.find((x) => x.tank_id === route.id); if (f && f.station_lat != null) { - MAP.setView([f.station_lat, f.station_lon], 14); - window.L.circleMarker([f.station_lat, f.station_lon], { radius: 7, color: rot, weight: 2, fillColor: rot, fillOpacity: 0.85 }).addTo(MAP).bindPopup((f.station_name || "") + "
" + (f.station_address || "")); + tankstelleAufKarteZeigen(MAP, { lat: f.station_lat, lon: f.station_lon }, f); + } else if (f && (f.station_address || f.station_name)) { + /* Ohne Koordinaten die Anschrift vom Beleg nachschlagen. Bis die Antwort + da ist, bleibt der neutrale Ausschnitt stehen - und die Karte könnte + inzwischen zu einer anderen Ansicht gehören, deshalb vor dem Zeichnen + prüfen, ob wir noch auf demselben Beleg sind. */ + MAP.setView(kartenMittelpunkt(), 11); + const karte = MAP, belegId = route.id; + adresseAufloesen(f.station_address || f.station_name).then((pos) => { + if (!pos || MAP !== karte || route.name !== "fill" || route.id !== belegId) return; + tankstelleAufKarteZeigen(karte, pos, f); + }); } else { MAP.setView(kartenMittelpunkt(), 11); } @@ -535,6 +563,15 @@ function fahrzeugMarkerSVG(px = 34) { return ``; } +/* Tankstellenposition im Einzelbeleg - dieselbe Nadelform wie beim Fahrzeug, + nur ohne Auto-Glyph (CI.poiL/poiS), damit beide Marker sofort auseinander- + zuhalten sind. Spitze unten wie bei poi-car, gleiche Ankerrechnung. */ +function tankstellenMarkerSVG(px = 32) { + const gross = px >= 34; + const box = gross ? 48 : 24; + return ``; +} + /* Fahrzustand fürs Menü: "fährt" solange die aktuelle Fahrt noch offen ist (siehe fahrterkennung.py), sonst "steht". Ohne echtes Bewegungssignal vom Fahrzeug (siehe AGENTS.md, FMM003/flespi offen) ist das die beste Näherung @@ -641,6 +678,76 @@ function standortAdresseLaden() { }); } +/* Gegenrichtung zu standortAdresseAufloesen: Adresse -> Koordinaten, für die + Tankstellennadel im Einzelbeleg. Nötig, weil der Beleg nur eine Anschrift + trägt und nichts im Backend station_lat/station_lon je befüllt - ohne diesen + Schritt bliebe die Karte dort dauerhaft ohne Nadel. + + Ergebnisse landen in localStorage statt nur im RAM: eine Tankstellenadresse + ändert ihre Lage nicht, und der Einzelbeleg wird oft genug erneut geöffnet, + dass ein Cache nur für die laufende Sitzung Nominatim unnötig oft anfassen + würde (deren Nutzungsbedingungen bitten ausdrücklich darum, Ergebnisse zu + cachen). Auch ein Fehlschlag wird vermerkt - als null -, damit eine Adresse, + die Nominatim nicht kennt, nicht bei jedem Öffnen erneut abgefragt wird. */ +const GEO_CACHE_KEY = "audi_dashboard_geocache"; +let GEO_CACHE = null; +function geoCacheLesen() { + if (GEO_CACHE) return GEO_CACHE; + try { GEO_CACHE = JSON.parse(localStorage.getItem(GEO_CACHE_KEY)) || {}; } + catch (e) { GEO_CACHE = {}; } + return GEO_CACHE; +} +function geoCacheSchreiben(schluessel, wert) { + const cache = geoCacheLesen(); + cache[schluessel] = wert; + try { localStorage.setItem(GEO_CACHE_KEY, JSON.stringify(cache)); } + catch (e) { /* voller/gesperrter Speicher: Cache bleibt dann RAM-only */ } +} +async function adresseAufloesen(adresse) { + const suche = (adresse || "").trim(); + if (!suche) return null; + const cache = geoCacheLesen(); + if (Object.prototype.hasOwnProperty.call(cache, suche)) return cache[suche]; + let treffer = null; + try { + const res = await fetch( + `https://nominatim.openstreetmap.org/search?format=jsonv2&limit=1&q=${encodeURIComponent(suche)}`, + { headers: { "Accept-Language": "de" } } + ); + const daten = await res.json(); + if (Array.isArray(daten) && daten.length) { + treffer = { lat: parseFloat(daten[0].lat), lon: parseFloat(daten[0].lon) }; + } + } catch (e) { + // Netzfehler ist kein "Adresse gibt es nicht" - nichts merken, damit der + // nächste Aufruf es erneut versucht (vgl. standortAdresseAufloesen). + return null; + } + geoCacheSchreiben(suche, treffer); + return treffer; +} + +/* Setzt Nadel und Ausschnitt, sobald die Koordinaten da sind. Getrennt von + initMap(), weil der Abruf asynchron ist und die Karte bis dahin schon + steht. */ +function tankstelleAufKarteZeigen(karte, pos, f) { + if (!karte || !window.L || !pos) return; + karte.setView([pos.lat, pos.lon], 14); + const icon = window.L.divIcon({ className: "fahrzeug-pin-wrap", html: `
${tankstellenMarkerSVG(32)}
`, iconSize: [32, 32], iconAnchor: [15, 31] }); + window.L.marker([pos.lat, pos.lon], { icon }).addTo(karte) + .bindPopup((f.station_name || "") + "
" + (f.station_address || "")); + // Position merken und den Knopf scharf schalten - er kann erst jetzt etwas + // Sinnvolles tun (bei Adressauflösung kommt das hier verzögert, siehe + // Aufrufstelle in initMap). + BELEG_STATION_POS = pos; + const knopf = ROOT && ROOT.getElementById("belegZentrieren"); + if (knopf) knopf.disabled = false; +} +function belegAufTankstelleZentrieren() { + if (!MAP || !BELEG_STATION_POS) return; + MAP.flyTo([BELEG_STATION_POS.lat, BELEG_STATION_POS.lon], Math.max(MAP.getZoom(), 14)); +} + function standortKartenlink() { return standortBekannt() ? `https://www.google.com/maps/dir/?api=1&destination=${CAR.standortLat},${CAR.standortLon}` : null; } @@ -814,6 +921,12 @@ const CI = { poiCar: '', /* poi-car-l - grosse Variante desselben CI-Symbols (48er-Raster), fuer die Fahrzeugnadel auf der Vollbildkarte. Siehe fahrzeugMarkerSVG(). */ + /* poi-l/poi-s aus dem CI-Satz: neutrale Kartennadel mit Kreis, fuer die + Tankstellenposition im Einzelbeleg (poi-car bleibt dem Fahrzeug + vorbehalten, sonst waeren beide Nadeln auf einer Karte nicht zu + unterscheiden). Siehe tankstellenMarkerSVG(). */ + poiL: '', + poiS: '', poiCarL: '', selectAll: '', planRoute: '', @@ -2735,7 +2848,7 @@ function vFill(id) { } return ` - ${mapBox()} + ${mapBoxMitZentrieren()}
Getankt
${f.liters != null ? de(f.liters, 2) : "–"}l
@@ -3669,6 +3782,9 @@ function ereignisseVerdrahten() { if (e.target.closest("[data-standort-beide]")) { standortBeideZeigen(); return; } if (e.target.closest("[data-standort-schliessen]")) { standortMenuSchliessen(); return; } if (e.target.closest("[data-standort-teilen]")) { standortTeilen(); return; } + // Einzelbeleg: zurück auf die Tankstelle (manipuliert MAP direkt, kein + // render() - das würde die Karte neu aufbauen und den Ausschnitt verlieren). + if (e.target.closest("#belegZentrieren")) { belegAufTankstelleZentrieren(); return; } const g = e.target.closest("[data-go]"); if (g) { const [n, i] = g.dataset.go.split(":"); go(n, i); } }); diff --git a/homeassistant/installationspaket/www/audi-dashboard-version.json b/homeassistant/installationspaket/www/audi-dashboard-version.json index b271edc..3adbe44 100644 --- a/homeassistant/installationspaket/www/audi-dashboard-version.json +++ b/homeassistant/installationspaket/www/audi-dashboard-version.json @@ -1 +1 @@ -{"version": 1786766000} +{"version": 1786769000} diff --git a/homeassistant/installationspaket/www/audi-dashboard.css b/homeassistant/installationspaket/www/audi-dashboard.css index 7894b76..ad1ab74 100644 --- a/homeassistant/installationspaket/www/audi-dashboard.css +++ b/homeassistant/installationspaket/www/audi-dashboard.css @@ -688,7 +688,10 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; } .swipe-wrap.swiped .swipe-content { transform: translateX(-84px); } /* -------------------------------------------------------------- Karte */ -.mapbox { border-radius: var(--r-tile); overflow: hidden; height: 210px; background: var(--tile); } +/* position:relative, damit der Zentrieren-Knopf im Einzelbeleg (absolut + positioniert, siehe .kartensteuerung-klein) sich an dieser Kachel ausrichtet + und nicht am naechsten Vorfahren weiter oben. */ +.mapbox { position: relative; border-radius: var(--r-tile); overflow: hidden; height: 210px; background: var(--tile); } .mapbox #map { width: 100%; height: 100%; } /* --------------------------------------------------------- Standort @@ -757,6 +760,12 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; } } .kartensteuerung button:active { transform: scale(.94); } :host([data-theme="tag"]) .kartensteuerung button:active { background: #fff; } +/* Variante in der Belegkarte: die Karte ist dort nur 210px hoch, ein 48er- + Knopf mit 16px Abstand wuerde ein Viertel davon verdecken. */ +.kartensteuerung-klein { top: 10px; right: 10px; } +.kartensteuerung-klein button { width: 36px; height: 36px; } +.kartensteuerung-klein button:disabled { opacity: .45; cursor: default; } +.kartensteuerung-klein button:disabled:active { transform: none; } /* Sichtbarer Anteil im geschlossenen Zustand (STANDORT_PEEK_PX im Frontend) - beide Werte müssen zusammenpassen, siehe Kommentar dort. Der Rest des diff --git a/homeassistant/www/audi-dashboard-app.js b/homeassistant/www/audi-dashboard-app.js index da54ea4..42ef544 100644 --- a/homeassistant/www/audi-dashboard-app.js +++ b/homeassistant/www/audi-dashboard-app.js @@ -386,6 +386,8 @@ function ring(pct) { /* ------------------------------------------------------------------ Karte Leaflet weiterhin per CDN geladen (siehe Kopfkommentar). */ let MAP = null, TILE = null, LEAFLET_LADEND = null; +// Zielpunkt des Zentrieren-Knopfs im Einzelbeleg (siehe mapBoxMitZentrieren). +let BELEG_STATION_POS = null; /* Bewusst themeunabhaengig hell: Kartendienste (Google Maps & Co.) zeigen die Standard-Strassenkarte immer im hellen Stil, weil Strassen, Beschriftungen und Wegfarben darauf lesbar bleiben. Die frueher bei Nacht eingeblendeten @@ -441,6 +443,21 @@ function leafletLaden() { function mapBox() { return `
`; } +/* Wie mapBox(), aber mit Zentrieren-Knopf - nur im Einzelbeleg. Der Knopf + holt die Tankstelle zurück in die Mitte, nachdem man auf der Karte + herumgezogen hat. Er sitzt im .mapbox (position:relative), damit er über + der Karte schwebt, und ist bis zum Eintreffen der Koordinaten deaktiviert - + die kommen bei Belegen ohne station_lat erst aus dem Nominatim-Abruf + (siehe adresseAufloesen). */ +function mapBoxMitZentrieren() { + return `
+
+
+ +
+
`; +} + /* Kartenmittelpunkt ohne eigene Positionsdaten (z. B. Fahrt ohne GPS-Punkte): die Werkstatt-Koordinaten aus dem Profil statt einer im Code fest hinterlegten Region - die liegen ohnehin meist in der Nähe und kommen @@ -487,10 +504,21 @@ async function initMap() { MAP.setView(kartenMittelpunkt(), 11); // kein Track ohne GPS-Daten (§7a-Backend-Lücke) } } else if (route.name === "fill") { + BELEG_STATION_POS = null; // gehört zum vorherigen Beleg, sonst zentriert der Knopf falsch const f = FILLS.find((x) => x.tank_id === route.id); if (f && f.station_lat != null) { - MAP.setView([f.station_lat, f.station_lon], 14); - window.L.circleMarker([f.station_lat, f.station_lon], { radius: 7, color: rot, weight: 2, fillColor: rot, fillOpacity: 0.85 }).addTo(MAP).bindPopup((f.station_name || "") + "
" + (f.station_address || "")); + tankstelleAufKarteZeigen(MAP, { lat: f.station_lat, lon: f.station_lon }, f); + } else if (f && (f.station_address || f.station_name)) { + /* Ohne Koordinaten die Anschrift vom Beleg nachschlagen. Bis die Antwort + da ist, bleibt der neutrale Ausschnitt stehen - und die Karte könnte + inzwischen zu einer anderen Ansicht gehören, deshalb vor dem Zeichnen + prüfen, ob wir noch auf demselben Beleg sind. */ + MAP.setView(kartenMittelpunkt(), 11); + const karte = MAP, belegId = route.id; + adresseAufloesen(f.station_address || f.station_name).then((pos) => { + if (!pos || MAP !== karte || route.name !== "fill" || route.id !== belegId) return; + tankstelleAufKarteZeigen(karte, pos, f); + }); } else { MAP.setView(kartenMittelpunkt(), 11); } @@ -535,6 +563,15 @@ function fahrzeugMarkerSVG(px = 34) { return ``; } +/* Tankstellenposition im Einzelbeleg - dieselbe Nadelform wie beim Fahrzeug, + nur ohne Auto-Glyph (CI.poiL/poiS), damit beide Marker sofort auseinander- + zuhalten sind. Spitze unten wie bei poi-car, gleiche Ankerrechnung. */ +function tankstellenMarkerSVG(px = 32) { + const gross = px >= 34; + const box = gross ? 48 : 24; + return ``; +} + /* Fahrzustand fürs Menü: "fährt" solange die aktuelle Fahrt noch offen ist (siehe fahrterkennung.py), sonst "steht". Ohne echtes Bewegungssignal vom Fahrzeug (siehe AGENTS.md, FMM003/flespi offen) ist das die beste Näherung @@ -641,6 +678,76 @@ function standortAdresseLaden() { }); } +/* Gegenrichtung zu standortAdresseAufloesen: Adresse -> Koordinaten, für die + Tankstellennadel im Einzelbeleg. Nötig, weil der Beleg nur eine Anschrift + trägt und nichts im Backend station_lat/station_lon je befüllt - ohne diesen + Schritt bliebe die Karte dort dauerhaft ohne Nadel. + + Ergebnisse landen in localStorage statt nur im RAM: eine Tankstellenadresse + ändert ihre Lage nicht, und der Einzelbeleg wird oft genug erneut geöffnet, + dass ein Cache nur für die laufende Sitzung Nominatim unnötig oft anfassen + würde (deren Nutzungsbedingungen bitten ausdrücklich darum, Ergebnisse zu + cachen). Auch ein Fehlschlag wird vermerkt - als null -, damit eine Adresse, + die Nominatim nicht kennt, nicht bei jedem Öffnen erneut abgefragt wird. */ +const GEO_CACHE_KEY = "audi_dashboard_geocache"; +let GEO_CACHE = null; +function geoCacheLesen() { + if (GEO_CACHE) return GEO_CACHE; + try { GEO_CACHE = JSON.parse(localStorage.getItem(GEO_CACHE_KEY)) || {}; } + catch (e) { GEO_CACHE = {}; } + return GEO_CACHE; +} +function geoCacheSchreiben(schluessel, wert) { + const cache = geoCacheLesen(); + cache[schluessel] = wert; + try { localStorage.setItem(GEO_CACHE_KEY, JSON.stringify(cache)); } + catch (e) { /* voller/gesperrter Speicher: Cache bleibt dann RAM-only */ } +} +async function adresseAufloesen(adresse) { + const suche = (adresse || "").trim(); + if (!suche) return null; + const cache = geoCacheLesen(); + if (Object.prototype.hasOwnProperty.call(cache, suche)) return cache[suche]; + let treffer = null; + try { + const res = await fetch( + `https://nominatim.openstreetmap.org/search?format=jsonv2&limit=1&q=${encodeURIComponent(suche)}`, + { headers: { "Accept-Language": "de" } } + ); + const daten = await res.json(); + if (Array.isArray(daten) && daten.length) { + treffer = { lat: parseFloat(daten[0].lat), lon: parseFloat(daten[0].lon) }; + } + } catch (e) { + // Netzfehler ist kein "Adresse gibt es nicht" - nichts merken, damit der + // nächste Aufruf es erneut versucht (vgl. standortAdresseAufloesen). + return null; + } + geoCacheSchreiben(suche, treffer); + return treffer; +} + +/* Setzt Nadel und Ausschnitt, sobald die Koordinaten da sind. Getrennt von + initMap(), weil der Abruf asynchron ist und die Karte bis dahin schon + steht. */ +function tankstelleAufKarteZeigen(karte, pos, f) { + if (!karte || !window.L || !pos) return; + karte.setView([pos.lat, pos.lon], 14); + const icon = window.L.divIcon({ className: "fahrzeug-pin-wrap", html: `
${tankstellenMarkerSVG(32)}
`, iconSize: [32, 32], iconAnchor: [15, 31] }); + window.L.marker([pos.lat, pos.lon], { icon }).addTo(karte) + .bindPopup((f.station_name || "") + "
" + (f.station_address || "")); + // Position merken und den Knopf scharf schalten - er kann erst jetzt etwas + // Sinnvolles tun (bei Adressauflösung kommt das hier verzögert, siehe + // Aufrufstelle in initMap). + BELEG_STATION_POS = pos; + const knopf = ROOT && ROOT.getElementById("belegZentrieren"); + if (knopf) knopf.disabled = false; +} +function belegAufTankstelleZentrieren() { + if (!MAP || !BELEG_STATION_POS) return; + MAP.flyTo([BELEG_STATION_POS.lat, BELEG_STATION_POS.lon], Math.max(MAP.getZoom(), 14)); +} + function standortKartenlink() { return standortBekannt() ? `https://www.google.com/maps/dir/?api=1&destination=${CAR.standortLat},${CAR.standortLon}` : null; } @@ -814,6 +921,12 @@ const CI = { poiCar: '', /* poi-car-l - grosse Variante desselben CI-Symbols (48er-Raster), fuer die Fahrzeugnadel auf der Vollbildkarte. Siehe fahrzeugMarkerSVG(). */ + /* poi-l/poi-s aus dem CI-Satz: neutrale Kartennadel mit Kreis, fuer die + Tankstellenposition im Einzelbeleg (poi-car bleibt dem Fahrzeug + vorbehalten, sonst waeren beide Nadeln auf einer Karte nicht zu + unterscheiden). Siehe tankstellenMarkerSVG(). */ + poiL: '', + poiS: '', poiCarL: '', selectAll: '', planRoute: '', @@ -2735,7 +2848,7 @@ function vFill(id) { } return ` - ${mapBox()} + ${mapBoxMitZentrieren()}
Getankt
${f.liters != null ? de(f.liters, 2) : "–"}l
@@ -3669,6 +3782,9 @@ function ereignisseVerdrahten() { if (e.target.closest("[data-standort-beide]")) { standortBeideZeigen(); return; } if (e.target.closest("[data-standort-schliessen]")) { standortMenuSchliessen(); return; } if (e.target.closest("[data-standort-teilen]")) { standortTeilen(); return; } + // Einzelbeleg: zurück auf die Tankstelle (manipuliert MAP direkt, kein + // render() - das würde die Karte neu aufbauen und den Ausschnitt verlieren). + if (e.target.closest("#belegZentrieren")) { belegAufTankstelleZentrieren(); return; } const g = e.target.closest("[data-go]"); if (g) { const [n, i] = g.dataset.go.split(":"); go(n, i); } }); diff --git a/homeassistant/www/audi-dashboard-version.json b/homeassistant/www/audi-dashboard-version.json index b271edc..3adbe44 100644 --- a/homeassistant/www/audi-dashboard-version.json +++ b/homeassistant/www/audi-dashboard-version.json @@ -1 +1 @@ -{"version": 1786766000} +{"version": 1786769000} diff --git a/homeassistant/www/audi-dashboard.css b/homeassistant/www/audi-dashboard.css index 7894b76..ad1ab74 100644 --- a/homeassistant/www/audi-dashboard.css +++ b/homeassistant/www/audi-dashboard.css @@ -688,7 +688,10 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; } .swipe-wrap.swiped .swipe-content { transform: translateX(-84px); } /* -------------------------------------------------------------- Karte */ -.mapbox { border-radius: var(--r-tile); overflow: hidden; height: 210px; background: var(--tile); } +/* position:relative, damit der Zentrieren-Knopf im Einzelbeleg (absolut + positioniert, siehe .kartensteuerung-klein) sich an dieser Kachel ausrichtet + und nicht am naechsten Vorfahren weiter oben. */ +.mapbox { position: relative; border-radius: var(--r-tile); overflow: hidden; height: 210px; background: var(--tile); } .mapbox #map { width: 100%; height: 100%; } /* --------------------------------------------------------- Standort @@ -757,6 +760,12 @@ button.tile, .tilebtn { transition: background .15s, transform .1s; } } .kartensteuerung button:active { transform: scale(.94); } :host([data-theme="tag"]) .kartensteuerung button:active { background: #fff; } +/* Variante in der Belegkarte: die Karte ist dort nur 210px hoch, ein 48er- + Knopf mit 16px Abstand wuerde ein Viertel davon verdecken. */ +.kartensteuerung-klein { top: 10px; right: 10px; } +.kartensteuerung-klein button { width: 36px; height: 36px; } +.kartensteuerung-klein button:disabled { opacity: .45; cursor: default; } +.kartensteuerung-klein button:disabled:active { transform: none; } /* Sichtbarer Anteil im geschlossenen Zustand (STANDORT_PEEK_PX im Frontend) - beide Werte müssen zusammenpassen, siehe Kommentar dort. Der Rest des