Files
opencrm/frontend/src/pages/Settings.tsx
T
duffyduckandClaude Opus 5 77aeb69aeb Rechtemodell Etappe 1: toter Katalog begradigt, Selbst-Erhoehung geschlossen
Vorarbeit fuer die Rollen-Oberflaeche. Eine Checkbox-Liste ueber einem
Katalog, der nicht stimmt, waere schlimmer als gar keine.

18 von 50 Rechten bewachten nichts: tariffs:*, cancellation-periods:*,
contract-durations:* und email-providers:* standen im Katalog und waren
anhakbar - die Routen prueften in Wahrheit providers:*, platforms:* und
settings:*. Die Routen gaten jetzt granular; eine rein additive Migration
vererbt jedes neue Recht an jede Rolle, die bisher das Sammelrecht hatte.
Nachgerechnet: keine Rolle hat etwas verloren.

Der Katalog stand dreifach und divergent - seed.ts, sync-roles.ts und,
abweichend, factoryReset. Die dritte Kopie kannte weder audit:* noch
gdpr:* und legte DSGVO, Audit-Betrieb und Gegenbuch gar nicht an: Nach
einem Werksreset konnte niemand mehr eine Auskunft nach Art. 15
ausfuehren, und aufgefallen waere es erst, wenn eine Frist laeuft. Jetzt
eine Quelle: src/config/rechte-katalog.ts.

Im selben Code: factoryReset setzte das Admin-Kennwort fest auf "admin"
mit bcrypt-Cost 10 - genau das, was seed.ts seit Pentest Runde 12
verbietet. Dieselbe Haertung war nur in einer der beiden Kopien
angekommen. Jetzt zufaellig, Cost 12, einmalig im Log.

Selbst-Erhoehung geschlossen: neues Recht roles:manage, getrennt von
users:*, dazu die Teilmengenregel in rechte.service.ts - niemand kann ein
Recht weitergeben, das er selbst nicht haelt. Sie greift auf allen vier
Wegen: Rolle anlegen, Rolle aendern, Rollen zuweisen und die drei Haken.
Der Haken-Weg war der wichtigste: Er brauchte nur users:create, ein
zweites Konto mit "Entwicklerzugriff" anlegen und sich damit anmelden -
die Developer-Rolle traegt alle Rechte. Geprueft wird der Zuwachs, nicht
der Endzustand, damit Entziehen erlaubt bleibt; und die Rechte des
Handelnden kommen frisch aus der Datenbank, nicht aus dem bis zu 15
Minuten alten Token.

Systemrollen gesperrt (Role.isSystem): Admin liess sich bisher umbenennen
oder leeren - und die versteckten Rollen haengen an ihrem Namen.
Role.isHidden ersetzt die im Frontend hartkodierte Namensliste, in der
"Gegenbuch" fehlte.

Rechteaenderung wirkt sofort: updateRole/deleteRole melden alle Traeger
ab. Werksreset und Backup-Restore verlangen zusaetzlich roles:manage -
beide loeschen alle Rollen und legen ein frisches admin@admin.com an.

Rollenpflege war der einzige Eingriff in die Rechtevergabe ohne
SecurityEvent, obwohl sie viele Konten auf einmal trifft. Jetzt
PERMISSION_CHANGED - ebenso fuer die bisher stummen Haken DSGVO und
Entwicklerzugriff.

Aussperr-Ausweg: prisma/rolle-zuweisen.ts. Umgeht die Regel bewusst - wer
Shell-Zugang hat, hat ohnehin die Datenbank; ein gestohlener Web-Zugang
hat ihn nicht. Schreibt ueber die Hash-Kette, nicht roh. Die Startwache
nennt den Befehl im Klartext.

Geprueft gegen Dev-DB und frische Wegwerf-DB: 7 Eskalationswege alle 403,
2 Gegenproben erlaubt; 6 Sperrtests auf Systemrollen alle 403, eigene
Rollen weiter aenderbar; Stammdaten-Lesen unveraendert 200;
Rechteaenderung sofort 401; Migration und sync-roles dreimal identisch;
db:seed auf bestehender DB ohne Wirkung auf die Rollenmatrix; frische
Installation mit allen 8 Systemrollen und ohne Hinweise.

Beim Deploy: Die Migration beendet alle Sitzungen einmalig. Noetig, weil
die Rechte im Token stehen - sonst liefen bis zu 15 Minuten 403er.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 13:49:30 +02:00

