Teil 1 — Coverbilder: - Migration 0004_bild_url (neuheiten), 0002_bild_url (planung, archiv mit beiden Spiegel-Tabellen); nullable VARCHAR(500), SQLite↔Postgres portabel, bestehende Zeilen bleiben erhalten - BGG-Client extrahiert Cover aus der Thing-Antwort (bevorzugt <image>, sonst <thumbnail>; protokoll-relative URLs → https) - Sync-Upsert pflegt bild_url beim Update; fehlendes Bild löscht keines - Web-Quellen übernehmen img-URLs aus den Listenelementen (src/data-src, relative Pfade gegen die Seiten-URL aufgelöst); SPIEL-Essen nutzt das BILDER-Feld des JSON-Endpunkts - Verschiebung Neuheiten→Planung und Archivierung/Wiederherstellen schreiben bild_url mit - Anzeige: kleine Vorschaubilder (40x56) in den Listen, groß (max 200px) in Planung-Bearbeiten/Prüf-Dialog; Platzhalter-Div ohne Bild; Hotlinking mit loading=lazy + decoding=async Teil 2 — Responsives Layout: - Basislayout: Burger-Menü (Alpine.js) auf Mobil, Desktop-Leiste ab md: - Listen kollabieren <768px zu Karten-Ansichten (Cover links, Titel + Metadaten rechts, Status-Badge oben); Desktop behält Tabellenform - Touch-Ziele >= 44px auf Mobil, Formulare vollbreit mit Labels oben - Sync-Übersicht/Audit-Log bleiben horizontal scrollbare Tabellen Tests: 15 neue Fälle (tests/test_covers.py) — Migration inkl. Daten-Erhalt, BGG-Parsing gemockt, Quellen-Übernahme, Template-Render; 316 Tests grün. README aktualisiert.
51 lines
2.5 KiB
HTML
51 lines
2.5 KiB
HTML
{% extends "base.html" %}
|
|
{% block titel %}{{ titel_headline }} — Spiele-Redaktion{% endblock %}
|
|
{% block inhalt %}
|
|
<h1 class="text-2xl font-bold mb-2">Eintrag bearbeiten</h1>
|
|
<p class="text-slate-600 mb-4">„{{ eintrag.titel }}“ · Rezensent:
|
|
{% if user.id == eintrag.rezensent_id %}du{% else %}{{ user.display_name }}{% endif %}
|
|
</p>
|
|
|
|
<div class="flex flex-col sm:flex-row gap-5 items-start mb-4">
|
|
<div class="shrink-0 mx-auto sm:mx-0">
|
|
{% set cover_bild = eintrag.bild_url %}
|
|
{% set cover_titel = eintrag.titel %}
|
|
{% set cover_gross = true %}
|
|
{% include "planung/_cover.html" %}
|
|
</div>
|
|
|
|
<form method="post" action="/planung/{{ eintrag.id }}/bearbeiten"
|
|
class="bg-white border border-slate-200 rounded-xl shadow-sm p-4 max-w-2xl space-y-4 w-full">
|
|
<div>
|
|
<label for="titel" class="block text-xs font-medium text-slate-500 mb-1">Spieltitel *</label>
|
|
<input type="text" id="titel" name="titel" required value="{{ eintrag.titel }}"
|
|
class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500">
|
|
</div>
|
|
<div class="grid md:grid-cols-2 gap-4">
|
|
<div>
|
|
<label for="verlag" class="block text-xs font-medium text-slate-500 mb-1">Verlag</label>
|
|
<input type="text" id="verlag" name="verlag" value="{{ eintrag.verlag or '' }}"
|
|
class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500">
|
|
</div>
|
|
<div>
|
|
<label for="ausgabe" class="block text-xs font-medium text-slate-500 mb-1">Ausgabe</label>
|
|
<input type="text" id="ausgabe" name="ausgabe" value="{{ eintrag.ausgabe }}" placeholder="z. B. 3/2025"
|
|
class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500">
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label for="notizen" class="block text-xs font-medium text-slate-500 mb-1">Notizen</label>
|
|
<textarea id="notizen" name="notizen" rows="3"
|
|
class="w-full border border-slate-300 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-500">{{ eintrag.notizen or '' }}</textarea>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<button type="submit"
|
|
class="bg-emerald-600 hover:bg-emerald-700 text-white font-medium px-4 py-2 rounded-lg text-sm">
|
|
Speichern
|
|
</button>
|
|
<a href="{{ abbrechen_url }}" class="text-sm text-slate-500 hover:text-slate-800 underline">Abbrechen</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|