Dieser Commit ist enthalten in:
Claude Project Manager
2025-09-20 21:31:04 +02:00
Commit 6b9b6d4f20
1821 geänderte Dateien mit 348527 neuen und 0 gelöschten Zeilen

Datei anzeigen

@ -0,0 +1,157 @@
import { useState } from 'react'
import { useThemeStore } from '../stores/themeStore'
import { SunIcon, MoonIcon } from './icons'
import { useAuthStore } from '../stores/authStore'
import { authApi } from '../services/api'
import { usePermissions } from '../hooks/usePermissions'
import WindowControls from './WindowControls'
export default function Header() {
const { isDarkMode, toggleTheme } = useThemeStore()
const { user, isAuthenticated, login, logout } = useAuthStore()
const { canAccessAdminPanel } = usePermissions()
const [showLogin, setShowLogin] = useState(false)
const [loginForm, setLoginForm] = useState({ username: '', password: '' })
const [loginError, setLoginError] = useState('')
const [loginLoading, setLoginLoading] = useState(false)
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault()
setLoginError('')
setLoginLoading(true)
try {
const response = await authApi.login(loginForm.username, loginForm.password)
login(response.user, response.token)
localStorage.setItem('token', response.token)
setShowLogin(false)
setLoginForm({ username: '', password: '' })
} catch (error: any) {
setLoginError(error.response?.data?.message || 'Login fehlgeschlagen')
} finally {
setLoginLoading(false)
}
}
const handleLogout = () => {
logout()
localStorage.removeItem('token')
}
return (
<header className="bg-tertiary border-b border-primary h-16 flex items-center justify-between px-container relative -webkit-app-region-drag">
<div className="flex items-center space-x-4">
<h2 className="text-title-dialog font-poppins font-semibold text-primary">
SkillMate
</h2>
{/* Login/Logout Section */}
<div className="flex items-center space-x-2 -webkit-app-region-no-drag">
{!isAuthenticated ? (
<div className="relative">
<button
onClick={() => setShowLogin(!showLogin)}
className="btn-secondary text-sm px-3 py-1 h-8"
>
Anmelden
</button>
{/* Login Dropdown */}
{showLogin && (
<div className="absolute top-full left-0 mt-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg p-4 w-64 z-50">
<form onSubmit={handleLogin} className="space-y-3">
<div>
<input
type="text"
placeholder="Benutzername"
value={loginForm.username}
onChange={(e) => setLoginForm(prev => ({ ...prev, username: e.target.value }))}
className="input-field w-full text-sm"
required
/>
</div>
<div>
<input
type="password"
placeholder="Passwort"
value={loginForm.password}
onChange={(e) => setLoginForm(prev => ({ ...prev, password: e.target.value }))}
className="input-field w-full text-sm"
required
/>
</div>
{loginError && (
<p className="text-red-600 text-xs">{loginError}</p>
)}
<div className="flex space-x-2">
<button
type="submit"
disabled={loginLoading}
className="btn-primary text-sm px-3 py-1 h-8 flex-1"
>
{loginLoading ? 'Wird angemeldet...' : 'Anmelden'}
</button>
<button
type="button"
onClick={() => setShowLogin(false)}
className="btn-secondary text-sm px-3 py-1 h-8"
>
Abbrechen
</button>
</div>
</form>
</div>
)}
</div>
) : (
<div className="flex items-center space-x-2">
<span className="text-secondary text-sm">{user?.username}</span>
<div className="w-8 h-8 rounded-full bg-primary-blue text-white flex items-center justify-center font-semibold">
{user?.username.charAt(0).toUpperCase()}
</div>
{canAccessAdminPanel() && (
<a
href="http://localhost:3002"
target="_blank"
rel="noopener noreferrer"
className="btn-secondary text-sm px-3 py-1 h-8"
>
Admin
</a>
)}
<button
onClick={handleLogout}
className="btn-secondary text-sm px-3 py-1 h-8"
>
Abmelden
</button>
</div>
)}
</div>
</div>
<div className="flex items-center space-x-4">
{/* Theme Toggle Slider - moved to the right */}
<div className="flex items-center space-x-2 -webkit-app-region-no-drag">
<SunIcon className="w-4 h-4 text-gray-500" />
<button
onClick={toggleTheme}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 ${
isDarkMode ? 'bg-blue-600' : 'bg-gray-200'
}`}
aria-label="Theme umschalten"
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition ${
isDarkMode ? 'translate-x-6' : 'translate-x-1'
}`}
/>
</button>
<MoonIcon className="w-4 h-4 text-gray-500" />
</div>
</div>
<WindowControls />
</header>
)
}