P1: Rollenbasiertes Dashboard mit Plugin-Karten

- Plugin-Vertrag erweitert: optionaler Hook dashboard_karten(user) +
  DashboardKarte-Dataclass (Kern bleibt schlank, Fachlogik im Plugin)
- Karten: Neuheiten+System-Status (neuheiten), Planungs-Stats bzw.
  eigene Einträge (planung), offene Dedup-Konflikte (dedup), nächste
  Redaktionsschlüsse (erinnerung), ungelesene Benachrichtigungen
  (benachrichtigung), letzte Audit-Einträge (audit-log, nur Admin),
  Benutzer-Anzahl (Kern, nur Admin)
- 'Geladene Plugins' als aufklappbarer Admin-Technikbereich
This commit is contained in:
Flo Hartmann
2026-08-26 08:48:30 +00:00
parent 742c265df8
commit 9bd3b700c4
9 changed files with 326 additions and 17 deletions

View File

@@ -25,7 +25,7 @@ from sqlalchemy.orm import Session
from redaktionskern.auth.deps import get_db, require_user
from redaktionskern.auth.models import User
from redaktionskern.contracts import BasePlugin, Migration, NavEntry
from redaktionskern.contracts import BasePlugin, DashboardKarte, Migration, NavEntry
from .adapter import BenachrichtigungsAdapter, adapter_aus_konfiguration
from .konfiguration import aus_umgebung
@@ -80,6 +80,28 @@ class BenachrichtigungPlugin(BasePlugin):
def navigation(self) -> list[NavEntry]:
return [NavEntry(label=self.title, url="/benachrichtigung")]
def dashboard_karten(self, user: User) -> list[DashboardKarte]:
"""Dashboard: Anzahl ungelesener In-App-Benachrichtigungen."""
with self.context.session_factory() as db:
ungelesen = db.scalar(
select(func.count())
.select_from(Benachrichtigung)
.where(
Benachrichtigung.user_id == user.id,
Benachrichtigung.gelesen.is_(False),
)
) or 0
return [
DashboardKarte(
titel="Ungelesene Benachrichtigungen",
wert=str(ungelesen),
beschreibung="Nachrichten in deinem Posteingang",
link_url="/benachrichtigung",
link_label="Zum Posteingang",
ton="warnung" if ungelesen else "ok",
)
]
# ---------------- Öffentliche Plugin-API ----------------
async def send_notification(