Files
tessera-ctl/.planning/todos/completed/2026-08-11-tender-radar-einstellungen-mischen-rollen.md
schalli 809afbce13 test(17-03): Tests fuer beide Seiten, Backlog-Punkt abgeschlossen
- RssFeedListForm.test.tsx auf scope umgestellt: personal zeigt eigene Feeds
  editierbar + plattformweite als schlichte, nicht bedienbare Liste darunter;
  platform zeigt nur plattformweite Feeds, eigene Feeds tauchen dort gar
  nicht auf; beide Faelle pruefen den scope-Parameter von createRssFeed
- settings-roles.test.tsx (neu): USER sieht Hinweis+Verweis statt
  Bedienelemente, ADMIN/SUPER_ADMIN sehen Abrufintervall+RSS-Feeds, unbekannte
  Rolle zeigt weder-noch, entfernte Abschnittsueberschriften kommen nirgends
  mehr vor
- my-sources.test.tsx (neu): alle drei Abschnittsueberschriften vorhanden,
  Verweis auf die Administrationsseite nur bei ADMIN/SUPER_ADMIN
- Erwartete Texte in allen drei Testdateien von Hand geschrieben, nicht aus
  der gleichen next-intl-Zuordnung abgeleitet, die die Komponenten benutzen
- Backlog-Punkt 2026-08-11-tender-radar-einstellungen-mischen-rollen.md nach
  todos/completed/ verschoben, mit Resolution-Abschnitt: beide Nutzer-
  Entscheidungen (eigene Quellen je Nutzer, Trefferliste bleibt
  plattform-global) und die Antwort auf offenen Punkt 4 (eigene
  nutzerseitige Modulseite als Vorbild fuer kuenftige Module) dokumentiert

Verifikation: Web 205/205, API src/tenders 362/362, beide Typpruefungen
fehlerfrei.
2026-08-12 12:02:36 +02:00

6.1 KiB

created, completed, title, area, severity, trigger, files, resolution_commits
created completed title area severity trigger files resolution_commits
2026-08-11 2026-08-12 Ausschreibungs-Radar — Einstellungsseite mischt Administration und persoenliche Einstellung tender-radar minor wenn das Modul das erste Mal von normalen Nutzern verwendet wird, nicht nur von Admins
apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx
apps/web/src/app/(portal)/settings/general/account
apps/api/src/tenders/tenders.controller.ts
05b1d29
55ceb24
adb72f6
9616155
7dee116
4100bb5
150046e

Problem

/modules/tender-radar/settings traegt vier Abschnitte, die drei verschiedenen Zustaendigkeiten gehoeren:

Abschnitt Gehoert
Quelle (Abrufintervall, Aktiv) Plattform-Administration
RSS-Feeds Plattform-Administration (ausdruecklich fuer alle Mandanten gleich)
E-Mail-Alerts (Postfach) Mandanten-Administration
Benachrichtigungen (Digest-Intervall) dem einzelnen Nutzer

Das Digest-Intervall ist die einzige persoenliche Einstellung auf der Seite — TenderNotificationPref haengt an userId, nicht am Mandanten (NOTIFY-01/D-03). Sie steht als letzter Abschnitt unter drei Bloecken, die ein normaler Nutzer weder aendern darf noch braucht. Wer nur seinen Digest auf woechentlich stellen will, scrollt an Abrufintervallen, Feed-URLs und Postfach-Zugangsdaten vorbei.

Kein Berechtigungsloch: die zugehoerigen API-Endpunkte (source-config, rss-feeds, Postfach) sind serverseitig mit @Roles(ADMIN, SUPER_ADMIN) abgesichert (tenders.controller.ts). Ein normaler Nutzer kann dort also nichts verstellen — er sieht die Bedienelemente aber und laeuft beim Speichern in eine Fehlermeldung, weil die Seite selbst keine Rollenpruefung hat.

Aufgefallen am 2026-08-11 beim Erklaeren des Moduls: der User fragte, wo man die Benachrichtigungsfrequenz einstellt, und die ehrliche Antwort war "ganz unten auf der Admin-Seite".

Solution

