mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-17 19:33:41 +00:00
feat: add dark mode toggle in header and login page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
9d79ba35bc
commit
1edf5835be
3 changed files with 50 additions and 2 deletions
|
|
@ -18,7 +18,8 @@ import {
|
||||||
} from '@/components/ui/popover'
|
} from '@/components/ui/popover'
|
||||||
import { ScrollArea } from '@/components/ui/scroll-area'
|
import { ScrollArea } from '@/components/ui/scroll-area'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import { Bell, CheckCheck, LogOut, Menu } from 'lucide-react'
|
import { Bell, CheckCheck, LogOut, Menu, Moon, Sun } from 'lucide-react'
|
||||||
|
import { useTheme } from '@/hooks/useTheme'
|
||||||
|
|
||||||
interface HeaderProps {
|
interface HeaderProps {
|
||||||
onToggleSidebar: () => void
|
onToggleSidebar: () => void
|
||||||
|
|
@ -27,6 +28,7 @@ interface HeaderProps {
|
||||||
export function Header({ onToggleSidebar }: HeaderProps) {
|
export function Header({ onToggleSidebar }: HeaderProps) {
|
||||||
const { user, logout } = useAuth()
|
const { user, logout } = useAuth()
|
||||||
const { notifications, unreadCount, markAsRead, markAllAsRead } = useNotifications()
|
const { notifications, unreadCount, markAsRead, markAllAsRead } = useNotifications()
|
||||||
|
const { theme, toggleTheme } = useTheme()
|
||||||
|
|
||||||
const initials = user?.username
|
const initials = user?.username
|
||||||
? user.username
|
? user.username
|
||||||
|
|
@ -54,6 +56,12 @@ export function Header({ onToggleSidebar }: HeaderProps) {
|
||||||
|
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
|
|
||||||
|
{/* Dark mode toggle */}
|
||||||
|
<Button variant="ghost" size="icon" onClick={toggleTheme}>
|
||||||
|
{theme === 'dark' ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />}
|
||||||
|
<span className="sr-only">Design umschalten</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
{/* Notification bell with dropdown */}
|
{/* Notification bell with dropdown */}
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
|
|
|
||||||
29
frontend/src/hooks/useTheme.ts
Normal file
29
frontend/src/hooks/useTheme.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
type Theme = 'light' | 'dark'
|
||||||
|
|
||||||
|
const STORAGE_KEY = 'dak-theme'
|
||||||
|
|
||||||
|
export function useTheme() {
|
||||||
|
const [theme, setThemeState] = useState<Theme>(() => {
|
||||||
|
const stored = localStorage.getItem(STORAGE_KEY)
|
||||||
|
if (stored === 'dark' || stored === 'light') return stored
|
||||||
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const root = document.documentElement
|
||||||
|
if (theme === 'dark') {
|
||||||
|
root.classList.add('dark')
|
||||||
|
} else {
|
||||||
|
root.classList.remove('dark')
|
||||||
|
}
|
||||||
|
localStorage.setItem(STORAGE_KEY, theme)
|
||||||
|
}, [theme])
|
||||||
|
|
||||||
|
const toggleTheme = useCallback(() => {
|
||||||
|
setThemeState((prev) => (prev === 'dark' ? 'light' : 'dark'))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return { theme, toggleTheme }
|
||||||
|
}
|
||||||
|
|
@ -9,7 +9,8 @@ import { Input } from '@/components/ui/input'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { Label } from '@/components/ui/label'
|
import { Label } from '@/components/ui/label'
|
||||||
import { Alert, AlertDescription } from '@/components/ui/alert'
|
import { Alert, AlertDescription } from '@/components/ui/alert'
|
||||||
import { AlertCircle, Loader2 } from 'lucide-react'
|
import { AlertCircle, Loader2, Moon, Sun } from 'lucide-react'
|
||||||
|
import { useTheme } from '@/hooks/useTheme'
|
||||||
|
|
||||||
const loginSchema = z.object({
|
const loginSchema = z.object({
|
||||||
email: z.string().email('Bitte geben Sie eine gueltige E-Mail-Adresse ein'),
|
email: z.string().email('Bitte geben Sie eine gueltige E-Mail-Adresse ein'),
|
||||||
|
|
@ -21,6 +22,7 @@ type LoginFormValues = z.infer<typeof loginSchema>
|
||||||
|
|
||||||
export function LoginPage() {
|
export function LoginPage() {
|
||||||
const { login } = useAuth()
|
const { login } = useAuth()
|
||||||
|
const { theme, toggleTheme } = useTheme()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
const [showMfa, setShowMfa] = useState(false)
|
const [showMfa, setShowMfa] = useState(false)
|
||||||
|
|
@ -70,6 +72,15 @@ export function LoginPage() {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-background px-4">
|
<div className="flex min-h-screen items-center justify-center bg-background px-4">
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={toggleTheme}
|
||||||
|
className="absolute top-4 right-4"
|
||||||
|
>
|
||||||
|
{theme === 'dark' ? <Sun className="h-5 w-5" /> : <Moon className="h-5 w-5" />}
|
||||||
|
<span className="sr-only">Design umschalten</span>
|
||||||
|
</Button>
|
||||||
<Card className="w-full max-w-md">
|
<Card className="w-full max-w-md">
|
||||||
<CardHeader className="text-center">
|
<CardHeader className="text-center">
|
||||||
<CardTitle className="text-2xl">DAK Portal</CardTitle>
|
<CardTitle className="text-2xl">DAK Portal</CardTitle>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue