1. New /admin route with sub-tabs (Protokoll, Benutzer) for admins. Top nav: "Protokoll" dropped, "Admin" added right of Einstellungen. /logs and /einstellungen/benutzer issue 301 redirects to the new paths. Benutzer is no longer part of Einstellungen sub-nav. 2. User_filters.max_age_hours (migration v6) — new dropdown (1–10 h / beliebig) under Einstellungen → Filter; Wohnungen list drops flats older than the cutoff by discovered_at. 3. Header shows "aktualisiert vor X s" instead of a countdown. Template emits data-counter-up-utc with last_alert_heartbeat; app.js ticks up each second. When a scrape runs, the heartbeat updates and the HTMX swap resets the counter naturally. 4. Chevron state synced after HTMX swaps: panes preserved via hx-preserve keep the user's open/closed state, and the sibling button's .open class is re-applied by syncFlatExpandState() on afterSwap — previously a scroll-triggered poll would flip the chevron back to closed while the pane stayed open. 5. "Final absenden" footer removed from the profile page (functionality is unchanged, the switch still sits atop Wohnungen). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
120 lines
6.3 KiB
HTML
120 lines
6.3 KiB
HTML
<h2 class="font-semibold mb-4">Bewerbungsdaten</h2>
|
|
|
|
<form method="post" action="/actions/profile" class="grid grid-cols-1 md:grid-cols-2 gap-4"
|
|
autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore data-form-type="other">
|
|
<input type="hidden" name="csrf" value="{{ csrf }}">
|
|
{# Honeypot: Chrome/Firefox password managers ignore autocomplete="off" but
|
|
autofill the *first* email+password pair they find. These hidden fields
|
|
absorb that autofill so the visible E-Mail/Immomio-Passwort stay clean.
|
|
The server ignores unknown form fields. #}
|
|
<div aria-hidden="true" style="position:absolute; left:-10000px; top:auto; width:1px; height:1px; overflow:hidden;">
|
|
<input type="text" name="_autofill_sink_user" tabindex="-1" autocomplete="username">
|
|
<input type="password" name="_autofill_sink_pass" tabindex="-1" autocomplete="current-password">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Anrede</label>
|
|
<select class="input" name="salutation">
|
|
{% for s in ['Herr', 'Frau', 'Divers'] %}
|
|
<option value="{{ s }}" {% if profile.salutation == s %}selected{% endif %}>{{ s }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div></div>
|
|
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Vorname</label>
|
|
<input class="input" name="firstname" value="{{ profile.firstname }}" autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Nachname</label>
|
|
<input class="input" name="lastname" value="{{ profile.lastname }}" autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">E-Mail</label>
|
|
<input class="input" type="text" inputmode="email" name="contact_addr" value="{{ profile.email }}"
|
|
autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Telefon</label>
|
|
<input class="input" name="telephone" value="{{ profile.telephone }}" autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Straße</label>
|
|
<input class="input" name="street" value="{{ profile.street }}" autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Hausnummer</label>
|
|
<input class="input" name="house_number" value="{{ profile.house_number }}" autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">PLZ</label>
|
|
<input class="input" name="postcode" value="{{ profile.postcode }}" autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Stadt</label>
|
|
<input class="input" name="city" value="{{ profile.city }}" autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
|
|
<div class="col-span-1 md:col-span-2 mt-4 border-t border-soft pt-4">
|
|
<h3 class="font-semibold mb-2">WBS</h3>
|
|
</div>
|
|
<label class="col-span-1 md:col-span-2 inline-flex items-center gap-2">
|
|
<input type="checkbox" name="is_possessing_wbs" {% if profile.is_possessing_wbs %}checked{% endif %}>
|
|
<span class="text-sm">WBS vorhanden</span>
|
|
</label>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">WBS-Typ</label>
|
|
<select class="input" name="wbs_type">
|
|
<option value="0" {% if not profile.wbs_type or profile.wbs_type == '0' %}selected{% endif %}>—</option>
|
|
{% for t in ['100','140','160','180','220'] %}
|
|
<option value="{{ t }}" {% if profile.wbs_type|string == t %}selected{% endif %}>WBS {{ t }}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">gültig bis</label>
|
|
<input class="input" type="date" name="wbs_valid_till" value="{{ profile.wbs_valid_till }}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Räume</label>
|
|
<input class="input" type="number" name="wbs_rooms" value="{{ profile.wbs_rooms }}" min="0">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Erwachsene</label>
|
|
<input class="input" type="number" name="wbs_adults" value="{{ profile.wbs_adults }}" min="0">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Kinder</label>
|
|
<input class="input" type="number" name="wbs_children" value="{{ profile.wbs_children }}" min="0">
|
|
</div>
|
|
<label class="inline-flex items-center gap-2 mt-6">
|
|
<input type="checkbox" name="is_prio_wbs" {% if profile.is_prio_wbs %}checked{% endif %}>
|
|
<span class="text-sm">Prio-WBS (besonderer Wohnbedarf)</span>
|
|
</label>
|
|
|
|
<div class="col-span-1 md:col-span-2 mt-4 border-t border-soft pt-4">
|
|
<h3 class="font-semibold mb-2">Immomio-Login (optional)</h3>
|
|
<p class="text-xs text-slate-500 mb-2">
|
|
Wird von Anbietern benötigt, die über immomio/tenant vermitteln (z.B. gesobau.de).
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Immomio-Email</label>
|
|
<input class="input" type="text" inputmode="email" name="immomio_login" value="{{ profile.immomio_email }}"
|
|
autocomplete="off" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Immomio-Passwort</label>
|
|
<input class="input" type="password" name="immomio_secret" value="{{ profile.immomio_password }}"
|
|
placeholder="(unverändert lassen = leer)"
|
|
autocomplete="new-password" data-lpignore="true" data-1p-ignore data-bwignore>
|
|
</div>
|
|
|
|
<div class="col-span-1 md:col-span-2">
|
|
<button class="btn btn-primary" type="submit">Speichern</button>
|
|
</div>
|
|
</form>
|