--- phase: 17-eigene-ausschreibungs-quellen-je-nutzer plan: 03 subsystem: ui tags: [nextjs, next-intl, zustand, vitest, testing-library] # Dependency graph requires: - phase: 17-01 provides: "TenderEmailConfig per-user, /modules/tender-radar/my-sources (Seite mit Postfach-Abschnitt)" - phase: 17-02 provides: "RssFeedSource.isPlatformWide-Vertrag (GET/POST /rss-feeds, scope-Wunsch, Besitz-basierter Loeschschutz)" provides: - "my-sources/page.tsx vollstaendig: Postfach + eigene Feeds + Digest-Intervall auf einer Seite" - "RssFeedListForm mit scope-Prop (personal/platform) — eine Komponente fuer beide Seiten" - "DigestIntervalForm als eigenstaendige Komponente (aus settings/page.tsx herausgeloest)" - "settings/page.tsx auf reine Administration reduziert, Anzeige-Rollenpruefung ueber useAuthStore" - "Zahnrad auf der Modulseite fuehrt zu /my-sources statt /settings" - "REQUIREMENTS.md SRC-01..05 Sektion + Traceability (fehlte bisher trotz Nutzung in 17-01/17-02-SUMMARY)" affects: [dkv-fleet-settings-restructuring, future-per-user-module-settings] # Actuals (#2632) actuals: tokens: 18180 tasks: 3 commits: 3 tech-stack: added: [] patterns: - "Eine Formkomponente mit einem scope-Prop (personal/platform) statt zwei fast identischer Komponenten — der Server entscheidet ueber Berechtigung, das Prop entscheidet nur ueber Darstellung und den scope-Wunsch im Request" - "Anzeige-Rollenpruefung ueber useAuthStore in der Seite selbst (drei Zustaende: unbekannt/Platzhalter, erlaubt/Inhalt, verweigert/Hinweis+Verweis) statt eines Guards oder Redirects — verbindliche Pruefung bleibt serverseitig" - "Verhaltensunveraendertes Herausloesen einer Komponente aus einer Seite ohne neue i18n-Schluessel, wenn nur der Ort wechselt, nicht das Verhalten (DigestIntervalForm)" key-files: created: - "apps/web/src/app/(portal)/modules/tender-radar/settings/components/DigestIntervalForm.tsx" - "apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx" - "apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx" modified: - apps/web/src/lib/tender-radar-api.ts - "apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx" - "apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx" - "apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx" - "apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx" - "apps/web/src/app/(portal)/modules/tender-radar/page.tsx" - apps/web/src/messages/de.json - apps/web/src/messages/en.json - .planning/REQUIREMENTS.md key-decisions: - "[Rule 3] settings/page.tsx auf RssFeedListForm scope=\"platform\" vorgezogen bereits in Task 1, statt erst in Task 2 — Task 1s eigener Type-Check-Verify verlangt eine kompilierbare RssFeedListForm-Prop-Signatur ueber das ganze Projekt hinweg, gleiches Muster wie in 17-01/17-02" - "[Rule 1] createRssFeed()-Antwort traegt kein isPlatformWide (nur GET /rss-feeds mappt es serverseitig, T-17-12) — RssFeedListForm setzt das Feld nach dem Anlegen lokal aus dem verwendeten scope, sonst waere ein frisch angelegter plattformweiter Feed bis zum naechsten Neuladen aus seiner eigenen Liste verschwunden" - "page.settingsTitle bleibt bestehen und wird als Verweistext auf der Nutzerseite fuer Administratoren wiederverwendet (Task 1/2), statt eines separaten neuen Schluessels — neuer Schluessel page.mySourcesTitle nur fuer das umgehaengte Zahnrad" - "REQUIREMENTS.md um die SRC-01..05-Sektion und Traceability-Zeilen ergaenzt — Luecke aus Plan 17-01/17-02 (dort bereits requirements-completed im SUMMARY-Frontmatter gefuehrt, aber nie in REQUIREMENTS.md selbst nachgezogen), gehoert inhaltlich zum Requirements-Traceability-Schritt dieses Plans" requirements-completed: [SRC-04, SRC-05] coverage: - id: D1 description: "\"Meine Quellen\" traegt alle drei nutzereigenen Abschnitte (Postfach, eigene Feeds, Benachrichtigung) auf einer Seite; ein neu angelegter eigener Feed erscheint sofort in der eigenen Liste; plattformweite Feeds erscheinen darunter als nicht bedienbare Aufzaehlung mit Administrations-Hinweis" requirement: "SRC-04" verification: - kind: unit ref: "apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx (5 Tests)" status: pass - kind: unit ref: "apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx (scope=personal-Block, 6 Tests inkl. Mischliste mit/ohne Entfernen-Knopf)" status: pass human_judgment: false - id: D2 description: "Die Administrationsseite zeigt nur noch Abrufintervall und plattformweite Feeds; ein Konto ohne ADMIN/SUPER_ADMIN-Rolle sieht dort keine Bedienelemente mehr, sondern Hinweis + Verweis auf \"Meine Quellen\"; waehrend die Rolle noch unbekannt ist, erscheint weder das eine noch das andere" requirement: "SRC-05" verification: - kind: unit ref: "apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx (5 Tests: USER/ADMIN/SUPER_ADMIN/unbekannt/entfernte Ueberschriften)" status: pass - kind: unit ref: "apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx (scope=platform-Block, 2 Tests: eigene Feeds tauchen dort nicht auf, scope-Parameter im Request)" status: pass human_judgment: false - id: D3 description: "Das Zahnrad auf der Modulseite fuehrt jeden Nutzer nach /my-sources statt zur admin-only Administrationsseite; Administratoren erreichen die Administrationsseite von dort mit einem Klick" requirement: "SRC-04" verification: [] human_judgment: true rationale: "Browser-Gegenprobe (Klick auf das Zahnrad, Navigationsziel im echten Router pruefen) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar (WINDOWS.md #8/#9). Der Link selbst ist per Testing-Library gegen den gerenderten href geprueft (my-sources.test.tsx, settings-roles.test.tsx), nicht aber die tatsaechliche Client-seitige Navigation im Next.js-Router." - id: D4 description: "Alle neuen Beschriftungen liegen in Deutsch und Englisch vor; keine mehr verwendete Beschriftung bleibt verwaist" requirement: "SRC-04" verification: - kind: unit ref: "Node-Skript-Schluesselvergleich beider Sprachdateien (in allen drei Task--Bloecken gelaufen)" status: pass human_judgment: false duration: 62min completed: 2026-08-12 status: complete --- # Phase 17 Plan 03: UI-Aufteilung — "Meine Quellen" vs. Administration Summary **Ausschreibungs-Radar-Oberflaeche in zwei Seiten getrennt: "Meine Quellen" traegt jetzt Postfach, eigene RSS-Feeds und Digest-Intervall auf einer Seite fuer jeden Modulnutzer, die Administrationsseite schrumpft auf Abrufintervall und plattformweite Feeds mit einer Anzeige-Rollenpruefung — der urspruengliche Anlass des Backlog-Punkts ist damit geschlossen.** ## Performance - **Duration:** 62 min - **Started:** 2026-08-12T09:58:00Z - **Completed:** 2026-08-12T11:00:00Z - **Tasks:** 3 - **Files modified:** 13 (3 neu, 10 geaendert) + Backlog-Punkt verschoben + REQUIREMENTS.md ergaenzt ## Accomplishments - `RssFeedSource` bekommt das serverseitig gelieferte `isPlatformWide`-Kennzeichen, `createRssFeed` einen `scope`-Parameter (`personal`/`platform`, Vorgabe `personal`) — der Server entscheidet ueber Berechtigung (T-17-08), das Frontend nur ueber Darstellung - `RssFeedListForm` bedient jetzt beide Seiten ueber ein `scope`-Prop: `scope="personal"` zeigt die eigenen Feeds editierbar und plattformweite darunter als schlichte, nicht bedienbare Liste mit Administrations-Hinweis; `scope="platform"` zeigt nur plattformweite Feeds editierbar, eigene Feeds tauchen dort gar nicht auf - `DigestIntervalForm` unveraendert aus `settings/page.tsx` herausgeloest — dieselbe Logik, dieselben Beschriftungen, jetzt eine eigenstaendige Komponente, die beide Seiten nutzen - `/modules/tender-radar/my-sources` traegt jetzt alle drei nutzereigenen Abschnitte (Postfach, eigene Feeds, Benachrichtigung) auf einer Seite; Administratoren sehen zusaetzlich einen Verweis auf die Administrationsseite - `/modules/tender-radar/settings` zeigt nur noch Abrufintervall und plattformweite Feeds; eine Anzeige-Rollenpruefung ueber den vorhandenen Anmelde-Speicher blendet die Bedienelemente fuer Konten ohne ADMIN/SUPER_ADMIN-Rolle aus (Hinweis + Verweis auf "Meine Quellen" stattdessen) — die verbindliche Pruefung bleibt serverseitig unveraendert - Das Zahnrad auf der Modulseite fuehrt jetzt zu "Meine Quellen" statt zur Administrationsseite - Backlog-Punkt `2026-08-11-tender-radar-einstellungen-mischen-rollen.md` nach `todos/completed/` verschoben, mit Resolution-Abschnitt, der beide vom User im Gespraech getroffenen Entscheidungen (eigene Quellen je Nutzer, Trefferliste bleibt plattform-global) und die Antwort auf den offenen Punkt 4 (eigene nutzerseitige Modulseite als Vorbild fuer DKV-Fleet und kuenftige Module) dokumentiert - `REQUIREMENTS.md` um die SRC-01..05-Sektion und Traceability-Zeilen ergaenzt (Luecke aus 17-01/17-02 geschlossen) ## Task Commits Jede Aufgabe wurde einzeln committet: 1. **Task 1: "Meine Quellen" wird vollstaendig — Postfach, eigene Feeds, Benachrichtigung** — `4100bb5` (feat) 2. **Task 2: Die Administrationsseite schrumpft auf das, was Administration ist** — `150046e` (feat) 3. **Task 3: Tests fuer beide Seiten und der Backlog-Punkt wird zugemacht** — `809afbc` (test) **Plan metadata:** wird mit diesem SUMMARY committet (docs) ## Files Created/Modified - `apps/web/src/lib/tender-radar-api.ts` — `RssFeedSource.isPlatformWide`, `createRssFeed(payload, scope)` - `apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx` — `scope`-Prop, editierbare/nicht-editierbare Teillisten, lokale `isPlatformWide`-Ableitung nach dem Anlegen - `apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx` — auf `scope` umgestellt, Mischlisten-Faelle fuer beide Scopes ergaenzt - `apps/web/src/app/(portal)/modules/tender-radar/settings/components/DigestIntervalForm.tsx` — neu, unveraendert aus `settings/page.tsx` herausgeloest - `apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx` — Abschnitte "Meine Feeds" und "Benachrichtigung" ergaenzt, Verweis auf Administrationsseite fuer ADMIN/SUPER_ADMIN - `apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx` — neu, 5 Tests - `apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx` — Postfach-/Benachrichtigungsabschnitt entfernt, Anzeige-Rollenpruefung ueber `useAuthStore` - `apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx` — neu, 5 Tests - `apps/web/src/app/(portal)/modules/tender-radar/page.tsx` — Zahnrad zeigt auf `/my-sources`, neue Beschriftung - `apps/web/src/messages/de.json`, `apps/web/src/messages/en.json` — neue `mySources.*`/`settings.*`/`page.mySourcesTitle`-Schluessel, drei verwaiste `settings.*`-Schluessel entfernt - `.planning/todos/completed/2026-08-11-tender-radar-einstellungen-mischen-rollen.md` — verschoben, mit Resolution-Abschnitt - `.planning/REQUIREMENTS.md` — SRC-01..05-Sektion + Traceability ergaenzt ## Decisions Made - `settings/page.tsx` bereits in Task 1 auf `RssFeedListForm scope="platform"` vorgezogen (Rule 3), volle Rollenpruefung + Abschnittsreduktion folgte in Task 2 - `createRssFeed`s Antwort traegt kein `isPlatformWide` — Komponente leitet es lokal aus dem verwendeten `scope` ab (Rule 1, sonst verschwaende ein frisch angelegter plattformweiter Feed bis zum Neuladen) - `page.settingsTitle` bleibt bestehen und dient jetzt als Verweistext auf der Nutzerseite; neuer Schluessel `page.mySourcesTitle` nur fuer das umgehaengte Zahnrad - `REQUIREMENTS.md` um SRC-01..05 ergaenzt (gehoert zum Requirements-Traceability-Schritt dieses Plans, schliesst eine Luecke aus 17-01/17-02) ## Deviations from Plan ### Auto-fixed Issues **1. [Rule 3 - Blocking] `settings/page.tsx` auf `RssFeedListForm scope="platform"` vorgezogen aus Task 2 nach Task 1** - **Found during:** Task 1 (Type-Check nach Aenderung der `RssFeedListForm`-Prop-Signatur) - **Issue:** Task 1s eigener Verify verlangt einen fehlerfreien projektweiten Type-Check; `settings/page.tsx` (Task 2s Datei) rief `` noch ohne das neue Pflicht-Prop `scope` auf - **Fix:** Minimale Ergaenzung `scope="platform"` an der bestehenden Aufrufstelle — die vollstaendige Umgestaltung der Seite (Rollenpruefung, Abschnittsreduktion) blieb Task 2 vorbehalten - **Files modified:** apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx - **Verification:** `pnpm --filter @tessera/web type-check` fehlerfrei ab Task 1 - **Committed in:** 4100bb5 (Task 1 commit) **2. [Rule 1 - Bug] `RssFeedListForm` leitet `isPlatformWide` nach dem Anlegen lokal ab** - **Found during:** Task 1 (beim Entwurf von `handleAdd` fuer beide Scopes) - **Issue:** `POST /rss-feeds` mappt `isPlatformWide` nicht (nur `GET /rss-feeds` tut das serverseitig, T-17-12) — ein frisch angelegter Eintrag haette das Feld `undefined`, wodurch ein neu angelegter plattformweiter Feed (`scope="platform"`) faelschlich aus der eigenen editierbaren Liste gefallen waere, bis zum naechsten Neuladen - **Fix:** Nach erfolgreichem `createRssFeed(...)` wird `isPlatformWide` clientseitig aus dem verwendeten `scope` gesetzt (`scope === 'platform'`), statt sich auf ein vom Server nicht geliefertes Feld zu verlassen - **Files modified:** apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx - **Verification:** RssFeedListForm.test.tsx deckt beide Scopes explizit ab ("die neue Feed landet sofort in der editierbaren Liste, mit funktionierendem Entfernen-Knopf") - **Committed in:** 4100bb5 (Task 1 commit) --- **Total deviations:** 2 auto-fixed (1× Rule 3 Kompilierfaehigkeit, 1× Rule 1 Bugfix) **Impact on plan:** Beide Anpassungen waren fuer die Korrektheit der eigentlichen Plan-Aenderung zwingend notwendig. Kein Scope Creep — Deviation 1 ist eine minimale Vorziehung der ohnehin geplanten Aenderung, Deviation 2 behebt eine waehrend der Implementierung entdeckte Inkonsistenz zwischen POST- und GET-Antwortform derselben Ressource. ## Issues Encountered - `REQUIREMENTS.md` enthielt keine SRC-Kategorie, obwohl 17-01/17-02-SUMMARY bereits `requirements-completed: [SRC-01, SRC-03]` bzw. `[SRC-02, SRC-03]` fuehrten — die Datei war seit dem Anlegen von Phase 17 nie nachgezogen worden (nur ROADMAP.md hatte die SRC-Beschreibungen). Ergaenzt als Teil des Requirements-Traceability-Schritts dieses Plans, keine separate Aenderung noetig. ## User Setup Required None — keine externe Dienstkonfiguration erforderlich. ## Next Phase Readiness - Phase 17 ist inhaltlich abgeschlossen: alle drei Plaene liefern D-01 bis D-05 vollstaendig, der Backlog-Punkt ist geschlossen, SRC-01..05 sind erledigt - Web 205/205 Tests gruen, API `src/tenders` 362/362 gruen, beide Typpruefungen (API + Web) fehlerfrei - Offen: die Browser-Gegenprobe aus den `human-check`-Schritten aller drei Plaene (17-01 bis 17-03) — kein Browser-Tool in den jeweiligen Sitzungen verfuegbar, festgehalten in WINDOWS.md (#7, #8, #9), vor `/gsd-ship` nachzuholen - Offen: Rollout beider Migrationen (17-01, 17-02) auf dem Testserver (192.168.13.12) bleibt bewusst Nutzeraktion (Projektregel: kein `docker compose pull/up/rebuild` durch Claude auf dem Testserver) ## Self-Check: PASSED Alle im SUMMARY genannten Dateien existieren auf der Festplatte; alle drei Task-Commit-Hashes (`4100bb5`, `150046e`, `809afbc`) sind im Git-Log auffindbar. --- *Phase: 17-eigene-ausschreibungs-quellen-je-nutzer* *Completed: 2026-08-12*