173 Zeilen
6.7 KiB
TypeScript
173 Zeilen
6.7 KiB
TypeScript
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'
|
|
import OrganizationChart from './OrganizationChart'
|
|
import { Building2 } from 'lucide-react'
|
|
|
|
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 [showOrganigramm, setShowOrganigramm] = 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={() => setShowOrganigramm(true)}
|
|
className="btn-secondary text-sm px-3 py-1 h-8 flex items-center gap-1"
|
|
title="Organigramm anzeigen"
|
|
>
|
|
<Building2 className="w-4 h-4" />
|
|
<span className="hidden sm:inline">Organigramm</span>
|
|
</button>
|
|
<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 />
|
|
|
|
{/* Organization Chart Modal */}
|
|
{showOrganigramm && (
|
|
<OrganizationChart onClose={() => setShowOrganigramm(false)} />
|
|
)}
|
|
</header>
|
|
)
|
|
} |