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

27 KiB
Raw Blame History

phase, slug, status, shadcn_initialized, preset, created
phase slug status shadcn_initialized preset created
7 dkv-fleet-module draft false none 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:

// 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):

{
  "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):

{
  "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)