364 lines
22 KiB
Markdown
364 lines
22 KiB
Markdown
---
|
|
phase: 17-eigene-ausschreibungs-quellen-je-nutzer
|
|
plan: 03
|
|
type: execute
|
|
wave: 3
|
|
depends_on: ["17-01", "17-02"]
|
|
files_modified:
|
|
- apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx
|
|
- apps/web/src/app/(portal)/modules/tender-radar/my-sources/my-sources.test.tsx
|
|
- apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx
|
|
- apps/web/src/app/(portal)/modules/tender-radar/settings/settings-roles.test.tsx
|
|
- 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/settings/components/DigestIntervalForm.tsx
|
|
- apps/web/src/app/(portal)/modules/tender-radar/page.tsx
|
|
- apps/web/src/lib/tender-radar-api.ts
|
|
- apps/web/src/messages/de.json
|
|
- apps/web/src/messages/en.json
|
|
- .planning/todos/pending/2026-08-11-tender-radar-einstellungen-mischen-rollen.md
|
|
autonomous: true
|
|
requirements: [SRC-04, SRC-05]
|
|
user_setup: []
|
|
|
|
estimate:
|
|
tokens: 55000
|
|
raw_tokens: 55000
|
|
tasks: 3
|
|
confidence: low
|
|
|
|
must_haves:
|
|
truths:
|
|
- "Auf `/modules/tender-radar/my-sources` findet ein normaler Nutzer alles, was ihm gehoert: sein Postfach, seine Feeds, sein Benachrichtigungsintervall (D-01, D-02, D-04)."
|
|
- "Auf `/modules/tender-radar/settings` bleibt nur Administrationskram: Abrufintervall der oeffentlichen Quelle und die plattformweiten Feeds (D-03)."
|
|
- "Ein normaler Nutzer sieht auf der Administrationsseite keine Bedienelemente mehr, die beim Speichern in eine Fehlermeldung laufen (offener Punkt 5)."
|
|
- "Das Zahnrad auf der Modulseite fuehrt jeden Nutzer dorthin, wo er etwas einstellen darf."
|
|
- "Alle neuen Beschriftungen liegen in beiden Sprachdateien vor."
|
|
artifacts:
|
|
- apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx
|
|
- 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
|
|
key_links:
|
|
- "RssFeedListForm(scope) <-> POST /rss-feeds mit `scope` — dieselbe Komponente bedient beide Seiten, die Berechtigung entscheidet der Server."
|
|
- "useAuthStore().user.role <-> Sichtbarkeit der Administrationsabschnitte — reine Anzeigefrage, die verbindliche Pruefung bleibt im Server."
|
|
- "Zahnrad in tender-radar/page.tsx <-> /modules/tender-radar/my-sources — der Weg, den jeder Nutzer gehen darf."
|
|
---
|
|
|
|
<objective>
|
|
Die Oberflaeche wird nach Zustaendigkeit getrennt. Bisher stehen vier Abschnitte
|
|
untereinander auf einer Seite, die drei verschiedenen Zustaendigkeiten gehoeren —
|
|
wer nur seine Benachrichtigungen auf woechentlich stellen will, scrollt an
|
|
Abrufintervallen, Feed-Adressen und Postfach-Zugangsdaten vorbei und sieht
|
|
Bedienelemente, die er gar nicht bedienen darf.
|
|
|
|
Nach diesem Plan gibt es zwei Seiten:
|
|
|
|
- **Meine Quellen** (`/modules/tender-radar/my-sources`, fuer jeden mit
|
|
Modulzugang): mein Postfach, meine Feeds, mein Benachrichtigungsintervall.
|
|
- **Einstellungen** (`/modules/tender-radar/settings`, nur Administration):
|
|
Abrufintervall der oeffentlichen Quelle und die plattformweiten Feeds.
|
|
|
|
Purpose: D-03, D-04 und den urspruenglichen Anlass des Backlog-Punkts abschliessen.
|
|
Output: Vollstaendige Nutzerseite, reduzierte und rollengepruefte
|
|
Administrationsseite, angepasstes Zahnrad, Beschriftungen in beiden Sprachen.
|
|
|
|
**Entscheidung zum offenen Punkt 5 (Rollenpruefung).** Die Administrationsseite
|
|
prueft die Rolle des angemeldeten Kontos aus dem bereits vorhandenen
|
|
Anmelde-Speicher im Browser und zeigt einem normalen Nutzer statt der
|
|
Bedienelemente einen kurzen Satz mit einem Verweis auf "Meine Quellen".
|
|
Begruendung: Die verbindliche Absicherung liegt und bleibt im Server — die
|
|
Endpunkte fuer das Abrufintervall und fuer plattformweite Feeds sind dort
|
|
rollengeprueft. Die Pruefung im Browser ist ausschliesslich eine Anzeigefrage:
|
|
sie verhindert, dass jemand Knoepfe sieht, die beim Speichern in eine
|
|
Fehlermeldung laufen. Genau das war der Anlass des Backlog-Punkts.
|
|
</objective>
|
|
|
|
<execution_context>
|
|
@$HOME/.claude/gsd-core/workflows/execute-plan.md
|
|
@$HOME/.claude/gsd-core/templates/summary.md
|
|
</execution_context>
|
|
|
|
<context>
|
|
@.planning/PROJECT.md
|
|
@.planning/ROADMAP.md
|
|
@.planning/STATE.md
|
|
@.planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-CONTEXT.md
|
|
@.planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-01-SUMMARY.md
|
|
@.planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-02-SUMMARY.md
|
|
@.planning/todos/pending/2026-08-11-tender-radar-einstellungen-mischen-rollen.md
|
|
|
|
@apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx
|
|
@apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx
|
|
@apps/web/src/lib/tender-radar-api.ts
|
|
@apps/web/src/lib/stores/auth-store.ts
|
|
</context>
|
|
|
|
<tasks>
|
|
|
|
<task type="tracer" tdd="true">
|
|
<name>Task 1: "Meine Quellen" wird vollstaendig — Postfach, eigene Feeds, Benachrichtigung</name>
|
|
<files>
|
|
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/DigestIntervalForm.tsx,
|
|
apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx,
|
|
apps/web/src/messages/de.json,
|
|
apps/web/src/messages/en.json
|
|
</files>
|
|
<read_first>
|
|
apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx,
|
|
apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.tsx,
|
|
apps/web/src/lib/tender-radar-api.ts (Zeilen 370-560)
|
|
</read_first>
|
|
<behavior>
|
|
- Auf "Meine Quellen" stehen drei Abschnitte untereinander: Mein Postfach, Meine Feeds, Benachrichtigung.
|
|
- Die Feed-Liste dort zeigt ausschliesslich die eigenen Feeds; plattformweite Feeds erscheinen darunter als kurze, nicht bedienbare Aufzaehlung mit dem Hinweis, dass die Administration sie pflegt.
|
|
- Ein neu angelegter Feed erscheint sofort in der eigenen Liste.
|
|
- Zu einem plattformweiten Feed gibt es auf dieser Seite keinen Entfernen-Knopf.
|
|
- Die Auswahl des Benachrichtigungsintervalls speichert und quittiert wie bisher.
|
|
</behavior>
|
|
<action>
|
|
**Zugriffsschicht (`lib/tender-radar-api.ts`).** Der Typ `RssFeedSource` bekommt
|
|
das vom Server gelieferte Kennzeichen, ob der Eintrag plattformweit ist.
|
|
`createRssFeed` bekommt einen zweiten Parameter fuer den Wirkungsbereich
|
|
(`personal` oder `platform`, Vorgabe `personal`), der in den Rumpf der Anfrage
|
|
wandert. `listRssFeeds` und `deleteRssFeed` bleiben in ihrer Signatur, ihr
|
|
Verhalten aendert sich serverseitig.
|
|
|
|
**Feed-Liste (`RssFeedListForm.tsx`).** Die Komponente bekommt eine
|
|
Eigenschaft `scope` mit den Werten `personal` und `platform`. Sie laedt
|
|
weiterhin die eine Liste vom Server und teilt sie selbst auf:
|
|
- `scope="personal"`: bearbeitbar sind die eigenen Eintraege (anlegen,
|
|
entfernen). Die plattformweiten Eintraege erscheinen darunter als schlichte
|
|
Aufzaehlung ohne Knoepfe, mit einem Satz, dass die Administration sie pflegt —
|
|
so ist auf einen Blick klar, dass niemand bei null anfaengt.
|
|
- `scope="platform"`: bearbeitbar sind die plattformweiten Eintraege; angelegt
|
|
wird mit dem entsprechenden Wirkungsbereich. Eigene Eintraege anderer Nutzer
|
|
tauchen hier gar nicht auf, weil der Server sie nicht liefert.
|
|
Die Fehleranzeige bleibt wie sie ist: eine abgelehnte Adresse zeigt weiterhin
|
|
die ausfuehrliche Meldung des Servers, nicht einen selbstgebauten Ersatztext.
|
|
Die Ablehnung wegen erreichter Obergrenze (aus Plan 17-02) laeuft ueber denselben
|
|
Weg und braucht keine Sonderbehandlung.
|
|
|
|
**Benachrichtigungsintervall (`DigestIntervalForm.tsx`).** Den heute in der
|
|
Einstellungsseite eingebetteten Block (Laden beim Aufbau, Auswahlfeld
|
|
Taeglich/Woechentlich/Aus, Speichern bei Aenderung, Erfolgs- und Fehlermeldung)
|
|
unveraendert in eine eigene Komponente herausloesen. Kein Verhalten aendern,
|
|
keine neuen Beschriftungsschluessel — nur verschieben, damit beide Seiten
|
|
dieselbe Umsetzung benutzen koennen und die Einstellungsseite in Task 2 sauber
|
|
kleiner wird.
|
|
|
|
**Seite (`my-sources/page.tsx`).** Die in Plan 17-01 angelegte Seite um zwei
|
|
Abschnitte erweitern: unter "Mein Postfach" folgt "Meine Feeds"
|
|
(`RssFeedListForm` mit `scope="personal"`), darunter "Benachrichtigung"
|
|
(`DigestIntervalForm`). Aufbau, Abstaende und Trennlinien wie auf der heutigen
|
|
Einstellungsseite uebernehmen, damit nichts fremd wirkt. Fuer Konten mit
|
|
Administratorrolle unten ein Verweis auf die Administrationsseite; fuer alle
|
|
anderen erscheint dieser Verweis nicht. Die Rolle kommt aus dem vorhandenen
|
|
Anmelde-Speicher; solange dieser noch nicht befuellt ist, den Verweis
|
|
weglassen statt kurz aufblitzen zu lassen.
|
|
|
|
**Beschriftungen (`messages/de.json`, `messages/en.json`).** Alle neuen
|
|
Schluessel im Namensraum `tenderRadar` unter `mySources` anlegen und in beiden
|
|
Dateien pflegen: Seitentitel, die drei Abschnittsueberschriften, der ehrliche
|
|
Hinweis zu D-05 (Ausschreibungen aus den eigenen Quellen erscheinen auch bei
|
|
Kollegen desselben Mandanten), der Satz zu den von der Administration
|
|
gepflegten Feeds, der Verweis auf die Administrationsseite. Keine Beschriftung
|
|
nur auf Deutsch anlegen.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/web type-check</automated>
|
|
<automated>cd /home/vicolab/projects/tessera-ctl && node -e "const de=require('./apps/web/src/messages/de.json'),en=require('./apps/web/src/messages/en.json');const walk=(o,p='')=>Object.entries(o).flatMap(([k,v])=>typeof v==='object'&&v?walk(v,p+k+'.'):[p+k]);const d=walk(de.tenderRadar),e=walk(en.tenderRadar);const miss=d.filter(k=>!e.includes(k)).concat(e.filter(k=>!d.includes(k)));if(miss.length){console.error('Fehlende Uebersetzungen:',miss);process.exit(1)}console.log('i18n vollstaendig:',d.length,'Schluessel')"</automated>
|
|
<human-check>Als normaler Nutzer `/modules/tender-radar/my-sources` oeffnen: drei Abschnitte sind sichtbar. Einen eigenen RSS-Feed anlegen — er erscheint in der eigenen Liste, der plattformweite service.bund.de-Feed steht darunter ohne Entfernen-Knopf. Das Benachrichtigungsintervall auf "Woechentlich" stellen, Seite neu laden, Wert steht noch. Kein Verweis auf die Administrationsseite sichtbar.</human-check>
|
|
</verify>
|
|
<done>
|
|
Die Typpruefung laeuft fehlerfrei, beide Sprachdateien decken denselben
|
|
Schluesselsatz ab. Ein normaler Nutzer kann auf einer Seite Postfach, eigene
|
|
Feeds und Benachrichtigungsintervall pflegen und sieht keinen Knopf, der ihm
|
|
verwehrt ist.
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 2: Die Administrationsseite schrumpft auf das, was Administration ist</name>
|
|
<files>
|
|
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
|
|
</files>
|
|
<read_first>
|
|
apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx,
|
|
apps/web/src/app/(portal)/modules/tender-radar/page.tsx (Zeilen 60-95),
|
|
apps/web/src/lib/stores/auth-store.ts
|
|
</read_first>
|
|
<behavior>
|
|
- Ein Konto mit Rolle ADMIN oder SUPER_ADMIN sieht auf der Einstellungsseite genau zwei Abschnitte: Abrufintervall und plattformweite Feeds.
|
|
- Ein Konto mit Rolle USER sieht dort keinen dieser Abschnitte, sondern einen kurzen Satz und einen Verweis auf "Meine Quellen".
|
|
- Solange die Kontodaten im Browser noch nicht geladen sind, erscheint weder das eine noch das andere, sondern ein Platzhalter — kein kurzes Aufblitzen der Administrationsabschnitte.
|
|
- Postfach und Benachrichtigungsintervall kommen auf dieser Seite nicht mehr vor.
|
|
- Das Zahnrad auf der Modulseite fuehrt fuer jeden Nutzer nach "Meine Quellen".
|
|
</behavior>
|
|
<action>
|
|
**Einstellungsseite (`settings/page.tsx`).** Die Abschnitte "E-Mail-Alerts" und
|
|
"Benachrichtigungen" ersatzlos herausnehmen — sie leben ab jetzt auf "Meine
|
|
Quellen" (D-01, D-04). Uebrig bleiben das Abrufintervall (`SourceConfigForm`,
|
|
D-03) und die plattformweiten Feeds (`RssFeedListForm` mit `scope="platform"`).
|
|
Der ganze Inhalt wird von einer Rollenpruefung umschlossen: Rolle aus dem
|
|
vorhandenen Anmelde-Speicher lesen; ist sie noch nicht bekannt, einen
|
|
Ladeplatzhalter zeigen; ist sie ADMIN oder SUPER_ADMIN, den Inhalt zeigen; sonst
|
|
einen kurzen Satz ("Diese Seite verwaltet plattformweite Einstellungen und ist
|
|
Administratoren vorbehalten.") mit einem Verweis auf `/modules/tender-radar/my-sources`.
|
|
Der Kopfkommentar der Datei wird auf den neuen Zuschnitt umgeschrieben; die
|
|
Absaetze zu den entfernten Abschnitten entfallen, ein Absatz zu Phase 17 kommt
|
|
hinzu. Beim Abschnitt der plattformweiten Feeds einen Satz ergaenzen, dass diese
|
|
fuer alle Nutzer gelten und persoenliche Feeds woanders liegen.
|
|
|
|
**Modulseite (`page.tsx`).** Das Zahnrad zeigt heute auf die
|
|
Administrationsseite. Es zeigt kuenftig auf `/modules/tender-radar/my-sources` —
|
|
das ist die Seite, die jeder Nutzer aufrufen darf, und Administratoren gehen von
|
|
dort mit einem Klick weiter. Beschriftung und Vorlesetext des Knopfes
|
|
entsprechend anpassen (neuer Schluessel, alter Schluessel bleibt fuer den Verweis
|
|
auf der Nutzerseite in Gebrauch, falls passend — sonst entfernen). Den
|
|
vorhandenen Kommentar zum absoluten Verweis (die dynamische Einstellungsroute
|
|
greift fuer dieses Modul nicht) beibehalten und auf den neuen Zielpfad
|
|
anpassen.
|
|
|
|
**Beschriftungen.** Neue Schluessel in beiden Sprachdateien anlegen: der
|
|
Hinweistext fuer Nutzer ohne Administratorrolle, der Verweistext auf "Meine
|
|
Quellen", der Zusatzsatz beim Abschnitt der plattformweiten Feeds, die neue
|
|
Zahnrad-Beschriftung. Nicht mehr benutzte Schluessel (`settings.emailSectionTitle`,
|
|
`settings.emailSectionBody`, `settings.notificationsSectionTitle` und die
|
|
Digest-Schluessel, sofern sie auf die Nutzerseite umziehen) erst entfernen,
|
|
nachdem im gesamten `apps/web/src` nachgesehen wurde, dass sie nirgends mehr
|
|
verwendet werden.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/web type-check</automated>
|
|
<automated>cd /home/vicolab/projects/tessera-ctl && node -e "const de=require('./apps/web/src/messages/de.json'),en=require('./apps/web/src/messages/en.json');const walk=(o,p='')=>Object.entries(o).flatMap(([k,v])=>typeof v==='object'&&v?walk(v,p+k+'.'):[p+k]);const d=walk(de.tenderRadar),e=walk(en.tenderRadar);const miss=d.filter(k=>!e.includes(k)).concat(e.filter(k=>!d.includes(k)));if(miss.length){console.error('Fehlende Uebersetzungen:',miss);process.exit(1)}console.log('i18n vollstaendig:',d.length,'Schluessel')"</automated>
|
|
<human-check>Als Konto mit Rolle USER `/modules/tender-radar/settings` direkt in der Adresszeile aufrufen: es erscheinen keine Bedienelemente, sondern der Hinweis samt Verweis. Danach als Administrator dieselbe Adresse: Abrufintervall und plattformweite Feeds sind da, Postfach und Benachrichtigungsintervall nicht mehr. Auf der Modulseite fuehrt das Zahnrad in beiden Faellen nach "Meine Quellen".</human-check>
|
|
</verify>
|
|
<done>
|
|
Die Einstellungsseite traegt nur noch Abrufintervall und plattformweite Feeds
|
|
und ist fuer Konten ohne Administratorrolle leer bis auf Hinweis und Verweis.
|
|
Das Zahnrad fuehrt jeden Nutzer auf eine Seite, auf der er etwas einstellen darf.
|
|
Beide Sprachdateien decken denselben Schluesselsatz ab.
|
|
</done>
|
|
</task>
|
|
|
|
<task type="auto" tdd="true">
|
|
<name>Task 3: Tests fuer beide Seiten und der Backlog-Punkt wird zugemacht</name>
|
|
<files>
|
|
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,
|
|
apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx,
|
|
.planning/todos/pending/2026-08-11-tender-radar-einstellungen-mischen-rollen.md
|
|
</files>
|
|
<read_first>
|
|
apps/web/src/app/(portal)/modules/tender-radar/settings/components/RssFeedListForm.test.tsx,
|
|
apps/web/src/app/(portal)/modules/tender-radar/settings/components/SourceConfigForm.test.tsx
|
|
</read_first>
|
|
<behavior>
|
|
- `RssFeedListForm` mit `scope="personal"`: die gelieferte Liste enthaelt einen plattformweiten und einen eigenen Eintrag; nur der eigene traegt einen Entfernen-Knopf, der plattformweite erscheint ohne.
|
|
- `RssFeedListForm` mit `scope="platform"`: umgekehrt.
|
|
- `RssFeedListForm` mit `scope="personal"` schickt beim Anlegen den Wirkungsbereich `personal` mit, mit `scope="platform"` entsprechend `platform`.
|
|
- Einstellungsseite mit Rolle USER: die Ueberschriften der Administrationsabschnitte kommen nicht vor, der Verweis auf "Meine Quellen" schon.
|
|
- Einstellungsseite mit Rolle ADMIN: die Administrationsabschnitte kommen vor.
|
|
- Einstellungsseite ohne geladene Kontodaten: weder das eine noch das andere.
|
|
- "Meine Quellen": alle drei Abschnittsueberschriften kommen vor; der Verweis auf die Administrationsseite nur bei Rolle ADMIN.
|
|
</behavior>
|
|
<action>
|
|
**Testaufbau.** Vorbild sind die vorhandenen Tests im selben Verzeichnis: die
|
|
Zugriffsschicht `@/lib/tender-radar-api` wird ersetzt, `next-intl` wird durch
|
|
eine Zuordnung von Schluessel auf Text ersetzt. Neu hinzu kommt das Ersetzen des
|
|
Anmelde-Speichers, damit die Rolle im Test gesetzt werden kann — dieselbe
|
|
Ersetzungsform wie bei den uebrigen Ersetzungen, ueber den Modulpfad des
|
|
Speichers.
|
|
|
|
**Wichtig zur Aussagekraft.** Die Pruefungen muessen an dem haengen, was der
|
|
Nutzer sieht: vorhandene beziehungsweise fehlende Beschriftungen und Knoepfe im
|
|
gerenderten Ergebnis. Nicht pruefen, ob eine Komponente "aufgerufen wurde" —
|
|
und die erwarteten Texte im Test woertlich hinschreiben, nicht aus derselben
|
|
Zuordnung ableiten, die auch die Komponente benutzt. Ein Test, der seine
|
|
Erwartung mit dem Pruefling selbst baut, beweist nichts.
|
|
|
|
**`RssFeedListForm.test.tsx`.** Bestehende Faelle auf die neue Eigenschaft
|
|
`scope` nachziehen (Vorgabe im Test ausdruecklich setzen, nicht raten) und die
|
|
drei Faelle aus dem Abschnitt "behavior" ergaenzen. Der bestehende Fall, dass die
|
|
ausfuehrliche Servermeldung bei einer gesperrten Adresse angezeigt wird, muss
|
|
unveraendert weiterlaufen.
|
|
|
|
**`settings-roles.test.tsx` (neu).** Die drei Rollenfaelle der
|
|
Einstellungsseite. Zusaetzlich ein Fall, der belegt, dass die Ueberschriften der
|
|
entfernten Abschnitte (Postfach, Benachrichtigung) auf dieser Seite nicht mehr
|
|
vorkommen.
|
|
|
|
**`my-sources.test.tsx` (neu).** Die drei Abschnitte sind da; der Verweis auf
|
|
die Administrationsseite erscheint nur bei Administratorrolle.
|
|
|
|
**Backlog-Punkt.** `2026-08-11-tender-radar-einstellungen-mischen-rollen.md`
|
|
nach `.planning/todos/completed/` verschieben und im Kopf einen kurzen Abschnitt
|
|
ergaenzen, wie er geloest wurde: nicht nur die Rollenpruefung aus der
|
|
urspruenglichen Skizze, sondern der Umbau auf nutzereigene Quellen, und die
|
|
Grundsatzfrage aus Punkt 2 ist mit "eigene nutzerseitige Modulseite" beantwortet
|
|
— das ist ab jetzt die Bauform, an der sich DKV-Fleet und kuenftige Module
|
|
orientieren.
|
|
</action>
|
|
<verify>
|
|
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/web exec vitest run src/app/\(portal\)/modules/tender-radar</automated>
|
|
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/web exec vitest run</automated>
|
|
<automated>cd /home/vicolab/projects/tessera-ctl && pnpm --filter @tessera/api exec vitest run src/tenders && pnpm --filter @tessera/api type-check && pnpm --filter @tessera/web type-check</automated>
|
|
<automated>test ! -f .planning/todos/pending/2026-08-11-tender-radar-einstellungen-mischen-rollen.md && test -f .planning/todos/completed/2026-08-11-tender-radar-einstellungen-mischen-rollen.md</automated>
|
|
</verify>
|
|
<done>
|
|
Alle Tests in Web und API laufen gruen, beide Typpruefungen sind sauber, der
|
|
Backlog-Punkt liegt im Ordner der erledigten Punkte.
|
|
</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<threat_model>
|
|
## Trust Boundaries
|
|
|
|
| Boundary | Description |
|
|
|----------|-------------|
|
|
| Browser-Anzeige -> Serverpruefung | Die Rollenpruefung im Browser ist eine Anzeigefrage; die verbindliche Pruefung liegt im Server. |
|
|
| Nutzerseite -> Endpunkte fuer plattformweite Einstellungen | Die Nutzerseite darf keinen Weg zu plattformweiten Schreibvorgaengen oeffnen. |
|
|
|
|
## STRIDE Threat Register
|
|
|
|
| Threat ID | Category | Component | Severity | Disposition | Mitigation Plan |
|
|
|-----------|----------|-----------|----------|-------------|-----------------|
|
|
| T-17-14 | Elevation of Privilege | Rollenpruefung im Browser | medium | mitigate | Die Pruefung blendet nur Bedienelemente aus. Die Endpunkte fuer Abrufintervall und plattformweite Feeds bleiben serverseitig rollengeprueft (Plan 17-02, T-17-08). Ein Test belegt, dass die Rolle USER die Abschnitte nicht zu sehen bekommt. |
|
|
| T-17-15 | Elevation of Privilege | `RssFeedListForm` mit `scope="personal"` | high | mitigate | Der Wirkungsbereich wandert als Wunsch in die Anfrage; ob er gewaehrt wird, entscheidet ausschliesslich der Server. Ein manipuliertes Frontend erreicht nichts. |
|
|
| T-17-16 | Information Disclosure | Kurzes Aufblitzen der Administrationsabschnitte vor dem Laden der Kontodaten | low | mitigate | Solange die Rolle unbekannt ist, wird ein Platzhalter gezeigt; ein Test deckt genau diesen Zustand ab. |
|
|
| T-17-17 | Repudiation | Unvollstaendige Uebersetzung | low | mitigate | Ein Pruefschritt vergleicht die Schluesselsaetze beider Sprachdateien und bricht bei Abweichung ab. |
|
|
| T-17-SC | Tampering | Paketinstallationen | low | accept | Diese Phase installiert kein neues Paket. |
|
|
</threat_model>
|
|
|
|
<verification>
|
|
1. `pnpm --filter @tessera/web exec vitest run` — vollstaendig gruen.
|
|
2. `pnpm --filter @tessera/api exec vitest run src/tenders` — vollstaendig gruen.
|
|
3. Beide Typpruefungen fehlerfrei.
|
|
4. Schluesselvergleich der beiden Sprachdateien ohne Abweichung.
|
|
5. Browser-Gegenprobe mit zwei Konten (Rolle USER und Rolle ADMIN) wie in den
|
|
`human-check`-Schritten der Tasks 1 und 2. Eine erfolgreiche Antwort des
|
|
Servers allein reicht als Nachweis nicht — es zaehlt, was auf der Seite steht.
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- "Meine Quellen" traegt Postfach, eigene Feeds und Benachrichtigungsintervall.
|
|
- Die Einstellungsseite traegt nur Abrufintervall und plattformweite Feeds.
|
|
- Ein normaler Nutzer sieht auf der Einstellungsseite keine Bedienelemente mehr,
|
|
die beim Speichern in eine Fehlermeldung laufen.
|
|
- Das Zahnrad fuehrt jeden Nutzer auf eine fuer ihn bedienbare Seite.
|
|
- Alle Beschriftungen liegen in Deutsch und Englisch vor.
|
|
- Der Backlog-Punkt ist abgeschlossen und die Grundsatzfrage darin beantwortet.
|
|
</success_criteria>
|
|
|
|
<output>
|
|
Create `.planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-03-SUMMARY.md` when done.
|
|
</output>
|