T07a: frontend core SPA — shell + auth + routing + i18n + UI library + a11y

- React 18 + Vite + TypeScript + Tailwind CSS setup
- AppShell with Sidebar (plugin menu) + TopBar (tenant switcher, search, notifications, user menu)
- Auth pages: Login, PasswordResetRequest, PasswordResetConfirm
- Protected routes with auth guard
- API client (axios with interceptors: session cookie, 401 redirect, 422 validation)
- TanStack Query hooks for auth, users, companies, contacts, notifications
- Zustand stores: authStore, uiStore
- i18n setup (de/en locales) with react-i18next
- UI component library: Button, Input, Select, Modal, Toast, Table, Card, Badge, Avatar, Pagination, EmptyState, Skeleton, ConfirmDialog
- Accessibility: ARIA labels, 44px touch targets, keyboard nav, reduced-motion, sr-only
- Design tokens from prototype as CSS custom properties
- 111 tests passing across 20 test files
- tsc --noEmit: 0 errors
- npm run build: success (471KB JS, 24KB CSS)
This commit is contained in:
leocrm-bot
2026-06-29 07:55:47 +02:00
parent f8193a6ab5
commit 22976abe92
66 changed files with 8598 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
{
"app": {
"name": "leocrm",
"tagline": "Mini-CRM für kleine Unternehmen"
},
"nav": {
"dashboard": "Dashboard",
"companies": "Firmen",
"contacts": "Kontakte",
"calendar": "Kalender",
"files": "Dateien",
"email": "E-Mail",
"users": "Benutzer",
"auditLog": "Audit-Log",
"settings": "Einstellungen"
},
"auth": {
"login": "Anmelden",
"logout": "Abmelden",
"email": "E-Mail-Adresse",
"password": "Passwort",
"loginButton": "Anmelden",
"loginFailed": "Anmeldung fehlgeschlagen. Bitte überprüfen Sie Ihre Eingaben.",
"forgotPassword": "Passwort vergessen?",
"resetPassword": "Passwort zurücksetzen",
"resetRequestTitle": "Passwort zurücksetzen",
"resetRequestDesc": "Geben Sie Ihre E-Mail-Adresse ein. Sie erhalten einen Link zum Zurücksetzen Ihres Passworts.",
"resetRequestButton": "Link senden",
"resetRequestSuccess": "Wenn die E-Mail-Adresse existiert, wurde ein Reset-Link gesendet.",
"resetConfirmTitle": "Neues Passwort festlegen",
"resetConfirmDesc": "Geben Sie Ihr neues Passwort ein.",
"newPassword": "Neues Passwort",
"confirmPassword": "Passwort bestätigen",
"resetConfirmButton": "Passwort ändern",
"resetConfirmSuccess": "Passwort erfolgreich geändert. Sie können sich jetzt anmelden.",
"resetConfirmFailed": "Passwort konnte nicht geändert werden. Der Link ist möglicherweise abgelaufen.",
"backToLogin": "Zurück zur Anmeldung",
"passwordMismatch": "Passwörter stimmen nicht überein.",
"passwordTooShort": "Passwort muss mindestens 8 Zeichen lang sein."
},
"common": {
"save": "Speichern",
"cancel": "Abbrechen",
"delete": "Löschen",
"edit": "Bearbeiten",
"create": "Erstellen",
"search": "Suchen",
"loading": "Wird geladen...",
"noResults": "Keine Ergebnisse gefunden",
"confirm": "Bestätigen",
"close": "Schließen",
"yes": "Ja",
"no": "Nein",
"actions": "Aktionen",
"all": "Alle",
"none": "Keine",
"error": "Fehler",
"success": "Erfolg",
"warning": "Warnung",
"info": "Information",
"required": "Pflichtfeld",
"optional": "Optional"
},
"settings": {
"title": "Einstellungen",
"language": "Sprache",
"theme": "Design",
"light": "Hell",
"dark": "Dunkel",
"system": "System",
"profile": "Profil",
"notifications": "Benachrichtigungen",
"security": "Sicherheit"
},
"topbar": {
"tenant": "Mandant",
"switchTenant": "Mandant wechseln",
"search": "Suchen...",
"notifications": "Benachrichtigungen",
"userMenu": "Benutzermenü",
"profile": "Profil",
"settings": "Einstellungen",
"logout": "Abmelden"
},
"toast": {
"success": "Erfolg",
"error": "Fehler",
"warning": "Warnung",
"info": "Information"
},
"table": {
"page": "Seite",
"of": "von",
"results": "Ergebnisse",
"perPage": "pro Seite",
"previous": "Zurück",
"next": "Weiter",
"sortAscending": "Aufsteigend sortieren",
"sortDescending": "Absteigend sortieren",
"sortedBy": "Sortiert nach",
"empty": "Keine Daten vorhanden"
},
"confirmDialog": {
"title": "Bestätigung erforderlich",
"confirm": "Bestätigen",
"cancel": "Abbrechen"
},
"pagination": {
"previous": "Zurück",
"next": "Weiter",
"page": "Seite {{page}}",
"first": "Erste Seite",
"last": "Letzte Seite"
},
"emptyState": {
"title": "Nichts gefunden",
"description": "Es sind noch keine Daten vorhanden."
},
"validation": {
"required": "Dieses Feld ist erforderlich.",
"email": "Bitte geben Sie eine gültige E-Mail-Adresse ein.",
"minLength": "Mindestens {{count}} Zeichen erforderlich.",
"maxLength": "Maximal {{count}} Zeichen erlaubt."
}
}