import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Palette, Type, Moon, Sun, RotateCcw, Check } from 'lucide-react'; import { useSystemSettings, useUpdateSystemSettings } from '@/api/settings'; import { useThemeStore } from '@/store/themeStore'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Select } from '@/components/ui/Select'; import { Badge } from '@/components/ui/Badge'; import { Skeleton } from '@/components/ui/Skeleton'; import { useToast } from '@/components/ui/Toast'; const FONT_OPTIONS = [ { value: 'Inter', label: 'Inter (Standard)' }, { value: 'Roboto', label: 'Roboto' }, { value: 'Open Sans', label: 'Open Sans' }, { value: 'Lato', label: 'Lato' }, { value: 'Montserrat', label: 'Montserrat' }, { value: 'Source Sans Pro', label: 'Source Sans Pro' }, { value: 'Nunito', label: 'Nunito' }, { value: 'system-ui', label: 'System UI' }, ]; const RADIUS_OPTIONS = [ { value: '0.25rem', label: 'Sharp (0.25rem)' }, { value: '0.375rem', label: 'Small (0.375rem)' }, { value: '0.5rem', label: 'Medium (0.5rem) — Standard' }, { value: '0.75rem', label: 'Large (0.75rem)' }, { value: '1rem', label: 'Extra Large (1rem)' }, { value: '1.5rem', label: 'Round (1.5rem)' }, ]; const PRESET_THEMES = [ { name: 'Classic Blue', primary: '#2563eb', accent: '#d946ef' }, { name: 'Ocean', primary: '#0ea5e9', accent: '#6366f1' }, { name: 'Forest', primary: '#16a34a', accent: '#84cc16' }, { name: 'Sunset', primary: '#ea580c', accent: '#f59e0b' }, { name: 'Purple', primary: '#7c3aed', accent: '#ec4899' }, { name: 'Slate', primary: '#475569', accent: '#64748b' }, ]; export function SettingsThemePage() { const { t } = useTranslation(); const toast = useToast(); const { data: settings, isLoading } = useSystemSettings(); const updateMutation = useUpdateSystemSettings(); const themeStore = useThemeStore(); const [primaryColor, setPrimaryColor] = useState('#2563eb'); const [accentColor, setAccentColor] = useState('#d946ef'); const [fontFamily, setFontFamily] = useState('Inter'); const [borderRadius, setBorderRadius] = useState('0.5rem'); const [darkMode, setDarkMode] = useState(false); const [hasChanges, setHasChanges] = useState(false); // Load theme from system settings on mount useEffect(() => { if (settings) { const pc = settings.theme_primary_color || '#2563eb'; const ac = settings.theme_accent_color || '#d946ef'; const ff = settings.theme_font_family || 'Inter'; const br = settings.theme_border_radius || '0.5rem'; setPrimaryColor(pc); setAccentColor(ac); setFontFamily(ff); setBorderRadius(br); // Apply to store for live preview themeStore.setTheme({ primaryColor: pc, accentColor: ac, fontFamily: ff, borderRadius: br, }); } }, [settings]); // Load dark mode from localStorage on mount useEffect(() => { themeStore.loadFromStorage(); setDarkMode(themeStore.darkMode); }, []); // Track changes useEffect(() => { if (!settings) return; const changed = primaryColor !== (settings.theme_primary_color || '#2563eb') || accentColor !== (settings.theme_accent_color || '#d946ef') || fontFamily !== (settings.theme_font_family || 'Inter') || borderRadius !== (settings.theme_border_radius || '0.5rem'); setHasChanges(changed); }, [primaryColor, accentColor, fontFamily, borderRadius, settings]); // Live preview: apply to theme store immediately const handlePrimaryChange = (val: string) => { setPrimaryColor(val); themeStore.setTheme({ primaryColor: val }); }; const handleAccentChange = (val: string) => { setAccentColor(val); themeStore.setTheme({ accentColor: val }); }; const handleFontChange = (val: string) => { setFontFamily(val); themeStore.setTheme({ fontFamily: val }); }; const handleRadiusChange = (val: string) => { setBorderRadius(val); themeStore.setTheme({ borderRadius: val }); }; const handleDarkModeToggle = () => { const newMode = !darkMode; setDarkMode(newMode); themeStore.toggleDarkMode(); }; const handlePreset = (preset: typeof PRESET_THEMES[0]) => { setPrimaryColor(preset.primary); setAccentColor(preset.accent); themeStore.setTheme({ primaryColor: preset.primary, accentColor: preset.accent, }); }; const handleSave = async () => { try { await updateMutation.mutateAsync({ ...settings, theme_primary_color: primaryColor, theme_accent_color: accentColor, theme_font_family: fontFamily, theme_border_radius: borderRadius, }); toast.success(t('settings.themeSaved', 'Theme gespeichert')); setHasChanges(false); } catch { toast.error(t('settings.themeSaveError', 'Theme konnte nicht gespeichert werden')); } }; const handleReset = () => { handlePreset(PRESET_THEMES[0]); setFontFamily('Inter'); setBorderRadius('0.5rem'); themeStore.setTheme({ primaryColor: '#2563eb', accentColor: '#d946ef', fontFamily: 'Inter', borderRadius: '0.5rem', }); }; if (isLoading) { return (
); } return (

{t('settings.themeTitle', 'Theme-Anpassung')}

{t('settings.themeDescription', 'Passen Sie Farben, Schriftart und Erscheinungsbild an.')}

{hasChanges && ( {t('settings.unsavedChanges', 'Ungespeicherte Änderungen')} )}
{/* Preset Themes */}
{PRESET_THEMES.map((preset) => ( ))}
{/* Color Customization */}
handlePrimaryChange(e.target.value)} className="w-12 h-12 rounded-md border border-secondary-200 cursor-pointer" aria-label={t('settings.primaryColor', 'Hauptfarbe')} /> handlePrimaryChange(e.target.value)} className="flex-1" placeholder="#2563eb" />
handleAccentChange(e.target.value)} className="w-12 h-12 rounded-md border border-secondary-200 cursor-pointer" aria-label={t('settings.accentColor', 'Akzentfarbe')} /> handleAccentChange(e.target.value)} className="flex-1" placeholder="#d946ef" />
{/* Typography & Layout */}
handleRadiusChange(e.target.value)} options={RADIUS_OPTIONS} />
{/* Dark Mode */}
{darkMode ? (
{/* Live Preview */}
{/* Buttons */}
{/* Badges */}
Primary Success Warning Danger Info
{/* Input preview */}
{/* Card preview */}

Dies ist eine Beispiel-Karte mit dem aktuellen Theme.

{/* Actions */}
); }