Files
tessera-ctl/.planning/phases/07-dkv-fleet-module/07-05-SUMMARY.md
T
schalli 5c191c8d10
Tessera CI/CD / Lint & Type Check (push) Failing after 41s
Tessera CI/CD / Tests (push) Has been skipped
Tessera CI/CD / Build & Deploy (push) Has been skipped
docs(07-05): complete DKV fleet module frontend plan
2026-06-27 00:43:55 +02:00

8.4 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
07-dkv-fleet-module 05 dkv-frontend
dkv
nextjs
react
frontend
crud
tdd
api-client
i18n
requires provides affects
DkvController REST surface (07-04)
dkvFleet i18n namespace (07-04)
CalendarSourceForm pattern (05-02)
dkv-api.ts typed client
DkvFleetPage (Surface A)
InboxConfigForm (Surface B Tab 1)
VehicleTable + CsvImportButton (Surface B Tab 2)
apps/web/src/lib/dkv-api.ts
apps/web/src/app/(portal)/modules/dkv-fleet/page.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/components/StatusBadge.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/components/InvoiceHistoryTable.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/components/ExportFileList.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/page.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/CsvImportButton.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.test.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/vehicles/page.tsx
added patterns
refreshKey lift: page.tsx increments refreshKey on checkNow success; InvoiceHistoryTable reruns useEffect
onItemsLoaded callback: InvoiceHistoryTable notifies parent of loaded items; parent passes to ExportFileList
T-07-12 password-blank-on-load: fetchConfig returns hasPassword boolean only; password field always blank; sent only when user types
T-07-13 exportFileUrl: pure helper using encodeURIComponent; no client-built raw paths
T-07-14 XSS: all backend strings via React text nodes; title attribute for badge tooltips (no dangerouslySetInnerHTML)
T-07-15 CsvImportButton: accept=.csv, replace mode requires explicit destructive confirm before importVehiclesCsv
TDD RED/GREEN: VehicleTable.test.tsx committed before VehicleTable.tsx; bug fixed (mockResolvedValueOnce vs mockResolvedValue)
created modified
apps/web/src/lib/dkv-api.ts
apps/web/src/app/(portal)/modules/dkv-fleet/page.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/components/StatusBadge.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/components/InvoiceHistoryTable.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/components/ExportFileList.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/page.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/CsvImportButton.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.test.tsx
apps/web/src/app/(portal)/modules/dkv-fleet/vehicles/page.tsx
InvoiceHistoryTable refetch: refreshKey prop (number) incremented by parent on checkNow success — triggers useEffect re-run; simpler than ref/callback
ExportFileList data: derives filenames from history items via onItemsLoaded callback from InvoiceHistoryTable; avoids second fetch
Password blank on load: configToForm() always sets password=''; placeholder shows bullet dots as UX hint; T-07-12 compliant
VehicleTable test mock fix: mockResolvedValueOnce for initial load (Hans Müller), mockResolvedValue for refetch (Max Mustermann)
CsvImportButton replace confirm: inline two-step (click Importieren → show confirm block → click Ersetzen) not a full modal; per UI-SPEC
Vehicles route split: settings/page.tsx has tab bar; Fahrzeuge tab is a <Link> to /modules/dkv-fleet/vehicles (own route); VehicleTable imported from settings/components/
duration completed tasks files_created files_modified
8min 2026-06-26T22:41:27Z 3 11 0

Phase 07 Plan 05: DKV Fleet Module Frontend — Summary

Typed fetch client (dkv-api.ts) and all three UI surfaces delivered: Surface A (module main page with history table + export list + "Jetzt prüfen" trigger), Surface B Tab 1 (InboxConfigForm with show/hide password and Active toggle), and Surface B Tab 2 (VehicleTable CRUD with CsvImportButton merge/replace). All STRIDE threat mitigations from the plan are implemented. VehicleTable covered by 5 passing Vitest tests (TDD RED→GREEN).

Tasks Completed

