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>
49 lines
2.2 KiB
HTML
49 lines
2.2 KiB
HTML
<form method="get" action="/admin/protokoll" class="flex flex-wrap items-end gap-3 mb-5">
|
|
<div>
|
|
<label class="block text-xs uppercase tracking-wide text-slate-500 mb-1">von</label>
|
|
<input class="input" type="date" name="from" value="{{ from_str }}">
|
|
</div>
|
|
<div>
|
|
<label class="block text-xs uppercase tracking-wide text-slate-500 mb-1">bis</label>
|
|
<input class="input" type="date" name="to" value="{{ to_str }}">
|
|
</div>
|
|
<button class="btn btn-primary text-sm" type="submit">Anwenden</button>
|
|
<a class="btn btn-ghost text-sm" href="/admin/protokoll">zurücksetzen</a>
|
|
<a class="btn btn-ghost text-sm"
|
|
href="/logs/export.csv?from={{ from_str }}&to={{ to_str }}">
|
|
CSV herunterladen
|
|
</a>
|
|
</form>
|
|
|
|
<div class="card">
|
|
<div class="px-4 py-3 border-b border-soft flex items-center justify-between">
|
|
<h2 class="font-semibold">System-Protokoll</h2>
|
|
<span class="text-xs text-slate-500">
|
|
{{ events|length }} Einträge
|
|
{% if from_str or to_str %}
|
|
· Zeitraum:
|
|
{{ from_str or "alles davor" }} — {{ to_str or "heute" }}
|
|
{% else %}
|
|
· ohne Filter (Anzeige bis 500; Aufbewahrung 14 Tage)
|
|
{% endif %}
|
|
</span>
|
|
</div>
|
|
<div class="divide-y divide-soft">
|
|
{% for e in events %}
|
|
<div class="px-4 py-2 mono flex items-start gap-3 flex-wrap">
|
|
<span class="text-slate-500 shrink-0">{{ e.ts|de_dt }}</span>
|
|
{% if e.kind == 'error' %}
|
|
<span class="chip chip-bad">{{ e.source }}</span>
|
|
{% else %}
|
|
<span class="chip chip-info">{{ e.source }}</span>
|
|
{% endif %}
|
|
<span class="text-slate-700">{{ e.action }}</span>
|
|
{% if e.user %}<span class="text-slate-500">Benutzer: {{ e.user }}</span>{% endif %}
|
|
{% if e.details %}<span class="text-slate-500">— {{ e.details }}</span>{% endif %}
|
|
{% if e.ip %}<span class="text-slate-400">[{{ e.ip }}]</span>{% endif %}
|
|
</div>
|
|
{% else %}
|
|
<div class="px-4 py-8 text-center text-slate-500">Keine Einträge im gewählten Zeitraum.</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|