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:
@@ -71,6 +71,33 @@ def _tabelle_anlegen(conn) -> None:
|
||||
ArchivPlanung.__table__.create(conn, checkfirst=True)
|
||||
|
||||
|
||||
def _spalte_existiert(conn, tabelle: str, spalte: str) -> bool:
|
||||
"""Dialekt-agnostische Spaltenprüfung (PRAGMA bzw. information_schema)."""
|
||||
if conn.dialect.name == "sqlite":
|
||||
info = conn.exec_driver_sql(f"PRAGMA table_info({tabelle})").fetchall()
|
||||
return any(zeile[1] == spalte for zeile in info)
|
||||
existiert = conn.exec_driver_sql(
|
||||
"SELECT column_name FROM information_schema.columns "
|
||||
"WHERE table_name = :tabelle AND column_name = :spalte",
|
||||
{"tabelle": tabelle, "spalte": spalte},
|
||||
).scalar()
|
||||
return existiert is not None
|
||||
|
||||
|
||||
def _bild_url_spalten(conn) -> None:
|
||||
"""Migration 0002: Spalte bild_url in beiden Spiegel-Tabellen.
|
||||
|
||||
`ALTER TABLE … ADD COLUMN` mit nullable VARCHAR ohne Default ist
|
||||
SQLite- und Postgres-portabel und erhält bestehende Zeilen.
|
||||
"""
|
||||
for tabelle in ("archiv_neuheiten", "archiv_planung"):
|
||||
if _spalte_existiert(conn, tabelle, "bild_url"):
|
||||
continue
|
||||
conn.exec_driver_sql(
|
||||
f"ALTER TABLE {tabelle} ADD COLUMN bild_url VARCHAR(500)"
|
||||
)
|
||||
|
||||
|
||||
class ArchivPlugin(BasePlugin):
|
||||
name = "archiv"
|
||||
title = "Archiv"
|
||||
@@ -89,7 +116,10 @@ class ArchivPlugin(BasePlugin):
|
||||
# ---------- Plugin-Vertrag ----------
|
||||
|
||||
def migrations(self) -> list[Migration]:
|
||||
return [Migration(version="0001_archiv_tabellen", up=_tabelle_anlegen)]
|
||||
return [
|
||||
Migration(version="0001_archiv_tabellen", up=_tabelle_anlegen),
|
||||
Migration(version="0002_bild_url", up=_bild_url_spalten),
|
||||
]
|
||||
|
||||
def navigation(self) -> list[NavEntry]:
|
||||
return [NavEntry(label=self.title, url="/archiv")]
|
||||
|
||||
Reference in New Issue
Block a user