Task Name Commit Key Files
1 dkv-api client + StatusBadge + Surface A main page 2fe7bca dkv-api.ts, page.tsx, StatusBadge, InvoiceHistoryTable, ExportFileList
2 Settings page + InboxConfigForm (Surface B Tab 1) 45e9a35 settings/page.tsx, InboxConfigForm.tsx
3 (RED) VehicleTable failing tests c739d27 VehicleTable.test.tsx
3 (GREEN) VehicleTable + CsvImportButton + vehicles/page 4d81b8b VehicleTable.tsx, CsvImportButton.tsx, vehicles/page.tsx

Architecture Notes

InvoiceHistoryTable Refetch Mechanism

The page holds a refreshKey state (number). After a successful checkNow() call, the page increments refreshKey by 1. InvoiceHistoryTable receives refreshKey as a prop and includes it in its useEffect dependency array — this triggers a fresh fetchHistory() call.

Additionally, InvoiceHistoryTable accepts an onItemsLoaded(items) callback. After each fetch, it calls this with the loaded items. The page stores these items in historyItems state and passes them to ExportFileList, which derives the 10 most recent unique export filenames from them. This avoids a second network request for export files.

Password Blank on Load (T-07-12)

configToForm() always maps the server response to password: '' — the password field is never pre-filled. The server only returns hasPassword: boolean. The password field shows placeholder="••••••••" as a UX hint when hasPassword is true (inferred from the config). The password is only included in the saveConfig payload when form.password !== ''.

TDD Gate Compliance

  • RED commit: c739d27 (VehicleTable.test.tsx only, fails with import error)
  • GREEN commit: 4d81b8b (VehicleTable.tsx + CsvImportButton.tsx + fix)
  • Tests: 5 passed ✓

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] VehicleTable test: mockResolvedValue sequencing bug

  • Found during: Task 3 GREEN phase (first test run)
  • Issue: The edit-row test called mockFetchVehicles.mockResolvedValue([Hans]) then immediately mockFetchVehicles.mockResolvedValue([Max]). The second call overwrote the first, making the initial component mount return Max Mustermann instead of Hans Müller. waitFor(() => getByText('Hans Müller')) timed out.
  • Fix: Changed the first mockResolvedValue to mockResolvedValueOnce so the initial mount uses Hans Müller; subsequent calls (refetch after update) use the Max Mustermann value.
  • Files modified: VehicleTable.test.tsx
  • Commit: 4d81b8b (included in GREEN task commit)

Verification Results

  • pnpm --filter @tessera/web exec tsc --noEmit -p tsconfig.json exits 0: PASS
  • pnpm --filter @tessera/web test -- --run VehicleTable: 5/5 passed
  • grep -q "credentials: 'include'" dkv-api.ts: PASS
  • grep -q "check-now" dkv-api.ts: PASS
  • grep -q "useTranslations('dkvFleet')" page.tsx: PASS
  • grep -q "role=\"switch\"" InboxConfigForm.tsx: PASS
  • grep -q "vi.mock" VehicleTable.test.tsx: PASS
  • All 11 files present on disk: PASS

Known Stubs

None. All API client functions are fully wired to real /dkv/* endpoints (Plan 04). All UI interactions call the corresponding dkv-api functions. The module is fully operable once the backend containers are running.

Threat Flags

None. All T-07-12 to T-07-15 mitigations from the plan's threat register are implemented:

  • T-07-12: Password blank-on-load in configToForm(); hasPassword-only interface field
  • T-07-13: exportFileUrl() + encodeURIComponent; no client-built paths
  • T-07-14: All backend strings via React text nodes; StatusBadge uses title attribute
  • T-07-15: accept=".csv" on hidden file input; replace requires explicit two-step confirm

TDD Gate Compliance

  • RED gate commit: c739d27 ✓ (test(07-05): add failing VehicleTable tests)
  • GREEN gate commit: 4d81b8b ✓ (feat(07-05): VehicleTable + CsvImportButton + vehicles page)
  • All tests pass: 5/5 ✓

Self-Check: PASSED