Files
tessera-ctl/.planning/phases/07-dkv-fleet-module/07-UI-SPEC.md
T
schalli baf502d967
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 40s
Tessera CI/CD / Tests (push) Waiting to run
docs(07): fix blocking typography and spacing issues in UI-SPEC
Collapse Display h1 weight from 700 to 600 (font-semibold) to keep
declared weights at exactly two (400 + 600). Remove 12px md token from
named spacing scale; move px-3/py-1.5 to Component Exceptions subsection.
Apply non-blocking recommendations: focal point sentence, aria-labels for
icon-only AKTIONEN buttons, and more specific form CTA copy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 14:53:49 +02:00

598 lines
27 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: 7
slug: dkv-fleet-module
status: draft
shadcn_initialized: false
preset: none
created: 2026-06-26
---
# Phase 7 — UI Design Contract: DKV Fleet Module
> Visual and interaction contract for frontend phases. Generated by gsd-ui-researcher, verified by gsd-ui-checker.
>
> This module spans **3 frontend surfaces**: module main page (history + exports),
> module settings page (inbox config + vehicle CRUD), and general settings SMTP page.
> Sections beyond the base template are phase-specific extensions required for a
> multi-surface module.
---
## Design System
| Property | Value | Source |
|----------|-------|--------|
| Tool | manual — shadcn CSS-variable convention; no CLI (`components.json` absent) | codebase audit |
| Preset | not applicable | — |
| Component library | manual hand-written components following shadcn token convention (no Radix dependency) | codebase audit |
| Icon library | inline SVG only — no external icon package | codebase audit (admin-sidebar.tsx) |
| Font | Inter via `var(--font-sans)` — system-ui fallback | globals.css |
| Border radius | `var(--radius)` = 0.5rem (8px) | globals.css |
| Color space | OKLCH throughout | globals.css |
**Registry safety:** Not applicable — no `components.json`, no CLI registry consumption.
**shadcn gate:** components.json NOT found. Project has established manual approach; do not initialize CLI. All new components must hand-write following existing patterns in `apps/web/src/components/`.
---
## Spacing Scale
Declared values (must be multiples of 4). Source: codebase audit of existing components.
| Token | Value | Tailwind class | Usage |
|-------|-------|----------------|-------|
| xs | 4px | `gap-1`, `p-1` | Icon gaps, inline element padding |
| sm | 8px | `gap-2`, `p-2` | Compact element spacing, icon-to-label gap |
| lg | 16px | `p-4`, `gap-4` | Default section padding, form field gap |
| xl | 24px | `p-6`, `gap-6` | Card padding, section breaks |
| 2xl | 32px | `gap-8` | Layout column gaps |
| 3xl | 48px | — | Major page-level spacing |
### Component Exceptions
The following sub-4 spacing values appear in inherited component patterns. They are **not named scale tokens** — do not use them for new layout decisions.
| Tailwind classes | Origin | Why not a token |
|-----------------|--------|-----------------|
| `px-3 py-1.5` (12px / 6px) | `CalendarSourceForm` `h-9` input pattern | Inherited browser-default input sizing; compensates for 36px target height within the 8-point grid constraint. Apply only inside `<input>` and `<select>` elements following the `h-9` pattern. |
**Standard form element heights (from existing `CalendarSourceForm` pattern):**
- Input height: `h-9` (36px)
- Input max-width: `max-w-md` (448px) — do not exceed on settings forms
- Button: `px-4 py-2` (16px / 8px)
---
## Typography
Declared sizes from codebase audit (`grep -r text-* apps/web/src/components`):
| Role | Size | Tailwind | Weight | Line Height | Usage |
|------|------|----------|--------|-------------|-------|
| Display | 24px | `text-2xl` | 600 (`font-semibold`) | 1.2 | Module page `<h1>` |
| Heading | 18px | `text-lg` | 600 (`font-semibold`) | 1.3 | Settings page `<h1>`, section titles — follows `settings/dashboard/page.tsx` |
| Body | 14px | `text-sm` | 400 (`font-normal`) | 1.5 | Form labels, table cells, nav items, general text |
| Label | 12px | `text-xs` | 600 (`font-semibold`) | 1.0 | Section category labels (uppercase + tracking-wider), table column headers, status badges |
**Declared weights:** 400 (regular) and 600 (semibold). No other weights are permitted in this phase.
**Section category label pattern** (from `settings-sidebar.tsx` and `admin-sidebar.tsx`):
```
text-xs font-semibold uppercase tracking-wider text-muted-foreground
```
Use this verbatim for sidebar category headings and table `<thead>` column labels.
---
## Color
All values from `apps/web/src/app/globals.css`. OKLCH color space. Dual light/dark mode support is mandatory for all new components.
| Role | CSS Token | Light Value | Dark Value | Usage |
|------|-----------|-------------|------------|-------|
| Dominant (60%) | `var(--background)` | oklch(0.99 0 0) | oklch(0.23 0.01 260) | Page background, main content area |
| Secondary (30%) | `var(--card)` | oklch(1 0 0) | oklch(0.27 0.01 260) | Table container, form panels, export file list |
| — | `var(--sidebar)` | oklch(0.97 0 0) | oklch(0.20 0.01 260) | Settings sidebar background |
| — | `var(--muted)` | oklch(0.96 0 0) | oklch(0.30 0.01 260) | Table row hover, disabled field backgrounds |
| Accent (10%) | `var(--primary)` | oklch(0.91 0.19 102) | oklch(0.91 0.19 102) | Primary CTA only — see reserved list below |
| Destructive | `var(--destructive)` | oklch(0.55 0.2 27) | oklch(0.55 0.2 27) | Delete vehicle, destructive CSV replace, error text |
| Border | `var(--border)` | oklch(0.90 0 0) | oklch(0.30 0.01 260) | Input borders, table borders, dividers |
| Muted text | `var(--muted-foreground)` | oklch(0.55 0 0) | oklch(0.65 0 0) | Placeholder text, secondary info, empty states |
**Accent (`var(--primary)`) is reserved for:**
- "Jetzt prüfen" button background
- Active state indicator in tab bar (border-bottom line)
- Focus ring (`var(--ring)`) on focused inputs
- Export file download links (text color: `text-primary`)
- Active settings sidebar item (`bg-sidebar-accent text-sidebar-accent-foreground`)
**Accent is NOT used for:** passive table borders, section headings, helper text, secondary buttons, status indicators.
### Status Badge Colors (new — not in globals.css)
These values are defined inline in the `StatusBadge` component. Do NOT add to globals.css for v1.
| State | Label | Text | Background |
|-------|-------|------|-----------|
| Verarbeitet | "Verarbeitet" | `oklch(0.40 0.15 148)` (green) | `oklch(0.40 0.15 148 / 0.10)` |
| Fehler | "Fehler" | `var(--destructive)` | `oklch(0.55 0.2 27 / 0.10)` |
| Versand fehlgeschlagen | "Versand fehlgeschlagen" | `oklch(0.50 0.13 70)` (amber) | `oklch(0.50 0.13 70 / 0.10)` |
Badge shape: `rounded-full px-2 py-0.5 text-xs font-medium`
---
## Copywriting Contract
All UI strings are German (the portal language for this tenant). i18n keys are listed in the Phase-Specific Extensions section below.
| Element | Copy |
|---------|------|
| Primary CTA (manual trigger) | "Jetzt prüfen" |
| CTA loading state | "Wird geprüft..." |
| Last checked timestamp | "Zuletzt geprüft: {TT.MM.JJJJ, HH:mm} Uhr" |
| Never checked | "Noch nie geprüft" |
| Add vehicle button | "Fahrzeug hinzufügen" |
| CSV import button | "CSV importieren" |
| Save (form action rows) | "Einstellungen speichern" |
| Cancel (form action rows) | "Bearbeitung abbrechen" |
| Cancel (dialogs) | "Abbrechen" |
| Test SMTP connection | "Verbindung testen" |
| History empty state heading | "Noch keine Rechnungen verarbeitet" |
| History empty state body | "Klicke auf 'Jetzt prüfen', um den Posteingang zu prüfen. Neue Rechnungen erscheinen hier automatisch." |
| Vehicle table empty state heading | "Keine Fahrzeuge hinterlegt" |
| Vehicle table empty state body | "Füge Fahrzeuge manuell hinzu oder lade eine CSV-Datei hoch." |
| Export files empty | "Noch keine Exportdateien vorhanden" |
| Poll error banner | "Posteingang konnte nicht geprüft werden. Überprüfe die Konfiguration unter 'Moduleinstellungen › Posteingang'." |
| SMTP send failure (in history badge tooltip) | "Export wurde erstellt, konnte aber nicht gesendet werden. Die Datei ist weiterhin zum Download verfügbar." |
| SMTP test success | "Verbindung erfolgreich" |
| SMTP test failure | "Verbindung fehlgeschlagen: {fehlermeldung}" |
| Delete vehicle — dialog title | "Fahrzeug löschen" |
| Delete vehicle — dialog body | "Das Fahrzeug {Kennzeichen} wird dauerhaft aus der Stammdatenliste entfernt. Diese Aktion kann nicht rückgängig gemacht werden." |
| Delete vehicle — confirm button | "Löschen" (destructive style) |
| CSV replace — dialog title | "Fahrzeuge ersetzen?" |
| CSV replace — dialog body | "Alle vorhandenen Fahrzeuge werden durch die CSV-Daten ersetzt. Diese Aktion kann nicht rückgängig gemacht werden." |
| CSV replace — confirm button | "Ersetzen" (destructive style) |
| CSV import success toast | "{count} Fahrzeuge importiert" |
**Destructive action pattern:** Both destructive actions use an inline dialog/modal (not a toast). Confirm button uses `bg-destructive text-destructive-foreground` style. Cancel is always leftmost and uses secondary button style.
---
## Registry Safety
| Registry | Blocks Used | Safety Gate |
|----------|-------------|-------------|
| shadcn official | none | not required — no CLI |
| third-party | none | not applicable |
---
## Phase-Specific Extensions
The following sections are required for multi-surface modules. gsd-executor must treat these as binding visual contracts identical to the base sections above.
---
### UI Surfaces Overview
| Surface | Route | Purpose |
|---------|-------|---------|
| A — Module Main | `/modules/dkv-fleet` | Invoice history table + export download list + "Jetzt prüfen" trigger |
| B — Module Settings | `/modules/dkv-fleet/settings` | Inbox config form (tab 1) + vehicle master CRUD table (tab 2) |
| C — General Settings SMTP | `/settings/general/smtp` | Shared SMTP configuration (extends SettingsSidebar with new category) |
---
### Surface A: Module Main Page
**File:** `apps/web/src/app/(portal)/modules/dkv-fleet/page.tsx`
**Primary visual anchor:** "Jetzt prüfen" button (top-right of page header) + InvoiceHistoryTable immediately below. All other elements are secondary.
**Layout (top to bottom):**
```
[ Page Header Row ]
<h1>DKV Flotte</h1> [Jetzt prüfen] ← right-aligned button
<p>Zuletzt geprüft: …</p> ← text-sm text-muted-foreground
[ Error Banner (conditional — only on poll failure) ]
border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive
+ × close button (right-aligned in banner)
[ Invoice Processing History ]
<h2>Verarbeitungshistorie</h2> ← text-lg font-semibold mb-4
<InvoiceHistoryTable />
[ Last Export Files ]
<h2>Letzte Exportdateien</h2> ← text-sm font-semibold mb-2 mt-6
<ExportFileList />
```
**InvoiceHistoryTable specification:**
Column headers: `text-xs font-semibold uppercase tracking-wider text-muted-foreground` with `px-4 py-3 border-b border-border`
| # | Column Header | Data Format | Width | Alignment |
|---|---------------|-------------|-------|-----------|
| 1 | DATUM/ZEIT | "26.06.2026, 14:32 Uhr" | 160px | left |
| 2 | RECHNUNGSNUMMER | string (e.g. "26-651566449-001") | 200px | left |
| 3 | FAHRZEUGE | number | 100px | right |
| 4 | TRANSAKTIONEN | number | 120px | right |
| 5 | STATUS | `<StatusBadge>` component | 180px | left |
| 6 | EXPORTDATEI | download link or "–" em dash | auto | left |
Table rows: `px-4 py-3 text-sm`, hover: `hover:bg-muted/50 transition-colors`
Table container: `rounded border border-border overflow-hidden`
Initial loading: 5 skeleton rows — each cell is `h-4 rounded bg-muted animate-pulse`
Pagination: show when > 25 rows. Pattern: `"Zeige 1–25 von {total}"` text-sm text-muted-foreground + [← Zurück] [Weiter →] buttons (secondary style, disabled when at limits)
**Export file in column 6:**
- If present: inline SVG download icon (16×16, currentColor) + filename as `<a href="/api/dkv/exports/{filename}" download>` — `text-primary text-sm hover:underline`
- If absent: `<span class="text-muted-foreground">–</span>`
**ExportFileList specification:**
Each entry on its own line:
```
[↓ icon 16×16] DKV_2026-04_26-651566449-001.xlsx (142 KB)
```
- Icon: inline SVG download arrow, `text-muted-foreground`
- Filename: `<a>` with `text-sm text-primary hover:underline`
- Size: `text-xs text-muted-foreground ml-2`
- Empty state: `<p class="text-sm text-muted-foreground">Noch keine Exportdateien vorhanden</p>`
- Max 10 entries (enforced server-side)
**"Jetzt prüfen" button states:**
| State | Classes | Disabled | Content |
|-------|---------|---------|---------|
| Idle | `rounded bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90` | no | "Jetzt prüfen" |
| Loading | `... opacity-80 cursor-not-allowed` | yes | inline spinner SVG (20×20, animate-spin) + "Wird geprüft..." |
| Success | returns to Idle; table re-fetches | no | "Jetzt prüfen" |
| Error | returns to Idle; error banner appears above table | no | "Jetzt prüfen" |
Spinner SVG: `<svg class="mr-2 h-4 w-4 animate-spin" ...>` — standard circular spinner path, `stroke="currentColor"`.
---
### Surface B: Module Settings Page
**File:** `apps/web/src/app/(portal)/modules/dkv-fleet/settings/page.tsx`
**Layout:**
- Page heading: `<h1 class="text-2xl font-semibold tracking-tight mb-6">Moduleinstellungen</h1>`
- Tab bar immediately below heading (NOT a sub-sidebar — inline tabs)
- Active tab: bottom border `border-b-2 border-primary text-foreground font-medium`
- Inactive tab: `text-muted-foreground hover:text-foreground`
- Tab padding: `px-4 py-2 text-sm`
- Tab bar separator: `border-b border-border mb-6`
**Tab 1: Posteingang → InboxConfigForm**
File: `apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/InboxConfigForm.tsx`
Form fields (in order, all follow CalendarSourceForm pattern):
```
Protokoll * [select: IMAP | Exchange]
Host * [text input]
Port * [number input]
Verschlüsselung * [select: Keine | STARTTLS | SSL-TLS]
Ordner * [text input, default "INBOX"]
Absenderfilter [email input, optional — placeholder: "rechnung@dkv.com"]
Abrufintervall * [number input, min=5, suffix label "Minuten"]
Benutzername [text input, optional]
Passwort [password input, optional, with show/hide toggle button]
Export-Empfänger * [email input]
Fahrzeug-Formatstring*[text input, default "{Marke}/{Modell}/{Kennzeichen}"]
[help text below: text-xs text-muted-foreground
"Verfügbare Platzhalter: {Marke}, {Modell}, {Kennzeichen}, {Fahrer}"]
Aktiv [toggle — visual: rounded pill, primary color when on]
```
Show/hide password toggle: inline button at right edge of input (`type="button"`, no border, shows eye/eye-off inline SVG 16×16). Input changes `type` attribute between `"password"` and `"text"`.
Active toggle: `<button role="switch" aria-checked={isActive}>` — pill shape:
- On: `bg-primary` with white circle
- Off: `bg-muted` with white circle
- Width: 40px, height: 22px
Form actions row: `flex gap-3 pt-4 border-t border-border mt-6`
- "Verbindung testen" (secondary button, left) — tests IMAP/Exchange connection
- "Einstellungen speichern" (primary button)
- Inline feedback below buttons: success in green (`text-sm` at oklch(0.40 0.15 148)) or error in destructive (`text-sm text-destructive`)
**Tab 2: Fahrzeuge → VehicleTable + CsvImportButton**
File: `apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable.tsx`
Table toolbar (above table): `flex justify-between items-center mb-3`
- Left: "Fahrzeuge" count badge — `text-sm text-muted-foreground` e.g. "27 Fahrzeuge"
- Right: [CSV importieren] (secondary button) + [Fahrzeug hinzufügen] (primary button), `gap-2`
Table columns (same header style as InvoiceHistoryTable):
| Column | Width | Input type in edit mode |
|--------|-------|------------------------|
| KENNZEICHEN | 140px | text, `uppercase` |
| MARKE | 140px | text |
| MODELL | 180px | text |
| FAHRER | 200px | text (format: "Vorname Nachname") |
| AKTIONEN | 80px | — |
**Row modes:**
- View mode: plain text cells + pencil icon button + trash icon button in AKTIONEN column
- Edit mode: text inputs replace cells (same `h-9 rounded border border-border bg-background px-2 text-sm` but without `max-w-md` constraint) + checkmark (save) + × (cancel) icon buttons
Icon buttons in AKTIONEN: `p-1 rounded text-muted-foreground hover:text-foreground hover:bg-muted transition-colors`
All icon-only buttons must carry an `aria-label` — they have no visible text:
| Button | SVG size | aria-label | Additional class |
|--------|----------|-----------|-----------------|
| Pencil (edit) | 16×16 | `"Fahrzeug bearbeiten"` | — |
| Trash (delete) | 16×16 | `"Fahrzeug löschen"` | `hover:text-destructive` |
| Checkmark (save row) | 16×16 | `"Änderungen speichern"` | `hover:text-primary` |
| × (cancel row) | 16×16 | `"Bearbeitung abbrechen"` | — |
"Add vehicle" row: clicking [Fahrzeug hinzufügen] appends a new empty edit-mode row at bottom of table.
File: `apps/web/src/app/(portal)/modules/dkv-fleet/settings/components/CsvImportButton.tsx`
Hidden `<input type="file" accept=".csv">`. Clicking [CSV importieren] triggers the hidden input.
After file selected → inline dialog (NOT a full modal; a contained `<div>` in the page below the button, with border and padding):
```
Importmodus:
○ Zusammenführen — neue Fahrzeuge hinzufügen, vorhandene behalten (default)
● Ersetzen — alle vorhandenen Fahrzeuge löschen und neu importieren
↳ [Warning text, text-xs text-destructive]: "Alle vorhandenen Fahrzeuge werden ersetzt."
[Abbrechen] [Importieren]
```
"Importieren" with Replace mode selected → destructive confirm dialog (standard browser-style: title + body + [Abbrechen] [Ersetzen])
Post-import: inline success message `text-sm` at green color + auto-dismiss after 4 seconds + table refresh.
---
### Surface C: General Settings SMTP
**File:** `apps/web/src/app/(portal)/settings/general/smtp/page.tsx`
**SettingsSidebar extension** (`apps/web/src/components/settings/settings-sidebar.tsx`):
Add new "Allgemein" section **above** the existing "Dashboard" section:
```tsx
// New section — insert before existing Dashboard items
<div className="p-4 pb-2">
<h2 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground">
{t('categoryGeneral')}
</h2>
</div>
<nav className="flex flex-col gap-1 px-3 mb-2">
<Link href="/settings/general/smtp" ...>
{t('categorySmtp')}
</Link>
</nav>
```
Active/inactive link styling: identical to existing items in `settings-sidebar.tsx` (copy verbatim).
**SmtpConfigForm:**
File: `apps/web/src/app/(portal)/settings/general/smtp/components/SmtpConfigForm.tsx`
Page heading: `<h1 class="mb-6 text-lg font-semibold text-foreground">SMTP-Konfiguration</h1>` — follows `settings/dashboard/page.tsx` heading pattern exactly.
Form fields (in order, follow CalendarSourceForm pattern, `max-w-md` on all inputs):
```
Host * [text input, placeholder: "smtp.example.com"]
Port * [number input, default 587]
Verschlüsselung * [select: Keine | STARTTLS | SSL-TLS, default STARTTLS]
Benutzername [text input, optional]
Passwort [password input, optional, with show/hide toggle]
Absenderadresse * [email input, placeholder: "tessera@example.com"]
[help text: "Die E-Mail-Adresse, die als Absender angezeigt wird."]
```
Form actions row: `flex gap-3 pt-2`
- "Einstellungen speichern" (primary button)
- "Verbindung testen" (secondary button)
Connection test feedback: appears below the action row:
- Loading: `text-sm text-muted-foreground` — "Verbindung wird getestet..."
- Success: `text-sm` green — "Verbindung erfolgreich"
- Error: `text-sm text-destructive` — "Verbindung fehlgeschlagen: {fehlermeldung}"
- Auto-clears after 6 seconds
---
### StatusBadge Component
**File:** `apps/web/src/app/(portal)/modules/dkv-fleet/components/StatusBadge.tsx`
```
type Status = 'Verarbeitet' | 'Fehler' | 'Versand fehlgeschlagen'
props: { status: Status; errorMessage?: string }
```
Shape: `<span class="inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium">`
Colors (inline style — not CSS classes, as these OKLCH values are not in Tailwind config):
- Verarbeitet: `color: oklch(0.40 0.15 148)`, `background: oklch(0.40 0.15 148 / 0.10)`
- Fehler: `color: var(--destructive)`, `background: oklch(0.55 0.2 27 / 0.10)`
- Versand fehlgeschlagen: `color: oklch(0.50 0.13 70)`, `background: oklch(0.50 0.13 70 / 0.10)`
"Fehler" and "Versand fehlgeschlagen" states: add `title={errorMessage}` attribute when `errorMessage` is provided — renders browser tooltip on hover.
---
### Interaction Contracts Summary
| Interaction | Trigger | Feedback | Error handling |
|------------|---------|---------|----------------|
| "Jetzt prüfen" | button click | loading state on button; table refetch on success | error banner above table; button re-enables |
| Delete vehicle | trash icon click | confirm dialog appears | none (server error: inline `text-destructive` below table) |
| Edit vehicle row | pencil icon click | row switches to edit-mode inputs inline | validation: Kennzeichen required; inline `text-destructive text-xs` below field |
| Save vehicle row | checkmark click | row returns to view mode with new values | server error: `text-destructive text-xs` appears below row |
| CSV import | file selected | import mode dialog appears | file parse error: `text-destructive text-sm` in dialog |
| Save InboxConfig | "Speichern" click | button disables during save; re-enables on done | inline error below button |
| Test inbox connection | "Verbindung testen" click | inline loading → success/error text | shown inline below button |
| Save SmtpConfig | "Speichern" click | button disables during save; re-enables | inline error below button |
| Test SMTP | "Verbindung testen" click | inline loading → success/error text | shown inline, auto-clears 6s |
| Export file download | filename link click | browser download dialog | n/a (HTTP error: browser default) |
**Toast pattern:** Use only for async success states that don't have a natural location (e.g., CSV import success). Toast: fixed bottom-right, `rounded border border-border bg-card shadow-md px-4 py-3 text-sm`, auto-dismiss 4 seconds.
---
### i18n Translation Keys
New keys required for this phase. Add to `apps/web/src/i18n/messages/{de,en}.json`.
**Namespace `dkvFleet` (new):**
```json
{
"pageTitle": "DKV Flotte",
"checkNow": "Jetzt prüfen",
"checking": "Wird geprüft...",
"lastChecked": "Zuletzt geprüft: {time} Uhr",
"neverChecked": "Noch nie geprüft",
"settingsTitle": "Moduleinstellungen",
"historyTitle": "Verarbeitungshistorie",
"exportFilesTitle": "Letzte Exportdateien",
"emptyHistory": "Noch keine Rechnungen verarbeitet",
"emptyHistoryBody": "Klicke auf 'Jetzt prüfen', um den Posteingang zu prüfen. Neue Rechnungen erscheinen hier automatisch.",
"emptyExports": "Noch keine Exportdateien vorhanden",
"emptyVehicles": "Keine Fahrzeuge hinterlegt",
"emptyVehiclesBody": "Füge Fahrzeuge manuell hinzu oder lade eine CSV-Datei hoch.",
"status": {
"processed": "Verarbeitet",
"error": "Fehler",
"sendFailed": "Versand fehlgeschlagen"
},
"col": {
"dateTime": "Datum/Zeit",
"invoiceNumber": "Rechnungsnummer",
"vehicles": "Fahrzeuge",
"transactions": "Transaktionen",
"status": "Status",
"exportFile": "Exportdatei",
"licensePlate": "Kennzeichen",
"brand": "Marke",
"model": "Modell",
"driver": "Fahrer",
"actions": "Aktionen"
},
"addVehicle": "Fahrzeug hinzufügen",
"editVehicle": "Fahrzeug bearbeiten",
"deleteVehicle": "Fahrzeug löschen",
"deleteVehicleBody": "Das Fahrzeug {plate} wird dauerhaft aus der Stammdatenliste entfernt. Diese Aktion kann nicht rückgängig gemacht werden.",
"importCsv": "CSV importieren",
"importMode": "Importmodus",
"importMerge": "Zusammenführen — neue Fahrzeuge hinzufügen, vorhandene behalten",
"importReplace": "Ersetzen — alle vorhandenen Fahrzeuge löschen und neu importieren",
"importReplaceWarning": "Alle vorhandenen Fahrzeuge werden ersetzt.",
"importReplaceConfirmTitle": "Fahrzeuge ersetzen?",
"importReplaceConfirmBody": "Alle vorhandenen Fahrzeuge werden durch die CSV-Daten ersetzt. Diese Aktion kann nicht rückgängig gemacht werden.",
"importReplaceConfirmButton": "Ersetzen",
"importSuccess": "{count} Fahrzeuge importiert",
"vehicleCount": "{count} Fahrzeuge",
"tabs": {
"inbox": "Posteingang",
"vehicles": "Fahrzeuge"
},
"form": {
"protocol": "Protokoll",
"host": "Host",
"port": "Port",
"encryption": "Verschlüsselung",
"encryptionNone": "Keine",
"folder": "Ordner",
"senderFilter": "Absenderfilter",
"pollInterval": "Abrufintervall",
"pollIntervalSuffix": "Minuten",
"username": "Benutzername",
"password": "Passwort",
"exportRecipient": "Export-Empfänger",
"vehicleFormat": "Fahrzeug-Formatstring",
"vehicleFormatHelp": "Verfügbare Platzhalter: {Marke}, {Modell}, {Kennzeichen}, {Fahrer}",
"active": "Aktiv",
"testConnection": "Verbindung testen",
"testTesting": "Verbindung wird getestet...",
"testSuccess": "Verbindung erfolgreich",
"testFailed": "Verbindung fehlgeschlagen: {error}",
"save": "Einstellungen speichern",
"cancel": "Bearbeitung abbrechen",
"cancelDialog": "Abbrechen",
"showPassword": "Passwort anzeigen",
"hidePassword": "Passwort verbergen"
},
"errors": {
"pollFailed": "Posteingang konnte nicht geprüft werden. Überprüfe die Konfiguration unter 'Moduleinstellungen › Posteingang'.",
"smtpFailed": "Export wurde erstellt, konnte aber nicht gesendet werden. Die Datei ist weiterhin zum Download verfügbar."
}
}
```
**Namespace `settings` (extend existing — add keys):**
```json
{
"categoryGeneral": "Allgemein",
"categorySmtp": "SMTP",
"smtp": {
"title": "SMTP-Konfiguration",
"host": "Host",
"port": "Port",
"encryption": "Verschlüsselung",
"username": "Benutzername",
"password": "Passwort",
"fromAddress": "Absenderadresse (Von)",
"fromAddressHelp": "Die E-Mail-Adresse, die als Absender angezeigt wird.",
"save": "Einstellungen speichern",
"test": "Verbindung testen"
}
}
```
---
### Component File Map
| Component | File path | Surface | New / Extend |
|-----------|-----------|---------|--------------|
| InvoiceHistoryTable | `.../modules/dkv-fleet/components/InvoiceHistoryTable.tsx` | A | New |
| StatusBadge | `.../modules/dkv-fleet/components/StatusBadge.tsx` | A | New |
| ExportFileList | `.../modules/dkv-fleet/components/ExportFileList.tsx` | A | New |
| InboxConfigForm | `.../modules/dkv-fleet/settings/components/InboxConfigForm.tsx` | B | New |
| VehicleTable | `.../modules/dkv-fleet/settings/components/VehicleTable.tsx` | B | New |
| CsvImportButton | `.../modules/dkv-fleet/settings/components/CsvImportButton.tsx` | B | New |
| SmtpConfigForm | `.../settings/general/smtp/components/SmtpConfigForm.tsx` | C | New |
| SettingsSidebar | `apps/web/src/components/settings/settings-sidebar.tsx` | C | Extend (add Allgemein section) |
Base path prefix for module components: `apps/web/src/app/(portal)/`
---
## Checker Sign-Off
- [ ] Dimension 1 Copywriting: PASS
- [ ] Dimension 2 Visuals: PASS
- [ ] Dimension 3 Color: PASS
- [ ] Dimension 4 Typography: PASS
- [ ] Dimension 5 Spacing: PASS
- [ ] Dimension 6 Registry Safety: PASS
**Approval:** pending
---
*Sources: CONTEXT.md (D-01 to D-21), RESEARCH.md (Architecture Patterns, Standard Stack), globals.css (all OKLCH tokens), settings-sidebar.tsx + calendar-source-form.tsx + admin-sidebar.tsx (component patterns), modules/domaincheck/page.tsx + settings/dashboard/page.tsx (heading typography patterns)*