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>
27 KiB
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>withtext-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-primarywith white circle - Off:
bg-mutedwith 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-smat 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-foregrounde.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-smbut withoutmax-w-mdconstraint) + 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-smgreen — "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)