feat(wohnungen): "Rausgefilterte Wohnungen" section with reason chips

Below "Abgelehnte Wohnungen", surface flats that survived the time
filter and aren't rejected but failed at least one of the user's
filters. Same collapsed-card style. Action buttons are replaced by
chips naming each failed dimension — "Zimmer", "Preis", "Größe",
"WBS", "Bezirk" — so it's obvious which constraint to relax.

Refactored matching: flat_matches_filter now delegates to a new
flat_filter_failures(flat, f) that returns the failed-dimension
labels (empty list = full match). rooms_min and rooms_max collapse
to a single "Zimmer" chip; reasons emit in stable _REASON_ORDER for
consistent rendering. The section is suppressed entirely when the
user has no filters set, since "everything matches" makes the chips
meaningless.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
EiSiMo 2026-04-23 11:27:49 +02:00
parent b5b4908ee7
commit fe43a402d8
3 changed files with 75 additions and 18 deletions

View file

@ -239,4 +239,38 @@
</section>
{% endif %}
{% if filtered_out_flats %}
<section class="card">
<details class="group">
<summary class="px-4 py-3 text-sm font-medium flex items-center justify-between cursor-pointer">
<span>Rausgefilterte Wohnungen</span>
<span class="text-xs text-slate-500">{{ filtered_out_flats|length }}</span>
</summary>
<div class="divide-y divide-soft border-t border-soft">
{% for item in filtered_out_flats %}
{% set f = item.row %}
<div class="px-4 py-3 flex flex-col md:flex-row md:items-center gap-3">
<div class="flex-1 min-w-0">
<a class="font-medium truncate" href="{{ f.link }}" target="_blank" rel="noopener noreferrer">
{{ f.address or f.link }}
</a>
<div class="text-xs text-slate-500 mt-0.5">
{% 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 %}
· gefunden <span data-rel-utc="{{ f.discovered_at|iso_utc }}" title="{{ f.discovered_at|de_dt }}"></span>
</div>
</div>
<div class="flex gap-1.5 flex-wrap">
{% for reason in item.reasons %}
<span class="chip chip-warn">{{ reason }}</span>
{% endfor %}
</div>
</div>
{% endfor %}
</div>
</details>
</section>
{% endif %}
</div>