* 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>
123 lines
5.7 KiB
HTML
123 lines
5.7 KiB
HTML
<h2 class="font-semibold mb-4">Bewerbungsdaten</h2>
|
|
<p class="text-sm text-slate-600 mb-4">
|
|
Diese Angaben werden beim Bewerben an die jeweilige Website gesendet.
|
|
<span class="chip chip-warn">sensibel</span> — werden nur in der DB gespeichert und pro Bewerbung als Snapshot protokolliert.
|
|
</p>
|
|
|
|
<form method="post" action="/actions/profile" class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
<input type="hidden" name="csrf" value="{{ csrf }}">
|
|
|
|
<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 }}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Nachname</label>
|
|
<input class="input" name="lastname" value="{{ profile.lastname }}">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">E-Mail</label>
|
|
<input class="input" type="email" name="email" value="{{ profile.email }}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Telefon</label>
|
|
<input class="input" name="telephone" value="{{ profile.telephone }}">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Straße</label>
|
|
<input class="input" name="street" value="{{ profile.street }}">
|
|
</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 }}">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">PLZ</label>
|
|
<input class="input" name="postcode" value="{{ profile.postcode }}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Stadt</label>
|
|
<input class="input" name="city" value="{{ profile.city }}">
|
|
</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>
|
|
<input class="input" name="wbs_type" value="{{ profile.wbs_type }}" placeholder="z.B. 180">
|
|
</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="email" name="immomio_email" value="{{ profile.immomio_email }}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase text-slate-500 mb-1">Immomio-Passwort</label>
|
|
<input class="input" type="password" name="immomio_password" value="{{ profile.immomio_password }}" placeholder="(unverändert lassen = leer)">
|
|
</div>
|
|
|
|
<div class="col-span-1 md:col-span-2">
|
|
<button class="btn btn-primary" type="submit">Speichern</button>
|
|
</div>
|
|
</form>
|
|
|
|
<hr class="my-6 border-soft">
|
|
<h3 class="font-semibold mb-2">Formulare wirklich absenden?</h3>
|
|
<p class="text-sm text-slate-600 mb-3">
|
|
<span class="chip chip-warn">experimentell</span>
|
|
Im Dry-Run-Modus füllt apply das Formular aus und stoppt vor „Senden". Nur einschalten, wenn du jeden Anbieter einmal im Dry-Run verifiziert hast.
|
|
</p>
|
|
<form method="post" action="/actions/submit-forms" class="inline-flex gap-3 items-center">
|
|
<input type="hidden" name="csrf" value="{{ csrf }}">
|
|
<input type="hidden" name="value" value="on">
|
|
<button class="btn btn-ghost text-sm" type="submit">Echt senden einschalten</button>
|
|
</form>
|
|
<form method="post" action="/actions/submit-forms" class="inline-flex gap-3 items-center ml-2">
|
|
<input type="hidden" name="csrf" value="{{ csrf }}">
|
|
<input type="hidden" name="value" value="off">
|
|
<button class="btn btn-ghost text-sm" type="submit">Dry-Run</button>
|
|
</form>
|