- Drop the fitBounds-to-markers logic so the map always opens at the same
Berlin-wide view (center 52.52/13.405, zoom 11), regardless of pin count
- Swap OSM standard tiles for CartoDB Voyager — cleaner, more
Google-Maps-like base style
- Add OpenRailwayMap overlay (opacity .75) so S-/U-Bahn/Tram lines are
highlighted on top of the base
- CSP img-src widened to cover the new tile hosts
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Surface "X/Y passende Wohnungen mit Koordinaten" on the Karte view +
admin-only "Koordinaten nachladen" button (POST /actions/backfill-coords)
that geocodes missing flats via Google Maps directly from the web container
- Add googlemaps dep + GMAPS_API_KEY env to web service
- Light console.log in map.js ("rendering N/M markers", "building Leaflet…")
so the browser DevTools shows what's happening
- Drop e-mail channel from notifications UI, notify dispatcher, and _alert_status;
coerce legacy 'email' channel rows back to 'ui' on save
- Countdown said "Aktualisierung läuft…" next to "nächste Aktualisierung" →
shortened to "aktualisiere…"
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- #flats-map uses hx-preserve; marker data moved to <script type="application/json">
sibling that diffs+updates instead of rebuilding Leaflet every poll (fixes
whitescreen + tiles rendering outside the card)
- upsert_flat backfills lat/lng on existing rows missing coords (older flats
scraped before the lat/lng migration now appear on the map once alert re-submits)
- collapsible "Abgelehnte Wohnungen" section at the bottom with Wiederherstellen
- remove €/m² column from the list
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The map was being initialised on DOMContentLoaded even when its container
was display:none (0×0). Leaflet sets up internal bounds from container size
at init; on a zero-sized container no tiles are ever requested. Even
invalidateSize afterwards didn't recover reliably.
* map.js now only builds the Leaflet instance once the container has real
dimensions (clientHeight >= 10). Triggered when the view toggle flips to
Karte (rAF x2 + safety timers), and via restoreView on page load if the
user's last choice was Karte.
* CSP img-src now includes https://unpkg.com for Leaflet marker icons.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Alarm-Status ist jetzt nur 'aktiv' wenn ein echter Push-Channel (Telegram
mit Token+Chat oder E-Mail mit Adresse) konfiguriert ist. UI-only zählt
nicht mehr als eingerichteter Alarm.
* Ablehnen-Button in der Wohnungsliste: flat_rejections (migration v4)
speichert pro-User-Ablehnungen, abgelehnte Flats fallen aus Liste und
Karte raus. Wiederholbar pro User unabhängig.
* Footer 'Programmiert für Annika ♥' erscheint nur auf Seiten, wenn annika
angemeldet ist.
* Map: Hinweistext unter leerer Karte entfernt; alle Zoom-Mechanismen
deaktiviert (Scrollrad, Doppelklick, Box, Touch, Tastatur, +/- Buttons).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* flats: new lat/lng columns (migration v3); alert geocodes every new flat
through googlemaps and ships coords in the payload
* web: CSP extended for unpkg (leaflet.css) + tile.openstreetmap.org
* Wohnungen tab: Liste/Karte view toggle (segmented, CSS-only via :has(),
selection persisted in localStorage). Karte shows passende flats as Pins
on an OSM tile map; Popup per Pin mit Adresse, Zimmer/m²/€ und Link
* Top-strip toggles are now proper iOS-style toggle switches (single
rounded knob sliding in a pill, red when on), no descriptive subtitle
* Alarm-Karte verlinkt jetzt auf /einstellungen/benachrichtigungen
(Filter-Karte bleibt /einstellungen/filter)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* apply: Recorder.step_snap(page, name) captures both a JPEG screenshot and
the page HTML for every major moment; every provider now calls step_snap at
each logical step so failure reports contain the exact DOM and rendered
state at every stage of the flow
* ZIP report: each snapshot becomes snapshots/NN_<label>.jpg +
snapshots/NN_<label>.html for AI-assisted debugging
* web: Wohnungsliste zeigt nur noch Flats, die die eigenen Filter treffen;
Match-Chip entfernt (Liste ist jetzt implizit matchend)
* UI komplett auf Deutsch: Protokoll statt Logs, Administrator statt admin,
Trockenmodus statt dry-run, Automatik pausiert statt circuit open,
Alarm statt Alert, Abmelden statt Logout
* Wohnungen-Header: Zeile 1 Info (Alarm + Filter), Zeile 2 Schalter mit
echten Radio-Paaren (An/Aus) für Automatisch bewerben und Trockenmodus;
hx-confirm auf den kritischen Radios; per-form CSS für sichtbaren Check-State
* Protokoll: von/bis-Datumsfilter (Berliner Zeit) + CSV-Download
(/logs/export.csv) mit UTC + lokaler Zeit
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Wohnungen header: single slim row with Alert status · Filter summary ·
Auto-Bewerben toggle · Trockenmodus toggle. Big filter panel removed —
filters live only in /einstellungen/filter.
* Alert status: 'nicht eingerichtet' until the user has actual filters (+
valid notification creds if telegram/email). 'aktiv' otherwise.
* Logs tab: admin-only (gated both in layout and server-side). Shows merged
audit + errors across all users, sorted newest-first, capped at 300.
* Apply, auto-apply, trockenmodus and circuit reset buttons post via HTMX and
swap the Wohnungen body. While any application is still running for the
user the poll interval drops from 30s to 3s so status flips to 'beworben'
or 'fehlgeschlagen' almost immediately.
* Browser tab title is now always 'lazyflat'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* remove the kill-switch: auto-apply toggle is the single on/off; manual
'Bewerben' button now only gated by apply reachability; circuit breaker
stays but only gates auto-apply (manual bypasses, so a user can retry)
* Berlin-timezone date filter (de_dt) formats timestamps as DD.MM.YYYY HH:MM
everywhere; storage stays UTC
* Wohnungen: live 'entdeckt vor X' on every flat + 'nächste Aktualisierung in Xs'
countdown in the header, driven by /static/app.js; HTMX polls body every 30s
* drop the Fehler tab entirely; failed applications now carry a
'Fehler-Report herunterladen (ZIP)' link -> /bewerbungen/{id}/report.zip
bundles application.json, flat.json, profile_snapshot.json, forensics.json,
step_log.txt, page.html, console/errors/network JSONs, and decoded
screenshots/*.jpg for AI-assisted debugging
* trim the 'sensibel' blurb from the Profil tab
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* DB: users + user_profiles/filters/notifications/preferences; applications gets
user_id + forensics_json + profile_snapshot_json; new errors table
with 14d retention; schema versioning via MIGRATIONS list
* auth: password hashes in DB (argon2); env vars seed first admin; per-user
sessions; CSRF bound to user id
* apply: personal info/WBS moved out of env into the request body; providers
take an ApplyContext with Profile + submit_forms; full Playwright recorder
(step log, console, page errors, network, screenshots, final HTML)
* web: five top-level tabs (Wohnungen/Bewerbungen/Logs/Fehler/Einstellungen);
settings sub-tabs profil/filter/benachrichtigungen/account/benutzer;
per-user matching, auto-apply and notifications (UI/Telegram/SMTP); red
auto-apply switch on Wohnungen tab; forensics detail view for bewerbungen
and fehler; retention background thread
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three isolated services (alert scraper, apply HTTP worker, web UI+DB)
with argon2 auth, signed cookies, CSRF, rate-limited login, kill switch,
apply circuit breaker, audit log, and strict CSP.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>