Richtung, nicht beschlossen — der zweite Punkt ist eine Produktfrage:

  1. Die Admin-Abschnitte hinter eine Rollenpruefung legen, damit ein normaler Nutzer sie gar nicht erst sieht. Kleinster sinnvoller Schritt, loest den Fehlermeldungs-Fall und den groessten Teil der Verwirrung.

  2. Wohin gehoert die persoenliche Einstellung? Es gibt bereits /settings/general/account fuer nutzereigene Einstellungen. Drei Varianten:

    • Digest-Intervall dorthin verschieben — konsequent, aber modulfremde Einstellungen in einer allgemeinen Seite sammeln sich mit jedem weiteren Modul an.
    • Im Modul lassen, aber sichtbar abgetrennt und oberhalb der Admin-Bloecke.
    • Eine eigene, nutzerseitige Modulseite ("Meine Benachrichtigungen") getrennt von der Admin-Seite.

    Die Antwort betrifft nicht nur dieses Modul: DKV-Fleet hat dieselbe Bauform (modules/dkv-fleet/settings), und jedes kuenftige Modul mit persoenlichen Einstellungen wird die Frage erneut stellen. Sinnvollerweise einmal grundsaetzlich entscheiden statt pro Modul.

Resolution (2026-08-12)

Beim Durchsprechen dieses Zettels mit dem User stellte sich heraus, dass die urspruengliche Diagnose zu eng war — nicht nur das Digest-Intervall gehoerte dem einzelnen Nutzer, auch das Postfach und die RSS-Feeds waren inhaltlich falsch zugeschnitten ("Wenn Person A ihre Ausschreibungen auf kontoa@mail.de und Person B von kontob@mail.de holt, macht eine gemeinsame Einstellung keinen Sinn"). Der User hat festgehalten, dass seine urspruengliche Zustimmung zur gemeinsamen Konfiguration auf einer missverstaendlichen Erklaerung meinerseits beruhte — keine Meinungsaenderung, eine Korrektur. Daraus wurde Phase 17 mit drei Plaenen statt einer kleinen Rollenpruefung:

Zwei Entscheidungen, die der User im Gespraech getroffen hat:

  • Eigene Quellen je Nutzer. TenderEmailConfig wechselt von tenantId @unique auf userId @unique (D-01), TenderRssFeedSource bekommt ein nullable userId — plattformweite Feeds (der seit Phase 14 aktive service.bund.de-Feed) bleiben fuer alle bestehen, jeder Nutzer kann zusaetzlich eigene Feeds anlegen (D-02). Umgesetzt in 05b1d29/55ceb24 (Plan 17-01) und adb72f6/9616155/7dee116 (Plan 17-02).
  • Trefferliste bleibt plattform-global (D-05, harte Grenze). Explizit gegengefragt und bestaetigt: "Reicht dir 'jeder speist seine eigenen Quellen ein, gesehen wird alles gemeinsam'?" → "Ja, plane". Tender bleibt ohne tenantId/RLS — geaendert wird nur, WER Quellen einspeist, nicht WER Treffer sieht. Was ueber das Postfach oder einen Feed von Nutzer A hereinkommt, steht danach auch in der Trefferliste von Nutzer B desselben Mandanten. Diese Konsequenz benennt die Oberflaeche jetzt ehrlich (mySources.intro, mySources.platformFeedsNote).

Offener Punkt 4 (wohin mit den nutzereigenen Abschnitten) ist damit ebenfalls beantwortet: eine eigene, nutzerseitige Modulseite (/modules/tender-radar/my-sources, "Meine Quellen") statt einer Erweiterung von /settings/general/account — modulspezifische Einstellungen bleiben beim Modul statt sich auf einer allgemeinen Seite anzusammeln. Das ist ab jetzt die Bauform, an der sich DKV-Fleet und kuenftige Module mit persoenlichen Einstellungen orientieren.

Offener Punkt 5 (Rollenpruefung) schliesst Plan 17-03 (4100bb5, 150046e): /modules/tender-radar/settings traegt nur noch, was tatsaechlich Plattform-Administration ist — Abrufintervall und plattformweite RSS-Feeds — und prueft die Rolle aus dem Anmelde-Speicher rein zur Anzeige (die verbindliche Pruefung bleibt serverseitig, @UseModule('tender-radar') + Inline-Rollencheck fuer scope: 'platform', T-17-08). Ein normaler Nutzer sieht dort keine Bedienelemente mehr, die beim Speichern in eine Fehlermeldung liefen — genau der urspruengliche Anlass dieses Zettels. Postfach, eigene Feeds und Benachrichtigungsintervall leben jetzt vollstaendig auf "Meine Quellen"; das Zahnrad auf der Modulseite fuehrt jeden Nutzer dorthin.

Nicht ausgefuehrt: die Browser-Gegenprobe aus den human-check-Schritten der Plaene 17-01 bis 17-03 (kein Browser-Tool in den jeweiligen Sitzungen verfuegbar) — festgehalten in WINDOWS.md (#7 und Folge-Eintraege), vor /gsd-ship nachzuholen.