diff --git a/web/app.py b/web/app.py index 17d560c..42e4538 100644 --- a/web/app.py +++ b/web/app.py @@ -404,6 +404,8 @@ def _wohnungen_context(user) -> dict: last = db.last_application_for_flat(uid, f["id"]) flats_view.append({"row": f, "last": last}) + rejected_view = db.rejected_flats(uid) + allowed, reason = _manual_apply_allowed() alert_label, alert_chip = _alert_status(notif_row) has_running = _has_running_application(uid) @@ -422,6 +424,7 @@ def _wohnungen_context(user) -> dict: }) return { "flats": flats_view, + "rejected_flats": rejected_view, "map_points": map_points, "has_filters": _has_filters(filters_row), "alert_label": alert_label, @@ -548,6 +551,20 @@ async def action_reject( return _wohnungen_partial_or_redirect(request, user) +@app.post("/actions/unreject") +async def action_unreject( + request: Request, + flat_id: str = Form(...), + csrf: str = Form(...), + user=Depends(require_user), +): + require_csrf(user["id"], csrf) + db.unreject_flat(user["id"], flat_id) + db.log_audit(user["username"], "flat.unrejected", f"flat_id={flat_id}", + user_id=user["id"], ip=client_ip(request)) + return _wohnungen_partial_or_redirect(request, user) + + # --------------------------------------------------------------------------- # Tab: Bewerbungen # --------------------------------------------------------------------------- diff --git a/web/db.py b/web/db.py index a0c0984..449cbe4 100644 --- a/web/db.py +++ b/web/db.py @@ -395,8 +395,17 @@ def update_preferences(user_id: int, data: dict) -> None: def upsert_flat(payload: dict) -> bool: flat_id = str(payload["id"]) with _lock: - existing = _conn.execute("SELECT id FROM flats WHERE id = ?", (flat_id,)).fetchone() + existing = _conn.execute( + "SELECT id, lat, lng FROM flats WHERE id = ?", (flat_id,) + ).fetchone() if existing: + # Backfill coords on old rows that pre-date the lat/lng migration. + if (existing["lat"] is None or existing["lng"] is None) \ + and payload.get("lat") is not None and payload.get("lng") is not None: + _conn.execute( + "UPDATE flats SET lat = ?, lng = ? WHERE id = ?", + (payload["lat"], payload["lng"], flat_id), + ) return False c = payload.get("connectivity") or {} _conn.execute( @@ -508,6 +517,16 @@ def rejected_flat_ids(user_id: int) -> set[str]: return {row["flat_id"] for row in rows} +def rejected_flats(user_id: int, limit: int = 200) -> list[sqlite3.Row]: + return list(_conn.execute( + """SELECT f.*, r.rejected_at + FROM flat_rejections r JOIN flats f ON f.id = r.flat_id + WHERE r.user_id = ? + ORDER BY r.rejected_at DESC LIMIT ?""", + (user_id, limit), + ).fetchall()) + + def last_application_for_flat(user_id: int, flat_id: str) -> Optional[sqlite3.Row]: return _conn.execute( """SELECT * FROM applications diff --git a/web/static/map.js b/web/static/map.js index 9cc6c3e..fa5e5e5 100644 --- a/web/static/map.js +++ b/web/static/map.js @@ -1,17 +1,75 @@ -// lazyflat — Leaflet flat map -// Initialised LAZILY: we only build the Leaflet instance when the container -// actually has a rendered size (> 0 height). Building it on a hidden 0×0 -// container leaves Leaflet in a state where tiles never load. +// lazyflat — Leaflet flat map. +// +// Two important properties: +// 1. The map must only be *built* once the container has a real size, because +// Leaflet initialised on a hidden 0×0 element never loads its tiles. +// 2. The `#wohnungen-body` partial is re-swapped by HTMX every few seconds. +// To avoid rebuilding Leaflet (and all its tile/marker state) on every +// poll — which caused the whitescreen + out-of-frame glitches — the map +// container itself is preserved across swaps via `hx-preserve`, and the +// marker data is pushed in through a sibling @@ -124,7 +125,6 @@ {% if f.rooms %}{{ "%.1f"|format(f.rooms) }} Z{% endif %} {% if f.size %} · {{ "%.0f"|format(f.size) }} m²{% endif %} {% if f.total_rent %} · {{ "%.0f"|format(f.total_rent) }} €{% endif %} - {% if f.sqm_price %} ({{ "%.2f"|format(f.sqm_price) }} €/m²){% endif %} {% if f.connectivity_morning_time %} · {{ "%.0f"|format(f.connectivity_morning_time) }} min morgens{% endif %} {% if f.wbs %} · WBS: {{ f.wbs }}{% endif %} · entdeckt … @@ -170,4 +170,38 @@ +{% if rejected_flats %} + + + + Abgelehnte Wohnungen + {{ rejected_flats|length }} + + + {% for f in rejected_flats %} + + + + {{ f.address or f.link }} + + + {% if f.rooms %}{{ "%.1f"|format(f.rooms) }} Z{% endif %} + {% if f.size %} · {{ "%.0f"|format(f.size) }} m²{% endif %} + {% if f.total_rent %} · {{ "%.0f"|format(f.total_rent) }} €{% endif %} + · abgelehnt … + + + + + + Wiederherstellen + + + {% endfor %} + + + +{% endif %} +