Files
tessera-ctl/.planning/phases/07-dkv-fleet-module/07-05-PLAN.md
T
schalli de06794e67
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 38s
Tessera CI/CD / Tests (push) Waiting to run
docs(07): create phase 7 execution plans for DKV fleet module
6 plans covering full pipeline: PDF parsing foundation (Wave 0),
inbox providers + export/SMTP services (Wave 1), pipeline
orchestration + frontend pages + settings UI (Wave 2). Includes
D-06 MailModule DB-config migration and Nyquist validation strategy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 18:56:59 +02:00

26 KiB
Raw Blame History

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, user_setup, must_haves
phase plan type wave depends_on files_modified autonomous requirements user_setup must_haves
07-dkv-fleet-module 05 execute 2
07-01
07-02
07-03
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/vehicles/page.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
true
DKV-01
DKV-03
DKV-04
truths artifacts key_links
User can open /modules/dkv-fleet and see the processing history (Verarbeitungshistorie) and the last export files
User can click 'Jetzt prüfen' to trigger an inbox poll and the history refetches afterward
User can download an export file from the history row / export list
User can configure the inbox (protocol, host, port, encryption, folder, sender filter, interval, credentials, recipient, format string, active) and save it
User can test the inbox connection and see inline success/error feedback
User can list, add, inline-edit, and delete vehicles in the vehicle master table
User can import a CSV of vehicles in merge or replace mode (replace asks for destructive confirmation)
path provides contains
apps/web/src/lib/dkv-api.ts Typed fetch client for all /dkv/* routes (config, check-now, history, exports, vehicles CRUD, CSV import, test-connection) credentials: 'include'
path provides min_lines
apps/web/src/app/(portal)/modules/dkv-fleet/page.tsx Surface A — module main page (history + export list + Jetzt prüfen) 30
path provides min_lines
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx Inbox config form (Surface B inbox tab) 40
path provides min_lines
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.tsx Vehicle master CRUD table with inline edit 40
path provides contains
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.test.tsx Vitest coverage for vehicle CRUD table (Wave 0 / VALIDATION DKV-03) vi.mock
from to via pattern
apps/web/src/app/(portal)/modules/dkv-fleet/page.tsx apps/web/src/lib/dkv-api.ts (checkNow + fetchHistory) Jetzt prüfen button calls checkNow then refetches history checkNow|fetchHistory
from to via pattern
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx apps/web/src/lib/dkv-api.ts (fetchConfig/saveConfig/testConnection) form load + save + test saveConfig|testConnection
from to via pattern
apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.tsx apps/web/src/lib/dkv-api.ts (vehicles CRUD) list/create/update/delete createVehicle|updateVehicle|deleteVehicle
Deliver the DKV Fleet Module frontend (Surfaces A and B from 07-UI-SPEC.md): the typed API client, the module main page (processing history + export download list + "Jetzt prüfen" trigger), the module settings inbox-config form, and the vehicle master CRUD table with CSV import. Consumes the `/dkv/*` REST surface built in Plan 04.

Purpose: This is the user-facing half of DKV-01 (inbox config + manual check), DKV-03 (vehicle mapping CSV + manual edit), and DKV-04 (export download + processing history). Each task is a vertical slice: after it, a user can do something concrete in the browser. Output: dkv-api client, main page (+ StatusBadge, InvoiceHistoryTable, ExportFileList), settings page (+ InboxConfigForm), vehicles page (+ VehicleTable, CsvImportButton) and a VehicleTable test.

Phase Goal

Als Administrator möchte ich DKV-Tankkarten-Rechnungen automatisch aus einem E-Mail-Postfach verarbeiten lassen, damit Flotten-Tankdaten ohne manuelle Eingabe als Excel-Datei exportiert und per SMTP zugestellt werden.

This plan makes the pipeline operable from the browser: configure the inbox, manage vehicle→driver mapping, trigger checks, and download exports.

Requirement mapping note: The checker brief listed DKV-03, DKV-05 for this plan. DKV-05 (SMTP in general settings) has no UI here — it is delivered by Plan 07-06. This plan's tasks address DKV-01 (inbox config + check-now UI), DKV-03 (vehicle CRUD + CSV), and DKV-04 (export download + history UI), which is the accurate mapping.

<execution_context> @$HOME/.claude/gsd-core/workflows/execute-plan.md @$HOME/.claude/gsd-core/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/phases/07-dkv-fleet-module/07-CONTEXT.md @.planning/phases/07-dkv-fleet-module/07-UI-SPEC.md @.planning/phases/07-dkv-fleet-module/07-PATTERNS.md @apps/web/src/lib/calendar-api.ts @apps/web/src/components/settings/calendar-source-form.tsx @apps/web/src/app/(portal)/settings/dashboard/calendar/calendar-settings.test.tsx @apps/web/src/app/(portal)/modules/domaincheck/page.tsx

Artifacts this phase produces (Plan 05 portion)

New symbols (exclude from drift verification):

  • File apps/web/src/lib/dkv-api.ts: types DkvConfig, DkvHistoryEntry, DkvVehicle, CreateVehiclePayload, UpdateVehiclePayload; functions fetchConfig, saveConfig, testConnection, checkNow, fetchHistory, fetchVehicles, createVehicle, updateVehicle, deleteVehicle, importVehiclesCsv, exportFileUrl
  • Components DkvFleetPage (default), StatusBadge, InvoiceHistoryTable, ExportFileList, DkvFleetSettingsPage (default), InboxConfigForm, DkvFleetVehiclesPage (default), VehicleTable, CsvImportButton

Path note: The existing API-client convention in this repo is apps/web/src/lib/<domain>-api.ts (see apps/web/src/lib/calendar-api.ts). The checker brief referenced lib/api/dkv.ts with analog lib/api/calendar.ts, but that path does not exist in the repo. This plan uses apps/web/src/lib/dkv-api.ts to match the real, existing analog. The @ import alias maps to apps/web/src.

Task 1: dkv-api client + StatusBadge + module main page (Surface A — DKV-04/DKV-01) apps/web/src/lib/dkv-api.ts, 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/page.tsx - apps/web/src/lib/calendar-api.ts — API-client convention: `API_URL` const, `credentials: 'include'`, exported typed functions, throw on non-ok (mirror this file exactly for structure) - apps/web/src/app/(portal)/modules/domaincheck/page.tsx — page shell: `'use client'`, `useTranslations('dkvFleet')`, `mx-auto max-w-5xl space-y-6 p-6`, card panel - 07-UI-SPEC.md "Surface A: Module Main Page" — full layout, InvoiceHistoryTable column spec (6 columns + widths), ExportFileList spec, "Jetzt prüfen" button states, skeleton rows, pagination > 25 rows - 07-UI-SPEC.md "StatusBadge Component" — exact OKLCH inline styles per status, rounded-full px-2 py-0.5 text-xs font-medium - 07-UI-SPEC.md "Color" + "Typography" + "Spacing Scale" — token usage; accent reserved list - 07-PATTERNS.md "dkv-fleet/page.tsx" — analog domaincheck/page.tsx; layout note (max-w-5xl) - 07-CONTEXT.md D-20 (history columns), D-15 (last 10 export files) Create `apps/web/src/lib/dkv-api.ts` following the calendar-api.ts structure: `const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001';`, all calls use `credentials: 'include'` and throw `new Error(...)` on non-ok. Export TypeScript types mirroring the Plan 04 backend contract: `DkvConfig` (protocol, host, port, encryption, folder, senderFilter, pollIntervalMin, isActive, exportRecipient, vehicleFormatString, username, hasPassword boolean — never a password), `DkvHistoryEntry` (id, datumZeit string, rechnungsnummer, anzahlFahrzeuge number, anzahlTransaktionen number, status 'Verarbeitet'|'Fehler'|'Versand fehlgeschlagen', errorMessage?, exportFilename?), `DkvVehicle` (id, kennzeichen, marke, modell, fahrer), `CreateVehiclePayload`, `UpdateVehiclePayload`. Export functions: `fetchConfig()` GET /dkv/config; `saveConfig(payload)` PUT /dkv/config; `testConnection(payload)` POST /dkv/test-connection → `{ success: boolean }`; `checkNow()` POST /dkv/check-now; `fetchHistory(page?, limit?)` GET /dkv/history with URLSearchParams; `fetchVehicles()` GET /dkv/vehicles; `createVehicle(payload)` POST /dkv/vehicles; `updateVehicle(id, payload)` PUT /dkv/vehicles/:id; `deleteVehicle(id)` DELETE /dkv/vehicles/:id; `importVehiclesCsv(file, mode)` POST /dkv/vehicles/import as `multipart/form-data` (FormData with `file` and `mode`, do NOT set Content-Type header manually); and a pure helper `exportFileUrl(filename)` returning `${API_URL}/dkv/exports/${encodeURIComponent(filename)}`. Create `StatusBadge.tsx`: props `{ status, errorMessage? }`. Render `` with the exact inline OKLCH color/background per 07-UI-SPEC "StatusBadge Component" for each of the three statuses; add `title={errorMessage}` for Fehler / Versand fehlgeschlagen when provided. Labels come from `t('status.processed'|'status.error'|'status.sendFailed')`. Create `InvoiceHistoryTable.tsx`: fetches via `fetchHistory` in useEffect, renders the 6-column table exactly per 07-UI-SPEC (column header style `text-xs font-semibold uppercase tracking-wider text-muted-foreground`, widths and alignments as specified, row hover `hover:bg-muted/50`, container `rounded border border-border overflow-hidden`). Show 5 skeleton rows while loading (`h-4 rounded bg-muted animate-pulse`). Render StatusBadge in the STATUS column and, in the EXPORTDATEI column, a download `` with inline SVG download icon (`text-primary text-sm hover:underline`) when present else an em-dash `–`. Empty state uses `t('emptyHistory')` heading + `t('emptyHistoryBody')`. Add pagination controls only when total > 25 (per UI-SPEC), wiring `fetchHistory(page, 25)`. Create `ExportFileList.tsx`: lists up to 10 export files (derive filenames from history entries' `exportFilename`, newest first, dedup) each as an inline SVG download icon + `` (`text-sm text-primary hover:underline`) using `exportFileUrl`; empty state `t('emptyExports')`. Create `page.tsx` (`'use client'`, default export `DkvFleetPage`): `mx-auto max-w-5xl space-y-6 p-6`. Header row: `

{t('pageTitle')}

` with the "Jetzt prüfen" button right-aligned and the "Zuletzt geprüft" text (`text-sm text-muted-foreground`). "Jetzt prüfen" button uses the exact idle/loading classes from UI-SPEC; on click set loading, call `checkNow()`, on success clear loading and trigger an InvoiceHistoryTable refetch (lift a refresh key or expose a ref/callback), on error show the destructive error banner above the table (`border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive` with a × close button) using `t('errors.pollFailed')`. Render `

Verarbeitungshistorie

` + ``, then `

Letzte Exportdateien

` + ``. All copy via `useTranslations('dkvFleet')` keys defined in 07-UI-SPEC (Plan 04 ships these keys). Dark + light mode must both work (use CSS tokens only). Do not introduce any external icon package — inline SVG only.
pnpm --filter @tessera/web exec tsc --noEmit -p tsconfig.json && grep -q "credentials: 'include'" apps/web/src/lib/dkv-api.ts && grep -q "check-now" apps/web/src/lib/dkv-api.ts && grep -q "useTranslations('dkvFleet')" "apps/web/src/app/(portal)/modules/dkv-fleet/page.tsx" - `dkv-api.ts` exports `checkNow`, `fetchHistory`, `fetchConfig`, `saveConfig`, `testConnection`, vehicle CRUD functions, `importVehiclesCsv`, and `exportFileUrl`; all use `credentials: 'include'` - `dkv-api.ts` never declares a returned `password` field on `DkvConfig` (only `hasPassword`) - Main page renders an `

` with `t('pageTitle')` and a "Jetzt prüfen" button that calls `checkNow` then refetches history - InvoiceHistoryTable renders the 6 columns from UI-SPEC and uses StatusBadge for status - Export download links use `exportFileUrl(...)` (server-validated filename) — no client-built path traversal - Web type-check / build passes (`tsc --noEmit`) The module main page lists processing history + export files and the user can trigger an inbox check from the browser. Task 2: Module settings page + InboxConfigForm (Surface B inbox — DKV-01) 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/components/settings/calendar-source-form.tsx — form field pattern: `'use client'`, controlled inputs, label `mb-1 block text-sm text-foreground`, input `h-9 w-full max-w-md rounded border border-border bg-background px-3 text-sm`, select pattern, password input, actions row `flex gap-3`, submit `data-testid` + disabled styling, show/hide not present here (add it per UI-SPEC) - apps/web/src/app/(portal)/settings/dashboard/page.tsx — settings page heading `mb-6 text-lg font-semibold text-foreground` - apps/web/src/lib/dkv-api.ts (Task 1) — fetchConfig / saveConfig / testConnection signatures - 07-UI-SPEC.md "Surface B: Module Settings Page" → "Tab 1: Posteingang → InboxConfigForm" — exact field list/order, password show/hide toggle, Active toggle switch, form actions row, inline test feedback colors - 07-UI-SPEC.md "Copywriting Contract" + form i18n keys (`form.*`) - 07-CONTEXT.md D-01/D-02/D-03 (inbox fields), D-07 (export recipient per module), D-19 (vehicle format string) Create `settings/page.tsx` (`'use client'`, default export `DkvFleetSettingsPage`, `useTranslations('dkvFleet')`): heading `

{t('settingsTitle')}

`, then render ``. (Per the checker's route split, vehicle management lives at its own `/modules/dkv-fleet/vehicles` route built in Task 3; add a simple inline link/tab to it here, e.g. a secondary-styled `{t('tabs.vehicles')}` styled per the UI-SPEC tab pattern.) Create `InboxConfigForm.tsx` (`'use client'`): on mount `fetchConfig()` to populate controlled state; render the fields in the exact order from UI-SPEC Tab 1 — Protokoll (select IMAP/Exchange), Host (text), Port (number), Verschlüsselung (select Keine/STARTTLS/SSL-TLS → values none/starttls/ssl-tls), Ordner (text default "INBOX"), Absenderfilter (email, optional, placeholder rechnung@dkv.com), Abrufintervall (number min=5, suffix "Minuten"), Benutzername (text optional), Passwort (password optional with show/hide toggle button — `type="button"`, inline eye/eye-off SVG, toggles input type between password/text), Export-Empfänger (email required), Fahrzeug-Formatstring (text default `{Marke}/{Modell}/{Kennzeichen}` with help text listing placeholders), Aktiv (toggle switch `role="switch" aria-checked`). Use the calendar-source-form label/input classes. Form actions row `flex gap-3 pt-4 border-t border-border mt-6`: "Verbindung testen" (secondary) → calls `testConnection(currentFormPayload)` and shows inline green/destructive feedback per UI-SPEC; "Einstellungen speichern" (primary) → calls `saveConfig`, disabling the button while saving and showing inline error on failure. Never render the existing password back from the server (config returns `hasPassword`, not the password) — leave the password field blank and only send it when the user types a new one. All copy via `t('form.*')`. Both color themes must work. pnpm --filter @tessera/web exec tsc --noEmit -p tsconfig.json && grep -q "saveConfig" "apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx" && grep -q "testConnection" "apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx" && grep -q "role=\"switch\"" "apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx" - Settings page renders `t('settingsTitle')` heading and the InboxConfigForm - InboxConfigForm renders all UI-SPEC Tab-1 fields in order, with the show/hide password toggle and the Active `role="switch"` toggle - Abrufintervall input enforces `min=5` - "Verbindung testen" calls `testConnection`; "Einstellungen speichern" calls `saveConfig` - Password field is never pre-filled from server data (only `hasPassword` is known) - Web type-check / build passes The user can configure and test the inbox connection and save module settings from the browser. Task 3: Vehicles page + VehicleTable + CsvImportButton (Surface B vehicles — DKV-03) apps/web/src/app/(portal)/modules/dkv-fleet/vehicles/page.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)/settings/dashboard/calendar/calendar-settings.test.tsx — Vitest+Testing-Library pattern: `vi.mock('next-intl', ...)`, `vi.mock('@/lib/calendar-api', ...)`, dynamic `await import(...)`, `render`, `screen`, `waitFor`, `fireEvent`, `afterEach` cleanup. Mirror this exactly for VehicleTable.test.tsx (mock `@/lib/dkv-api`) - apps/web/src/lib/dkv-api.ts (Task 1) — fetchVehicles / createVehicle / updateVehicle / deleteVehicle / importVehiclesCsv - apps/web/src/components/settings/calendar-source-form.tsx — input/label/button classes to reuse - 07-UI-SPEC.md "Tab 2: Fahrzeuge → VehicleTable + CsvImportButton" — toolbar, columns (KENNZEICHEN/MARKE/MODELL/FAHRER/AKTIONEN), view vs edit row modes, icon button aria-labels + SVG sizes, add-vehicle row, CSV import dialog (merge/replace), destructive replace confirm, success toast - 07-UI-SPEC.md "Copywriting Contract" — delete dialog + CSV replace dialog copy; "col.*" header keys - apps/web/vitest.config.ts — confirm jsdom env + setup (tests run via `pnpm --filter @tessera/web test`) VehicleTable.test.tsx (write FIRST — RED): - Renders a list of two mocked vehicles (Kennzeichen, Marke, Modell, Fahrer) returned by mocked `fetchVehicles` - Shows the empty-state heading (`t('emptyVehicles')` → mocked string) when `fetchVehicles` resolves `[]` - Clicking the delete (trash) icon button opens a confirm dialog, and confirming calls `deleteVehicle` with the vehicle id - Clicking the edit (pencil) icon button switches the row to inputs; saving calls `updateVehicle` with the id and changed fields - Icon-only action buttons expose the UI-SPEC `aria-label`s ("Fahrzeug bearbeiten", "Fahrzeug löschen", "Änderungen speichern", "Bearbeitung abbrechen") Write `VehicleTable.test.tsx` first and confirm it fails (RED) before implementing — mirror the calendar-settings.test.tsx mocking approach exactly (mock `next-intl` to echo keys/short strings, mock `@/lib/dkv-api`). Create `VehicleTable.tsx` (`'use client'`): on mount `fetchVehicles()`; toolbar `flex justify-between items-center mb-3` with a left count badge (`t('vehicleCount', { count })`) and right `[CsvImportButton]` + "Fahrzeug hinzufügen" (primary) buttons. Table columns per UI-SPEC with the shared header style. Each row has view mode (plain cells + pencil + trash icon buttons) and edit mode (text inputs, Kennzeichen `uppercase`, + checkmark/× icon buttons). All icon-only buttons carry the exact UI-SPEC `aria-label`s and SVG sizes (16×16, inline SVG). "Fahrzeug hinzufügen" appends a new empty edit-mode row. Delete uses an inline confirm dialog with the UI-SPEC delete copy (`deleteVehicle`/`deleteVehicleBody`), confirm button destructive-styled; on confirm call `deleteVehicle(id)` and refetch. Save row calls `createVehicle` (new) or `updateVehicle(id, ...)` (existing); validation: Kennzeichen required, inline `text-destructive text-xs` on error. Wire CsvImportButton's `onImported` to refetch + show a success toast (`importSuccess`). Create `CsvImportButton.tsx` (`'use client'`): hidden ``; the visible "CSV importieren" button triggers it. After a file is selected, show the inline import-mode dialog (Zusammenführen default / Ersetzen) per UI-SPEC; "Ersetzen" → destructive confirm dialog before importing. On confirm call `importVehiclesCsv(file, mode)` then call the `onImported(count)` callback. Validate the selected file is `.csv` client-side; show parse/import errors as `text-destructive text-sm` in the dialog. Create `vehicles/page.tsx` (`'use client'`, default export `DkvFleetVehiclesPage`, `useTranslations('dkvFleet')`): heading `

{t('tabs.vehicles')}

` (or settingsTitle + subheading), a back link to `/modules/dkv-fleet/settings`, then ``. Import VehicleTable/CsvImportButton from `../settings/components/` (UI-SPEC component file map). Both color themes must work; inline SVG icons only. pnpm --filter @tessera/web test -- --run VehicleTable && pnpm --filter @tessera/web exec tsc --noEmit -p tsconfig.json - `VehicleTable.test.tsx` exists, mocks `@/lib/dkv-api` + `next-intl`, and passes (was RED before VehicleTable existed) - VehicleTable lists vehicles, supports inline edit (calls `updateVehicle`), create (`createVehicle`), and delete with confirm dialog (`deleteVehicle`) - All icon-only action buttons carry the UI-SPEC `aria-label`s - CsvImportButton offers merge/replace modes and shows a destructive confirm before replace, calling `importVehiclesCsv(file, mode)` - `vehicles/page.tsx` renders VehicleTable under a heading with a back link to settings - `pnpm --filter @tessera/web test` (VehicleTable) and web type-check pass The user can manage the vehicle→driver master list (CRUD + CSV merge/replace) from the browser, covered by a passing Vitest test.

<threat_model>

Trust Boundaries

Boundary Description
browser form → /dkv/* API Admin-entered inbox config, credentials, and vehicle/CSV data cross to the backend
API response → React render Backend-supplied history/vehicle/export data is rendered in the DOM
user file → CSV import User-selected file uploaded for bulk vehicle import

STRIDE Threat Register

Threat ID Category Component Disposition Mitigation Plan
T-07-12 Information Disclosure InboxConfigForm / dkv-api.ts mitigate Password field never pre-filled from server (config returns hasPassword only, never the secret — mirrors calendar T-05-09); password sent only when user types a new one; transport is cookie auth via credentials: 'include' (V3)
T-07-13 Tampering ExportFileList / InvoiceHistoryTable mitigate Download URLs built with exportFileUrl() + encodeURIComponent; the server enforces the DKV_*.xlsx filename format and path-traversal guard (Plan 04) — client never constructs raw paths
T-07-14 Injection (XSS) InvoiceHistoryTable / VehicleTable mitigate All backend strings rendered through React text nodes (auto-escaped); no dangerouslySetInnerHTML; StatusBadge tooltip uses the title attribute, not raw HTML
T-07-15 Tampering CsvImportButton mitigate Client restricts selection to .csv (accept=".csv") and the replace path requires an explicit destructive confirmation; server-side validation/size limits remain authoritative (Plan 04)
T-07-SC — npm/pnpm installs n/a No new frontend dependencies are added in this plan (inline SVG icons, existing Vitest/Testing-Library only)
</threat_model>
- `pnpm --filter @tessera/web exec tsc --noEmit -p tsconfig.json` exits 0 - `pnpm --filter @tessera/web test -- --run VehicleTable` passes - All copy resolves through `useTranslations('dkvFleet')` keys defined in 07-UI-SPEC (shipped by Plan 04) - Components honor the 07-UI-SPEC color/typography/spacing tokens in both light and dark mode

<success_criteria>

  • DKV-01 inbox-config UI + manual "Jetzt prüfen" trigger operable in the browser
  • DKV-03 vehicle master CRUD + CSV merge/replace import operable, covered by a Vitest test
  • DKV-04 export download links + processing-history display operable
  • All three surfaces follow the 07-UI-SPEC visual contract </success_criteria>
Create `.planning/phases/07-dkv-fleet-module/07-05-SUMMARY.md` when done. Record the InvoiceHistoryTable refetch mechanism chosen, how the password-blank-on-load behavior is implemented, and any UI-SPEC deviations.