505 lines
26 KiB
TypeScript
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.
import { Link } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
import Card from '../components/ui/Card';
import { Settings as SettingsIcon, Code, Store, Clock, Calendar, UserCog, ChevronRight, Building2, FileType, Eye, Globe, Mail, Database, Shield, ShieldAlert, ShieldOff, FileText, FileEdit, PackageCheck, Hash, Landmark } from 'lucide-react';
export default function Settings() {
const { hasPermission, developerMode, setDeveloperMode } = useAuth();
const settingsCards = [
{
to: '/settings/users',
icon: UserCog,
title: 'Benutzer',
description: 'Verwalten Sie Benutzerkonten, Rollen und Berechtigungen.',
show: hasPermission('users:read'),
},
{
to: '/settings/platforms',
icon: Store,
title: 'Vertriebsplattformen',
description: 'Verwalten Sie die Plattformen, über die Verträge abgeschlossen werden.',
show: hasPermission('platforms:read'),
},
{
to: '/settings/cancellation-periods',
icon: Clock,
title: 'Kündigungsfristen',
description: 'Konfigurieren Sie die verfügbaren Kündigungsfristen für Verträge.',
show: hasPermission('cancellation-periods:read'),
},
{
to: '/settings/contract-durations',
icon: Calendar,
title: 'Vertragslaufzeiten',
description: 'Konfigurieren Sie die verfügbaren Laufzeiten für Verträge.',
show: hasPermission('contract-durations:read'),
},
{
to: '/settings/providers',
icon: Building2,
title: 'Anbieter & Tarife',
description: 'Verwalten Sie Anbieter und deren Tarife für Verträge.',
show: hasPermission('providers:read') || hasPermission('tariffs:read'),
},
{
to: '/settings/contract-categories',
icon: FileType,
title: 'Vertragstypen',
description: 'Konfigurieren Sie die verfügbaren Vertragstypen (Strom, Gas, Mobilfunk, etc.).',
show: hasPermission('contract-categories:read'),
},
{
to: '/settings/credit-note-number-range',
icon: Hash,
title: 'Nummernkreise (Gutschrift/Lieferschein)',
description: 'Präfix, Jahr und Zähler für Gutschriften und Lieferscheine (betragslose Sachwerte).',
show: hasPermission('settings:read'),
},
{
to: '/settings/company-profile',
icon: Building2,
title: 'Firmenstammdaten (Absender)',
description: 'Absenderdaten für Gutschrift-PDF und ZUGFeRD (Name, Anschrift, USt-IdNr, Bank).',
show: hasPermission('settings:read'),
},
];
return (
<div>
<div className="flex items-center gap-3 mb-6">
<SettingsIcon className="w-6 h-6" />
<h1 className="text-2xl font-bold">Einstellungen</h1>
</div>
{/* Stammdaten-Konfiguration */}
<div className="mb-8">
<h2 className="text-lg font-semibold mb-4 text-gray-700">Stammdaten</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{settingsCards
.filter((card) => card.show)
.map((card) => (
<Link
key={card.to}
to={card.to}
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<card.icon className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
{card.title}
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">{card.description}</p>
</div>
</div>
</Link>
))}
</div>
</div>
{/* System-Einstellungen */}
{/*
Outer-Check umfasst BEWUSST `settings:update`, `audit:read` und
`gdpr:admin`, sonst sieht die DSGVO-Rolle (nur audit/gdpr-Perms, kein
`settings:update`) die DSGVO-/Audit-Karten gar nicht. Jede Karte
innerhalb hat ihren eigenen feingranularen Check.
*/}
{(hasPermission('settings:update') || hasPermission('audit:read') || hasPermission('gdpr:admin')) && (
<div className="mb-8">
<h2 className="text-lg font-semibold mb-4 text-gray-700">System</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Link
to="/settings/portal"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<Globe className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Kundenportal
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Konfigurieren Sie das Kundenportal und Support-Anfragen.</p>
</div>
</div>
</Link>
<Link
to="/settings/deadlines"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<Clock className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Fristenschwellen
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Konfigurieren Sie die Farbkodierung für Vertragsfristen im Cockpit.</p>
</div>
</div>
</Link>
<Link
to="/settings/bank-data"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<Landmark className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Bankdaten (BLZ)
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Datenstand der Bankleitzahlen (BIC/Bank aus IBAN) + Auto-Update-Intervall.</p>
</div>
</div>
</Link>
<Link
to="/settings/pdf-templates"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<FileText className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Auftragsvorlagen
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">PDF-Vorlagen für Auftragsformulare hochladen und Felder zuordnen.</p>
</div>
</div>
</Link>
{hasPermission('email-providers:read') && (
<Link
to="/settings/email-providers"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<Mail className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Email-Provisionierung
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Konfigurieren Sie die automatische E-Mail-Erstellung für Kunden-E-Mail-Adressen.</p>
</div>
</div>
</Link>
)}
<Link
to="/settings/database-backup"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<Database className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Datenbank & Zurücksetzen
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Backups erstellen, wiederherstellen oder auf Werkseinstellungen zurücksetzen.</p>
</div>
</div>
</Link>
<Link
to="/settings/factory-defaults"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<PackageCheck className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Factory-Defaults
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Stammdaten-Kataloge (Anbieter, Tarife, PDF-Vorlagen) exportieren ohne Kundendaten.</p>
</div>
</div>
</Link>
{hasPermission('audit:read') && (
<Link
to="/settings/audit-logs"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<FileText className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Audit-Protokoll
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Protokollierte API-Zugriffe und Änderungen einsehen.</p>
{hasPermission('developer:access') && (
<p className="text-xs text-purple-500 mt-1 flex items-center gap-1">
<Code className="w-3 h-3" />
Sichtbar durch Entwicklerzugriff
</p>
)}
</div>
</div>
</Link>
)}
{hasPermission('gdpr:admin') && (
<Link
to="/settings/email-logs"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<Mail className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
E-Mail-Versandlog
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Alle gesendeten E-Mails mit SMTP-Details und Status einsehen.</p>
</div>
</div>
</Link>
)}
{hasPermission('settings:read') && (
<Link
to="/settings/monitoring"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-orange-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-orange-50 rounded-lg group-hover:bg-orange-100 transition-colors">
<ShieldAlert className="w-6 h-6 text-orange-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-orange-600 transition-colors flex items-center gap-2">
Sicherheits-Monitoring
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">
Login-Fehlversuche, IDOR-Abwehr, SSRF-Blocks etc. + Alert-E-Mail-Adresse konfigurieren.
</p>
</div>
</div>
</Link>
)}
{hasPermission('settings:read') && (
<Link
to="/settings/rate-limits"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-orange-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-orange-50 rounded-lg group-hover:bg-orange-100 transition-colors">
<ShieldOff className="w-6 h-6 text-orange-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-orange-600 transition-colors flex items-center gap-2">
Rate-Limit-Sperren
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">
Aktuell gesperrte IP-Adressen anzeigen und ggf. wieder freigeben.
</p>
</div>
</div>
</Link>
)}
{hasPermission('gdpr:admin') && (
<Link
to="/settings/gdpr"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<Shield className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
DSGVO-Dashboard
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Löschanfragen, Datenexporte und Einwilligungen verwalten.</p>
{hasPermission('developer:access') && (
<p className="text-xs text-purple-500 mt-1 flex items-center gap-1">
<Code className="w-3 h-3" />
Sichtbar durch Entwicklerzugriff
</p>
)}
</div>
</div>
</Link>
)}
{hasPermission('gdpr:admin') && (
<Link
to="/settings/privacy-policy"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<FileEdit className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Datenschutzerklärung
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Datenschutzerklärung bearbeiten mit Platzhaltern für Kundendaten.</p>
{hasPermission('developer:access') && (
<p className="text-xs text-purple-500 mt-1 flex items-center gap-1">
<Code className="w-3 h-3" />
Sichtbar durch Entwicklerzugriff
</p>
)}
</div>
</div>
</Link>
)}
{hasPermission('gdpr:admin') && (
<Link
to="/settings/authorization-template"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<FileEdit className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Vollmacht-Vorlage
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Vollmacht-Vorlage für Vertreterregelungen bearbeiten.</p>
</div>
</div>
</Link>
)}
{hasPermission('gdpr:admin') && (
<Link
to="/settings/imprint"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<FileEdit className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Impressum
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Impressum für das Kundenportal bearbeiten.</p>
</div>
</div>
</Link>
)}
{hasPermission('gdpr:admin') && (
<Link
to="/settings/website-privacy-policy"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<FileEdit className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Website-Datenschutzerklärung
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Datenschutzerklärung für das Kundenportal bearbeiten.</p>
</div>
</div>
</Link>
)}
</div>
</div>
)}
{/* Persönliche Einstellungen */}
<div className="mb-8">
<h2 className="text-lg font-semibold mb-4 text-gray-700">Persönlich</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<Link
to="/settings/view"
className="block p-4 bg-white border border-gray-200 rounded-lg shadow-sm hover:shadow-md hover:border-blue-300 transition-all group"
>
<div className="flex items-start gap-4">
<div className="p-2 bg-blue-50 rounded-lg group-hover:bg-blue-100 transition-colors">
<Eye className="w-6 h-6 text-blue-600" />
</div>
<div className="flex-1">
<h3 className="font-semibold text-gray-900 group-hover:text-blue-600 transition-colors flex items-center gap-2">
Ansicht
<ChevronRight className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity" />
</h3>
<p className="text-sm text-gray-500 mt-1">Passen Sie die Darstellung der Anwendung an.</p>
</div>
</div>
</Link>
</div>
</div>
{hasPermission('developer:access') && (
<Card title="Entwickleroptionen" className="mb-6">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Code className="w-5 h-5 text-gray-500" />
<div>
<p className="font-medium">Entwicklermodus</p>
<p className="text-sm text-gray-500">
Aktiviert erweiterte Funktionen wie direkten Datenbankzugriff
</p>
</div>
</div>
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={developerMode}
onChange={(e) => setDeveloperMode(e.target.checked)}
className="sr-only peer"
/>
<div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
</label>
</div>
{developerMode && (
<div className="mt-4 p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
<p className="text-sm text-yellow-800">
<strong>Warnung:</strong> Der Entwicklermodus ermöglicht direkten Zugriff auf die Datenbank.
Unsachgemäße Änderungen können zu Datenverlust oder Inkonsistenzen führen.
</p>
</div>
)}
</Card>
)}
<Card title="Über">
<dl className="space-y-3">
<div>
<dt className="text-sm text-gray-500">Version</dt>
<dd>1.0.0</dd>
</div>
<div>
<dt className="text-sm text-gray-500">System</dt>
<dd>OpenCRM</dd>
</div>
</dl>
</Card>
</div>
);
}