Coverbilder (bild_url) + responsives Layout für Mobil/Tablet

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.
This commit is contained in:
Flo Hartmann
2026-08-24 16:54:56 +00:00
parent 20b84a8e1a
commit 96d6af8163
33 changed files with 1134 additions and 69 deletions

View File

@@ -96,6 +96,11 @@ Fake-BGG-Client, Prüfprotokoll, Prüfseite) und **planung** (beide Eintragswege
mit Stub-gemockter dedup-/Benachrichtigungs-/Audit-API, Verlags-Auswahl-
Dialog, Statuswechsel, Bearbeiten/Löschen, Rechte pro Rolle) plus ein
Integrationstest mit den echten Plugins (In-App-Nachricht + Audit-Eintrag).
Dazu die **Coverbilder-Erweiterung**: Migrationen für `bild_url` in allen
vier Tabellen inkl. Daten-Erhalt beim ALTER TABLE, BGG-Bildextraktion
(`<image>` bevorzugt, `<thumbnail>` als Rückfallebene, gemockte Antworten),
Bildübernahme aus Web-Quellen, Update-Pflege des Bildes im Sync-Upsert sowie
Template-Darstellung (`loading="lazy"`/`decoding="async"` bzw. Platzhalter).
Dazu das **archiv**-Plugin: 12-Monats-Regel mit eingefrorener Uhr (Grenzfälle
„genau 12 Monate“, Schaltjahr/29. Februar, UTC-Konsistenz bei
Zeitzonen-Unterschieden), Job-Lauf mit Audit-Einträgen, Wiederherstellen
@@ -270,7 +275,8 @@ manuelle Synchronisation für die Redaktion, deutsche sortier-/filterbare Liste.
### Datentabelle
Eigene Migration `0001_neuheiten_tabelle`, Tabelle `neuheiten`
Eigene Migrationen `0001_neuheiten_tabelle`, `0002_bgg_id_nullable`,
`0003_quellen_status` und `0004_bild_url`; Tabelle `neuheiten`
(SQLite ↔ Postgres portabel):
| Spalte | Inhalt |
@@ -279,6 +285,7 @@ Eigene Migration `0001_neuheiten_tabelle`, Tabelle `neuheiten`
| `verlag` / `autor` | Verlag bzw. Autor(en), kommagetrennt |
| `erscheinungsjahr` | Erscheinungsjahr — BGG liefert nur das Jahr, kein genaues Datum |
| `bgg_id` | BoardGameGeek-ID, eindeutig → Merge-Kriterium des Syncs |
| `bild_url` | URL des Coverbildes (nullable; BGG-Image/-Thumbnail oder Bild der Web-Quelle) |
| `status` | `neuheit` (spätere Plugins: Planung/Archiv) |
| `quelle` | Datenquelle (`boardgamegeek`) |
| `erstellt_am` / `aktualisiert_am` | Zeitstempel |
@@ -288,6 +295,14 @@ Eigene Migration `0001_neuheiten_tabelle`, Tabelle `neuheiten`
Pro Suchbegriff: `/search?type=boardgame&query=…` → IDs sammeln →
`/thing?id=…&type=boardgame` in Batches à 20 IDs.
**Coverbilder:** Aus der Thing-Antwort wird die Bild-URL extrahiert
(bevorzugt das große `<image>`, sonst das kleinere `<thumbnail>`;
protokoll-relative URLs werden auf https normalisiert) und in
`bild_url` gespeichert. Beim Update bestehender Einträge wird das Bild
mitgepflegt; fehlt es in einer Antwort, bleibt ein bereits gespeichertes
Bild erhalten. Bilder werden direkt von BGG eingebettet (Hotlinking,
kein Download) — im UI immer mit `loading="lazy"` und `decoding="async"`.
- **Rate-Limit:** mindestens 1 Sekunde zwischen zwei Requests.
- **Retry mit Backoff:** exponentielles Zurückhalten bei 5xx, 429 und
Netzwerkfehlern; HTTP 202 (BGG-Warteschlange) wird gemäß `Retry-After`
@@ -344,7 +359,9 @@ Neben BGG crawlt das Plugin zusätzlich redaktionell gepflegte Web-Quellen
Adapter in `plugins/neuheiten/quellen/` — mit Rate-Limit (1 Request/s),
Retry/Backoff, konditionalen Requests (ETag/304), Fehler-Isolation pro Quelle
und Duplikat-Gate gegen die BGG-Liste. Der Crawler arbeitet standardmäßig rein
regelbasiert.
regelbasiert. Liefert eine Quelle im Listenelement ein Bild (`img` mit `src`
bzw. `data-src`), wird dessen URL in `bild_url` übernommen — sonst bleibt das
Feld leer.
Optional kann eine **LLM-Hilfsstufe** (`plugins/neuheiten/quellen/ki_hilfe.py`)
zugeschaltet werden — konsistent zur Dedup-KI über die
@@ -452,12 +469,14 @@ laufen automatisch durch die dedup-Prüfung.
### Datentabelle
Eigene Migration `0001_planungsliste`, Tabelle `planungsliste`:
Eigene Migrationen `0001_planungsliste` und `0002_bild_url`,
Tabelle `planungsliste`:
| Spalte | Inhalt |
|--------|--------|
| `titel` / `verlag` / `autor` | Spieldaten |
| `bgg_id` | BoardGameGeek-ID (Match-Kriterium der dedup-Prüfung) |
| `bild_url` | URL des Coverbildes (nullable; wird beim Verschieben aus den Neuheiten übernommen) |
| `ausgabe` | Magazin-Ausgabe, z. B. „3/2025“ (mehrere parallel möglich) |
| `rezensent_id` | Zuordnung (FK auf die Benutzer-Tabelle des Kerns) |
| `status` | `offen``in_bearbeitung``abgeschlossen` |
@@ -501,9 +520,10 @@ unverändert zurück in die aktive Liste verschieben.
### Datentabellen
Eigene Migration `0001_archiv_tabellen` mit zwei Spiegel-Tabellen
(SQLite ↔ Postgres portabel, ohne Fremdschlüssel — archivierte Zeilen
überleben auch das Löschen eines Benutzers):
Eigene Migrationen `0001_archiv_tabellen` und `0002_bild_url`
(Spiegel-Tabellen, SQLite ↔ Postgres portabel, ohne Fremdschlüssel —
archivierte Zeilen überleben auch das Löschen eines Benutzers;
`bild_url` wird mit archiviert und beim Wiederherstellen zurückgeschrieben):
| Tabelle | Spiegel von | Besondere Spalten |
|---------|-------------|-------------------|
@@ -639,6 +659,31 @@ Docker/Podman Compose ist vorgesehen (Henry-Lab, danach Kundenhardware).
Für Postgres genügt `SPIELE_DATABASE_URL`; die Migrationen sind portabel
geschrieben.
## Oberfläche: Coverbilder & responsives Layout
**Coverbilder:** Die Listen (Neuheiten, Planung, Archiv) zeigen je Eintrag
eine kleine Cover-Vorschau (40×56 px, `loading="lazy"`, `decoding="async"`,
Alt-Text = Spieltitel); die Detail-/Prüf-Ansichten der Planung zeigen das
Cover großformatig (max. 200 px Breite). Fehlt ein Bild, erscheint ein
dezenter CSS-Platzhalter mit Spielicon-Zeichen. Eingebettet wird per
Hotlink direkt von BGG bzw. der jeweiligen Quelle — es werden keine Bilder
heruntergeladen oder lokal gespeichert.
**Responsive Verhalten:** Das Basis-Layout ist mobil-first:
- Navigation: Ab Tablet (`md:`) horizontale Leiste; auf Mobilgeräten ein
Burger-Menü (Alpine.js) mit vollhöhen Touch-Zielen.
- Listen: Auf schmalen Screens (<768 px) kollabieren Neuheiten, Planung und
Archiv zu Karten-Ansichten — Cover links, Titel + Metadaten rechts,
Status-Badge oben. Desktop behält die Tabellenform.
- Formulare (Planung, Benutzerverwaltung, Einstellungen): vollbreite
Eingabefelder mit Labels oberhalb auf Mobil.
- Sync-Übersicht und Audit-Log bleiben Tabellen und scrollen horizontal
(`overflow-x-auto`).
- Buttons/Links haben auf Mobil mindestens 44 px Höhe (Touch-Ziel).
Getestet gegen 375 px (Smartphone), 768 px (Tablet) und Desktop.
## Projekt-Fortschritt
> Diese Tabelle ist der Live-Status. Sie wird bei jedem Push aktualisiert.
@@ -654,7 +699,7 @@ geschrieben.
| 7 | Plugin `archiv` (12-Monats-Autopilot, Wiederherstellen, täglicher Job) | ✅ fertig |
| 8 | Plugin `erinnerung` (Redaktionsschluss pro Ausgabe, 4-Wochen-Erinnerung, Tages-Job) | ✅ fertig |
| 9 | Plugin `export` (CSV + PDF via WeasyPrint) | ✅ fertig |
| 10 | Integrationstests über alle Plugins | ✅ 217 Tests grün |
| 10 | Integrationstests über alle Plugins | ✅ 316 Tests grün |
| 11 | Deployment auf swen.henry.insight-it.de (Compose + Traefik, Live-Check) | ✅ live |
Legende: ✅ fertig · 🔄 in Arbeit · ⏳ offen · ⚠️ fertig mit offenen Punkten