settings: relabel dry-run, harder PM block, rework users page

- Bewerbungen chip "Trockenmodus" → "nicht abgeschickt" (list + detail view)
- Profile form: add an off-screen honeypot (username + password) so Chrome's
  autofill burns its fill on those instead of the real E-Mail field; switch
  the visible E-Mail and Immomio-Email to type=text + inputmode=email so the
  browser heuristic no longer tags them as login emails
- Users page: create-form sits on top in its own card (3-column grid with
  Administrator checkbox inline); full-width list below with Administrator
  chip, aktiv/deaktiviert chip, "du" marker for the current user, plus
  disable/activate and a new red "löschen" button (confirm prompt) wired to
  new POST /actions/users/delete which cascades through the user's data

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
EiSiMo 2026-04-21 14:11:50 +02:00
parent 931e0bb8b7
commit de3ce19393
6 changed files with 89 additions and 31 deletions

View file

@ -964,6 +964,26 @@ async def action_users_disable(
return RedirectResponse("/einstellungen/benutzer", status_code=303)
@app.post("/actions/users/delete")
async def action_users_delete(
request: Request,
target_id: int = Form(...),
csrf: str = Form(...),
admin=Depends(require_admin),
):
require_csrf(admin["id"], csrf)
if target_id == admin["id"]:
raise HTTPException(400, "refusing to delete self")
target = db.get_user(target_id)
if not target:
return RedirectResponse("/einstellungen/benutzer", status_code=303)
db.delete_user(target_id)
db.log_audit(admin["username"], "user.deleted",
f"target={target_id} username={target['username']}",
user_id=admin["id"], ip=client_ip(request))
return RedirectResponse("/einstellungen/benutzer?deleted=1", status_code=303)
# ---------------------------------------------------------------------------
# Internal endpoints
# ---------------------------------------------------------------------------