* apply: Recorder.step_snap(page, name) captures both a JPEG screenshot and the page HTML for every major moment; every provider now calls step_snap at each logical step so failure reports contain the exact DOM and rendered state at every stage of the flow * ZIP report: each snapshot becomes snapshots/NN_<label>.jpg + snapshots/NN_<label>.html for AI-assisted debugging * web: Wohnungsliste zeigt nur noch Flats, die die eigenen Filter treffen; Match-Chip entfernt (Liste ist jetzt implizit matchend) * UI komplett auf Deutsch: Protokoll statt Logs, Administrator statt admin, Trockenmodus statt dry-run, Automatik pausiert statt circuit open, Alarm statt Alert, Abmelden statt Logout * Wohnungen-Header: Zeile 1 Info (Alarm + Filter), Zeile 2 Schalter mit echten Radio-Paaren (An/Aus) für Automatisch bewerben und Trockenmodus; hx-confirm auf den kritischen Radios; per-form CSS für sichtbaren Check-State * Protokoll: von/bis-Datumsfilter (Berliner Zeit) + CSV-Download (/logs/export.csv) mit UTC + lokaler Zeit Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
54 lines
2.3 KiB
HTML
54 lines
2.3 KiB
HTML
{% extends "_layout.html" %}
|
|
{% block content %}
|
|
<section class="card p-4">
|
|
<form method="get" action="/logs" class="flex flex-wrap items-end gap-3">
|
|
<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="/logs">zurücksetzen</a>
|
|
<a class="btn btn-ghost text-sm"
|
|
href="/logs/export.csv?from={{ from_str }}&to={{ to_str }}">
|
|
CSV herunterladen
|
|
</a>
|
|
</form>
|
|
</section>
|
|
|
|
<section 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>
|
|
</section>
|
|
{% endblock %}
|