P1: Einheitliche klassische Pagination (Neuheiten, Planung, Audit-Log)

- Infinite Scroll ersetzt durch Zurück/Weiter + Seitenzahlen-Fenster
  (funktioniert ohne JavaScript)
- Gemeinsames Makro seitennavigation.html (aria-current auf aktiver
  Seite, deaktivierte Buttons als span)
- Neuheiten: Pagination bleibt beim HTMX-Filter-Swap innerhalb von
  #neuheiten-liste konsistent; Filter/Sortierung/Jahr bleiben in den
  Seiten-URLs erhalten
- Audit-Log: gleiche Optik statt reiner Text-Links
This commit is contained in:
Flo Hartmann
2026-08-26 08:59:49 +00:00
parent 117b34653c
commit 7645f6a791
7 changed files with 104 additions and 34 deletions

View File

@@ -24,7 +24,8 @@ der Eintrage-Vorgang bricht.
from __future__ import annotations
import logging
from urllib.parse import quote
from math import ceil
from urllib.parse import quote, urlencode
from fastapi import Depends, Form, Request
from fastapi.responses import RedirectResponse
@@ -299,13 +300,22 @@ class PlanungPlugin(BasePlugin):
gesamt = db.scalar(
select(func.count()).select_from(abfrage.subquery())
) or 0
gesamt_seiten = max(1, ceil(gesamt / pro_seite))
seite_aktuell = min(max(seite_nr, 1), gesamt_seiten)
eintraege = db.scalars(
abfrage.order_by(
Planungseintrag.aktualisiert_am.desc(), Planungseintrag.id.desc()
)
.offset((max(seite_nr, 1) - 1) * pro_seite)
.offset((seite_aktuell - 1) * pro_seite)
.limit(pro_seite)
).all()
def _seiten_url(nr: int) -> str:
parameter = {"seite_nr": str(nr)}
if status in GUELTIGE_STATUS:
parameter["status"] = status
return f"/planung?{urlencode(parameter)}"
namen = {
benutzer.id: (benutzer.display_name or benutzer.username)
for benutzer in db.scalars(select(User)).all()
@@ -326,8 +336,9 @@ class PlanungPlugin(BasePlugin):
"meldung": meldung[:300],
"fehler": fehler[:300],
"anzahl": gesamt,
"hat_weitere": max(seite_nr, 1) * pro_seite < gesamt,
"naechste_seite": max(seite_nr, 1) + 1,
"seite": seite_aktuell,
"gesamt_seiten": gesamt_seiten,
"seiten_url": _seiten_url,
},
)

View File

@@ -217,13 +217,8 @@
</tbody>
</table>
</div>
{% if hat_weitere %}
<div id="planung-lade-mehr"
hx-get="/planung?seite_nr={{ naechste_seite }}&status={{ status_filter }}"
hx-trigger="revealed delay:150ms" hx-swap="outerHTML">
<div class="py-6 text-center text-sm text-slate-400">Lade weitere Eintr&auml;ge&nbsp;&hellip;</div>
</div>
{% endif %}
{% from "seitennavigation.html" import seitennavigation %}
{{ seitennavigation(seiten_url, seite, gesamt_seiten) }}
<!-- Lösch-Bestätigungsdialog (Progressive Enhancement: ohne JavaScript
sendet das Formular sofort, mit Alpine öffnet sich dieser Dialog). -->
<div x-show="loeschFormular !== null" x-cloak