docs(11-04): complete Detailansicht plan
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
---
|
||||
phase: 11-filter-engine-results-ui-saved-searches
|
||||
plan: 04
|
||||
subsystem: ui
|
||||
tags: [nextjs-app-router, react, vitest, testing-library]
|
||||
|
||||
requires:
|
||||
- phase: 11-filter-engine-results-ui-saved-searches
|
||||
plan: 01
|
||||
provides: "Modulseite (page.tsx), ResultsList, tender-radar-api.ts Client, GET /modules/tender-radar/:id Backend-Endpunkt"
|
||||
provides:
|
||||
- "getTender(id): Promise<Tender> im tender-radar-api.ts Client"
|
||||
- "TenderDetail.tsx: selbstständig fetchende In-Component-Detailansicht"
|
||||
- "?tender=<id> URL-Verdrahtung: ResultsList-Zeile setzt Param, page.tsx liest ihn und rendert TenderDetail als Overlay"
|
||||
affects: [11-05-triage, 11-06-saved-searches]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Selbstständig fetchende Detail-Komponente (Muster SourceConfigForm): Container reicht nur die ID durch, Fetch+Ladezustand lebt in der Komponente selbst"
|
||||
- "?tender=<id> als In-Component-Overlay statt Next.js-Unterroute — Modul ist als dynamic(ssr:false)-Komponente gemountet (Anti-Pattern A5)"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.test.tsx
|
||||
modified:
|
||||
- apps/web/src/lib/tender-radar-api.ts
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/page.tsx
|
||||
- apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.tsx
|
||||
|
||||
key-decisions:
|
||||
- "TenderDetail fetcht selbstständig via getTender(tenderId) im useEffect (Muster SourceConfigForm), statt dass page.tsx den Tender lädt und als Prop durchreicht — page.tsx bleibt ein reiner ?tender-Param-Reader, die gesamte Lade-/Fehler-/NULL-Logik lebt in einer einzigen testbaren Komponente."
|
||||
- "Deviation (Rule 3, blocking): ResultsList.tsx wurde modifiziert, obwohl nicht in den files_modified des Plans gelistet. Der Plan-eigene key_link ('ResultsList-Zeile setzt ?tender=<id>') und das Done-Kriterium ('Klick auf eine Zeile öffnet die Detailansicht') sind ohne einen Klick-Handler in ResultsList technisch nicht erfüllbar — die Modifikation war notwendig, um den Plan selbst zu erfüllen."
|
||||
|
||||
requirements-completed: [UI-02]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "TenderDetail rendert alle vorhandenen Tender-Felder (Titel, Auftraggeber, Region/PLZ/Bundesland, CPV, Frist, Wert, Verfahrensart, Status, Veröffentlichungsdatum) und einen sicheren sourceUrl-Link (target=_blank, rel=noopener noreferrer)"
|
||||
requirement: "UI-02"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.test.tsx#renders a source link pointing at the tender sourceUrl with safe target/rel"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "estimatedValue=null und deadlineAt=null rendern 'keine Wertangabe' / 'keine Frist angegeben' statt Fehler/leer (D-05 gilt auch für die Detailansicht)"
|
||||
requirement: "UI-02"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.test.tsx#shows graceful placeholders when estimatedValue and deadlineAt are null"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "Klarer Hinweis, dass Vergabeunterlagen nicht lokal gespiegelt werden, ist immer sichtbar; kein Live-Dokument-Fetch/Mirroring implementiert (rawPayload 100% NULL, keine Dokument-URLs vorhanden)"
|
||||
requirement: "UI-02"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.test.tsx#always shows the no-local-mirroring notice for Vergabeunterlagen"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "ResultsList-Zeile setzt ?tender=<id> via router.replace; page.tsx liest den Param, rendert TenderDetail als Overlay, Schließen entfernt den Param"
|
||||
requirement: "UI-02"
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "tsc --noEmit clean; voller Web-Test-Suite (117 Tests) grün — keine Regression in ResultsList/page.tsx"
|
||||
status: pass
|
||||
human_judgment: true
|
||||
rationale: "Die eigentliche Klick-Interaktion (Zeile klicken -> Overlay öffnet -> Quell-Link öffnet neuen Tab -> Schließen entfernt Overlay) ist eine visuelle/UX-Interaktion im Browser gegen die echte Live-DB (~1671 Zeilen) und erfordert einen Docker-Rebuild + manuellen Klick-Test (User führt Docker-Rebuild selbst aus, projektinterne Regel) — Unit-Tests decken die Render-/Formatierungslogik ab, nicht die End-to-End-Klick-Interaktion im laufenden Container."
|
||||
|
||||
duration: 12min
|
||||
completed: 2026-07-21
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 11 Plan 04: Detailansicht (TenderDetail) Summary
|
||||
|
||||
**Selbstständig fetchende `?tender=<id>`-In-Component-Overlay-Detailansicht mit allen Tender-Feldern, sicherem sourceUrl-Link und explizitem Kein-Mirroring-Hinweis — keine lokale Spiegelung der Vergabeunterlagen, da `rawPayload` zu 100% NULL ist.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 12 min
|
||||
- **Started:** 2026-07-21T16:19:40+02:00
|
||||
- **Completed:** 2026-07-21T16:31:xx+02:00 (see final commit)
|
||||
- **Tasks:** 2 (Task 1 TDD: RED + GREEN; Task 2 bereits durch Task 1s RED-Testfile vollständig erfüllt)
|
||||
- **Files modified:** 5 (2 created, 3 modified)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- `tender-radar-api.ts`: `getTender(id): Promise<Tender>` ergänzt — `GET /modules/tender-radar/:id`, plain fetch, `credentials: 'include'` (bestehendes Backend-Endpoint aus Phase 10/11-01, keine Backend-Änderung nötig).
|
||||
- `TenderDetail.tsx`: neue, selbstständig fetchende Overlay-Komponente (Muster `SourceConfigForm`). Rendert Titel, Auftraggeber, Region/PLZ/Bundesland, CPV-Codes, Abgabefrist, geschätzten Wert, Verfahrensart, Status, Veröffentlichungsdatum. `estimatedValue=null` → „keine Wertangabe", `deadlineAt=null` → „keine Frist angegeben", `cpvCodes=[]` → „keine CPV-Codes angegeben" — nichts wird verschwiegen oder wirft einen Fehler (D-05 gilt auch hier). Deutlich sichtbarer Hinweis „Vergabeunterlagen werden nicht lokal gespiegelt" über dem `sourceUrl`-Link (`target="_blank" rel="noopener noreferrer"`, T-11-09); fehlt `sourceUrl`, wird „Kein Quell-Link verfügbar." angezeigt statt eines toten Links.
|
||||
- `page.tsx`: liest `useSearchParams().get('tender')`; ist der Param gesetzt, wird `<TenderDetail tenderId onClose>` als Overlay über der bestehenden Liste/Filter gerendert; Schließen (Button oder Backdrop-Klick) entfernt den Param via `router.replace`.
|
||||
- `ResultsList.tsx` (Deviation, siehe unten): Zeilen sind jetzt klickbar (`cursor-pointer`, `onClick`) und setzen `?tender=<id>` — ohne diese Änderung wäre der Plan-eigene key_link nicht erfüllbar gewesen.
|
||||
|
||||
## Task Commits
|
||||
|
||||
1. **Task 1a: TenderDetail RED** — `3cc7194` (test) — failing `TenderDetail.test.tsx` (3 Tests: sourceUrl-Link + safe target/rel, NULL-graceful Wert/Frist, Kein-Mirroring-Hinweis)
|
||||
2. **Task 1b: TenderDetail GREEN** — `653c63b` (feat) — `getTender()`, `TenderDetail.tsx`, `page.tsx`-Verdrahtung, `ResultsList.tsx`-Klick-Handler implementiert, 3/3 Tests grün
|
||||
|
||||
_Task 1 ist `tdd="true"` — RED-Commit dann GREEN-Commit._
|
||||
|
||||
**Task 2 (Komponententest TenderDetail):** Die drei geforderten Testfälle (a: sourceUrl-Link, b: NULL-Graceful, c: Kein-Mirroring-Hinweis) waren bereits vollständig Teil des RED-Testfiles aus Task 1 (identische Datei `TenderDetail.test.tsx`, identische Anforderungen). Es gab keine eigenständige, von Task 1 verschiedene Aktion mehr auszuführen — daher kein separater Commit für Task 2. Verifiziert durch erneutes Ausführen der Testsuite (weiterhin 3/3 grün, siehe Self-Check).
|
||||
|
||||
## Files Created/Modified
|
||||
- `apps/web/src/lib/tender-radar-api.ts` - `getTender(id)` ergänzt
|
||||
- `apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.tsx` - Detail-Overlay-Komponente (neu)
|
||||
- `apps/web/src/app/(portal)/modules/tender-radar/components/TenderDetail.test.tsx` - 3 Component-Tests (neu)
|
||||
- `apps/web/src/app/(portal)/modules/tender-radar/page.tsx` - `?tender`-Param-Verdrahtung + Overlay-Rendering
|
||||
- `apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.tsx` - Zeilen-Klick setzt `?tender=<id>` (Deviation)
|
||||
|
||||
## Decisions Made
|
||||
|
||||
- `TenderDetail` fetcht selbstständig via `getTender(tenderId)` im `useEffect` (Muster `SourceConfigForm.tsx`) statt dass `page.tsx` den Tender lädt und als Prop durchreicht. `page.tsx` bleibt dadurch ein reiner `?tender`-Param-Reader; Lade-/Fehler-/NULL-Zustandslogik lebt vollständig in einer einzigen, unabhängig testbaren Komponente — konsistent mit dem einzigen existierenden Precedent für eine "auf ID fetchende" Detail-Komponente im Modul.
|
||||
- Backdrop-Klick schließt das Overlay zusätzlich zum expliziten „Schließen"-Button — nicht explizit im Plan gefordert, aber Standard-Overlay-UX-Erwartung (Rule 1/2-Grenzfall, unter Correctness/UX eingeordnet, kein separater Deviation-Fall da rein additiv und ohne Verhaltensänderung der geforderten Felder).
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] `ResultsList.tsx` wurde modifiziert, obwohl nicht in `files_modified` des Plans gelistet**
|
||||
- **Found during:** Task 1 (Verdrahtung)
|
||||
- **Issue:** Der Plan fordert explizit im `key_links`-Feld: „ResultsList-Zeile setzt ?tender=<id> → page.tsx liest useSearchParams → getTender(id) → ... → TenderDetail" und im Done-Kriterium: „Klick auf eine Zeile öffnet die Detailansicht via ?tender=<id>". Ohne einen Klick-Handler auf den Zeilen in `ResultsList.tsx` ist dieser Fluss technisch nicht herstellbar — `page.tsx` allein kann den Param nicht "von einer Zeile aus" setzen, da die Zeilen in `ResultsList` gerendert werden.
|
||||
- **Fix:** `openDetail(id)`-Funktion in `ResultsList.tsx` ergänzt (identisches Muster zu vorhandenem `goToPage`/`handleSort`), Zeilen erhielten `onClick={() => openDetail(tender.id)}` + `cursor-pointer`.
|
||||
- **Files modified:** `apps/web/src/app/(portal)/modules/tender-radar/components/ResultsList.tsx`
|
||||
- **Commit:** `653c63b`
|
||||
|
||||
## Auth Gates
|
||||
|
||||
None.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None — keine externen Service-Konfigurationen erforderlich. Keine neuen npm-Pakete (RESEARCH Package Legitimacy Audit: keine Neuinstallation).
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- `?tender=<id>`-URL-Vertrag ist etabliert und stabil für Plan 11-05 (Triage: gelesen/Favorit-Buttons könnten künftig direkt in `TenderDetail` ergänzt werden) und 11-06 (Saved Searches: der `tender`-Param wird bei Saved-Search-Serialisierung nicht mitgespeichert, da er kein Filter-Kriterium ist — kein Konflikt).
|
||||
- **Manuelle UAT ausstehend:** Der Docker-Stack läuft mit alten Images — für eine Live-Verifikation im Browser (Zeile klicken, Overlay-Layout prüfen, sourceUrl-Link in neuem Tab öffnen, Schließen-Verhalten) ist `docker compose build api web && docker compose up -d` erforderlich (User führt Docker-Rebuild selbst aus, keine Docker-Deploys durch Claude auf dem Testserver — projektinterne Regel). Alle automatisierten Tests (117 Web-Tests inkl. 3 neue TenderDetail-Tests) sind grün; `tsc --noEmit` fehlerfrei.
|
||||
|
||||
---
|
||||
*Phase: 11-filter-engine-results-ui-saved-searches*
|
||||
*Completed: 2026-07-21*
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
All 5 created/modified files verified present on disk; both task commits (3cc7194, 653c63b) verified in git log.
|
||||
Reference in New Issue
Block a user