Benachrichtigungen
In-App-Benachrichtigungen über das persistente Notification-Modell (Glocke im
Header). Bewusst keine Browser-/Push-Benachrichtigungen.
Wenn ein Beitrag in einer aktiven Veranstaltung oder Veranstaltungsreihe
erstellt wird, erhalten alle anderen bestätigten Teilnehmenden dieses Kontexts
eine Benachrichtigung. Der Autor selbst wird ausgeschlossen. Bei einem Termin
innerhalb einer Reihe werden direkte Termin- und Reihen-Zuordnungen
zusammengeführt und doppelte Empfänger entfernt. Zusätzlich erhält die
hinterlegte Kontaktperson (contactAccountId) des Events/der Reihe bei einem
neuen Beitrag (NEW_POST) ebenfalls eine Meldung, auch wenn sie selbst keine
bestätigte Teilnahme hat — bewusst nur bei neuen Beiträgen, nicht bei jeder
einzelnen Antwort, um Vielschreiber-Threads nicht zu einer Benachrichtigungsflut
für die verantwortliche Person zu machen (TASK-303).
Für Beiträge/Antworten direkt auf einer Reihe (nicht auf einem einzelnen
Termin) gilt zusätzlich eine Aktivitätsregel (Entscheidung 2026-07-30,
TASK-306): NEW_POST und NEW_REPLY verteilen nur, wenn die Reihe aktuell
EventStatus.ACTIVE ist — analog zur bereits bestehenden Regel für einzelne
Termine. Admin-/Management-Accounts können zwar auch in Entwurfs- oder
archivierten Reihen posten (Zugriffsprüfung kennt keine Statusgrenze), das
löst dort aber bewusst keine Meldung an bestätigte Teilnehmende aus.
Statuswechsel-Meldungen (EVENT_UPDATED, z. B. „Reihe archiviert”) sind davon
ausdrücklich ausgenommen — sie kündigen genau den Wechsel in/aus ACTIVE an
und würden sich sonst selbst unterdrücken.
Benachrichtigungen zu neuen Beiträgen und Reaktionen führen direkt zum betroffenen Beitrag, Benachrichtigungen zu Antworten direkt zur jeweiligen Antwort. Liegt der Inhalt außerhalb der zuerst geladenen Beiträge, wird er gezielt nachgeladen. Wurde der Inhalt inzwischen entfernt, bleibt der Bereich „Beiträge“ der Veranstaltung als Rückfallziel sichtbar.
Empfängermatrix
Abschnitt betitelt „Empfängermatrix“Verbindliche Produktregel für alle elf NotificationType-Werte (TASK-303):
| Typ | Auslöser | Empfänger | Selbstausschluss | Linkkontext |
|---|---|---|---|---|
NEW_POST | Neuer Beitrag | Bestätigte Teilnehmende des Kontexts + Kontaktperson | Autor:in | Event/Reihe + Beitrag |
NEW_REPLY | Neue Antwort | Bestätigte Teilnehmende des Kontexts (bewusst alle, nicht nur Beteiligte — Entscheidung 2026-07-30) | Autor:in | Event/Reihe + Antwort |
REACTION | Reaktion gesetzt, geändert oder entfernt | Autor:in des Beitrags (einzelne Person) | Reagierende Person = Autor:in | Event/Reihe + Beitrag |
DOCUMENT | Dokument hochgeladen | Bestätigte Teilnehmende des Kontexts | Hochladende Admin/Management-Person | Event/Reihe + #dokumente |
RECORDING | Aufzeichnung hochgeladen | Bestätigte Teilnehmende des Kontexts | Hochladende Admin/Management-Person | Event/Reihe + #aufzeichnungen |
EVENT_UPDATED | Termin-, Titel- oder Statusänderung, Löschung eines Events, Entfernen eines Termins aus einer Reihe | Bestätigte Teilnehmende des Kontexts | Bearbeitende Admin/Management-Person | Event/Reihe |
FEEDBACK | Feedback-Link nach Ende von Event/Reihe verfügbar | Die betroffene Person selbst (systemgeneriert, kein Auslöser-Account) | — | Event/Reihe |
PARTICIPANT_ADDED | Zuordnung zu Event/Reihe angelegt | Die zugeordnete Person (einzelne Person) | — (Ziel ist immer die betroffene Person) | Event/Reihe |
PARTICIPANT_REMOVED | Zuordnung zu Event/Reihe entfernt | Die entfernte Person (einzelne Person) | — | Immer Übersicht (/), siehe unten |
RSVP_UPDATED | Zu-/Absage einer Teilnahme | Alle ADMIN- und MANAGEMENT-Accounts | Nicht nötig — Auslöser ist immer Role.PARTICIPANT | Event/Reihe |
ACCOUNT_DELETED | Selbstlöschung des eigenen Kontos | Alle ADMIN-Accounts (bewusst nicht Management) | Nicht nötig — betroffener Account wird geleert | Übersicht (/) |
PARTICIPANT_ADDED/PARTICIPANT_REMOVED und ACCOUNT_DELETED haben keinen
klassischen „Selbstausschluss”, weil der Empfängerkreis dort ohnehin nicht mit
dem Auslöser überschneidet (Zielperson bzw. Admin-Rundruf statt
Teilnehmenden-Liste).
Reaktionsmeldungen bilden den aktuellen Zustand ab
Abschnitt betitelt „Reaktionsmeldungen bilden den aktuellen Zustand ab“REACTION-Benachrichtigungen sind kein historisches Aktivitäts-Log, sondern
bilden den aktuellen Reaktionszustand ab (Entscheidung 2026-07-30, TASK-304):
Pro Beitrag und reagierender Person existiert höchstens eine Meldung.
- Reagieren (erstmalig oder mit geändertem Emoji): Die bestehende Meldung
wird per Upsert aktualisiert (neues Emoji, wieder ungelesen gesetzt) statt
eine weitere anzulegen. Ein Unique-Index auf
(accountId, dedupeKey)mitdedupeKey = "reaction:<postId>:<reactorId>"macht das auch bei gleichzeitigen Änderungen atomar — verhindert genau das Duplikat-Problem, das vor dieser Entscheidung live beobachtet wurde (mehrfache Meldungen für dieselbe Reaktion durch schnell wiederholtes Reagieren). - Reaktion entfernen: Die zugehörige Meldung wird gelöscht, nicht nur unverändert belassen — keine Reaktion mehr bedeutet keine Meldung mehr.
dedupeKey ist derselbe Mechanismus, der bereits FEEDBACK-Meldungen
dedupliziert (TASK-302); das Feld hieß zunächst feedbackContext und wurde
zu diesem Zweck auf dedupeKey verallgemeinert.
Umfang von Änderungsbenachrichtigungen
Abschnitt betitelt „Umfang von Änderungsbenachrichtigungen“EVENT_UPDATED deckt folgende Anlässe ab (Entscheidung 2026-07-30, TASK-305):
- Feldänderungen an einem Event: Datum/Uhrzeit, Format, Ort, Online-Link,
Titel und Status. Alle Änderungen aus einem Speichervorgang werden zu
einer Meldung gebündelt (
buildEventChangessammelt die betroffenen Felder, erst danach wird eine einzelne Notification erzeugt) — kein Aufsplitten in mehrere Meldungen pro Speichervorgang. - Löschung eines Events (
deleteEvent): Empfänger werden vor dem Löschen erfasst (direkte Termin-Teilnahme + Reihen-Teilnahme, falls das Event Teil einer Reihe war), die Meldung selbst wird erst danach ohneeventIdverschickt — der Datensatz existiert zu dem Zeitpunkt nicht mehr und würde überonDelete: Cascadesonst die gerade erst angelegte Notification gleich wieder mitlöschen. War das Event Teil einer Reihe, bleibt die Reihen-seriesIdals Linkziel erhalten. - Entfernen eines Termins aus einer Reihe (
removeEventFromSeries): Der Termin selbst bleibt bestehen (wird nur eigenständig), Empfänger sind sowohl die direkten Termin-Teilnehmenden als auch die Reihen-Teilnehmenden, da ein Reihentermin überwiegend über die Reihen-Zuordnung wahrgenommen wird, nicht über eine separate Termin-Zuordnung.
Bewusst nicht benachrichtigungswürdig: rein redaktionelle Änderungen wie
Beschreibungstext oder Thumbnail — diese lösen keine EVENT_UPDATED-Meldung
aus, um die Glocke nicht mit für Teilnehmende operativ irrelevanten Hinweisen
zu fluten.
Änderungsbeschreibungen sind sprachneutral gespeichert
Abschnitt betitelt „Änderungsbeschreibungen sind sprachneutral gespeichert“EVENT_UPDATED-Meldungen (Feldänderungen, Reihen-Statuswechsel, neuer Termin)
speichern seit der Entscheidung 2026-07-30 (TASK-307) keine vorformulierten
deutschen Texte mehr in paramsJson, sondern stabile Schlüssel und Rohwerte:
- Feldänderungen:
buildEventChangesliefert strukturierte Deskriptoren ({ key: "title" },{ key: "format", from, to }, …) statt fertiger Sätze. Gespeichert wirdchangeKeys(kommagetrennte Schlüssel) plusformatFrom/formatTobzw.statusFrom/statusToals rohe Enum-Werte. - Reihen-Statuswechsel:
statusFrom/statusTostatt vorformatierterbeforeLabel/afterLabel. - Neuer Termin:
dateIso(ISO-String) statt eines mitde-DEfest formatierten Datumstexts.
Übersetzt wird erst beim Rendern in components/notification-bell.tsx, mit
der aktiven Locale der lesenden Person — die ist zum Anlegezeitpunkt der
Meldung nicht bekannt, locale liegt nur als Cookie der aktuellen Anfrage
vor, nicht als Feld am Account. Format- und Statuswerte nutzen dafür die
bereits bestehenden lokalisierten Maps t.eventFormat/t.eventStatus.
Bereits vor dieser Änderung gespeicherte Meldungen enthalten die neuen Rohwerte nicht und behalten dadurch unverändert ihren damals auf Deutsch vorformulierten Text als Rückfall (kein Blindtext, keine kaputte Anzeige, aber weiterhin einsprachig Deutsch für historische Einträge).
Zustellung und Fehlerisolation
Abschnitt betitelt „Zustellung und Fehlerisolation“Server Actions warten die persistente Anlage ausgelöster Benachrichtigungen ab, bevor sie antworten oder weiterleiten. Die Zustellung läuft dabei bewusst innerhalb der lokal gehosteten Anwendung und benötigt weder eine externe Queue noch einen Cloud-Dienst.
Ein reiner Benachrichtigungsfehler macht eine bereits erfolgreich gespeicherte Fachaktion nicht rückgängig. Er wird stattdessen mit Benachrichtigungstyp, Auslöser, handelndem Account und betroffenem Event-, Reihen- oder Inhaltskontext strukturiert protokolliert. Das gilt einheitlich für Beiträge, Antworten, Reaktionen, Teilnahmeentscheidungen, Zuordnungen, Importe, Inhaltsuploads und administrative Änderungen.
Linkziele
Abschnitt betitelt „Linkziele“Der Link einer Benachrichtigung wird zentral aufgeloest (getNotificationHref
in lib/notification-links.ts) und fragt dafuer den aktuellen Datenbankstand
ab, nicht den Stand zum Anlegezeitpunkt der Meldung:
- Gehoert der betroffene Termin inzwischen zu einer Reihe, fuehrt der Link
zur Reihenseite mit vorselektiertem Termin (
/series/<id>?event=<id>) statt zu/event/<id>— Reihentermine sind dort bewusst nicht direkt erreichbar (notFound()) und nur ueber die Reihenseite zugaenglich. - Meldungen zu Dokumenten und Aufzeichnungen springen zusaetzlich zum
passenden Inhaltsbereich (
#dokumente/#aufzeichnungen). PARTICIPANT_REMOVEDfuehrt immer zur Uebersicht (/), da der Zugriff auf den ehemaligen Kontext zu diesem Zeitpunkt bereits entzogen ist.
Initiale und nachgeladene Meldungen (/api/notifications/more) nutzen
dieselbe Funktion.
Empfänger von Zu-/Absagen
Abschnitt betitelt „Empfänger von Zu-/Absagen“Wenn Teilnehmende einer Veranstaltung oder Reihe zu- oder absagen
(RSVP_UPDATED), erhalten alle Accounts mit Rolle ADMIN und MANAGEMENT
eine Meldung — nicht nur Admins. Management-Accounts dürfen Events und Reihen
über requireManagementOrAdmin genauso verwalten wie Admins, ohne diese
Meldung würden sie Zu-/Absagen für die von ihnen betreuten Veranstaltungen
verpassen. Die hinterlegte Kontaktperson eines Events/einer Reihe ist bei der
Auswahl im Formular ohnehin auf ADMIN/MANAGEMENT beschränkt und damit über
diese Rollenkombination bereits erreicht — eine separate Sonderregel für die
Kontaktperson ist daher nicht nötig.