implemented LE and ACME and fixed some bugs

This commit is contained in:
2025-11-27 04:20:09 +01:00
parent ec1e0da9d5
commit 145dfd3d7c
36 changed files with 10583 additions and 1107 deletions

View File

@@ -13,14 +13,24 @@ const Sidebar = ({ isOpen, setIsOpen }) => {
// Prüfe ob User Berechtigungsgruppen hat
const hasGroups = isAdmin || hasFullAccess || (accessibleSpaces && accessibleSpaces.length > 0)
// Menüpunkte - Home ist immer sichtbar, andere nur mit Gruppen
// Menüpunkte - andere nur mit Gruppen
const menuItems = [
{ path: '/', label: 'Home', icon: '🏠', alwaysVisible: true },
{ path: '/spaces', label: 'Spaces', icon: '📁', requiresGroups: true },
{ path: '/audit-logs', label: 'Audit Log', icon: '📋', requiresGroups: true },
{ path: '/impressum', label: 'Impressum', icon: '', requiresGroups: true },
].filter(item => item.alwaysVisible || !item.requiresGroups || hasGroups)
// Home mit Unterpunkten
const homeMenu = {
label: 'Home',
icon: '🏠',
path: '/',
alwaysVisible: true,
subItems: [
{ path: '/queue', label: 'Queue', icon: '⏰', requiresGroups: true },
].filter(item => !item.requiresGroups || hasGroups)
}
// Settings mit Unterpunkten
const settingsMenu = {
label: 'Settings',
@@ -53,7 +63,7 @@ const Sidebar = ({ isOpen, setIsOpen }) => {
return expandedMenus[menuPath] || false
}
// Automatisch Settings-Menü expandieren, wenn auf einer Settings-Seite
// Automatisch Menüs expandieren, wenn auf einer entsprechenden Seite
useEffect(() => {
if (location.pathname.startsWith('/settings')) {
setExpandedMenus(prev => ({
@@ -61,6 +71,12 @@ const Sidebar = ({ isOpen, setIsOpen }) => {
'/settings': true
}))
}
if (location.pathname === '/queue' || location.pathname === '/') {
setExpandedMenus(prev => ({
...prev,
'/': true
}))
}
}, [location.pathname])
return (
@@ -112,6 +128,82 @@ const Sidebar = ({ isOpen, setIsOpen }) => {
</div>
<nav className="px-2 py-4 overflow-hidden flex flex-col h-[calc(100%-4rem)]">
<ul className="space-y-2 flex-1">
{/* Home Menu mit Unterpunkten */}
<li>
<div className={`flex items-center rounded-lg transition-all duration-200 ${
(isActive(homeMenu.path) || location.pathname === '/queue')
? 'bg-slate-700 shadow-md'
: ''
}`}>
<Link
to={homeMenu.path}
className={`flex-1 flex items-center px-3 py-3 rounded-lg transition-all duration-200 ${
(isActive(homeMenu.path) || location.pathname === '/queue')
? 'text-white font-semibold'
: 'text-slate-300 hover:bg-slate-700/50 hover:text-white'
}`}
title={!isOpen ? homeMenu.label : ''}
>
<span className={`text-xl flex-shrink-0 ${isOpen ? 'mr-3' : 'mx-auto'}`}>
{homeMenu.icon}
</span>
{isOpen && (
<span className="whitespace-nowrap overflow-hidden">
{homeMenu.label}
</span>
)}
</Link>
{isOpen && homeMenu.subItems && homeMenu.subItems.length > 0 && (
<button
onClick={(e) => {
e.stopPropagation()
toggleMenu(homeMenu.path)
}}
className={`p-2 rounded-lg transition-colors ${
(isActive(homeMenu.path) || location.pathname === '/queue')
? 'text-white hover:bg-slate-600'
: 'text-slate-400 hover:text-slate-300 hover:bg-slate-700/50'
}`}
title="Menü erweitern"
>
<svg
className={`w-4 h-4 transition-transform duration-200 ${
isMenuExpanded(homeMenu.path) ? 'rotate-90' : ''
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</button>
)}
</div>
{isOpen && isMenuExpanded(homeMenu.path) && homeMenu.subItems && homeMenu.subItems.length > 0 && (
<ul className="ml-4 mt-1 space-y-1">
{homeMenu.subItems.map((subItem) => (
<li key={subItem.path}>
<Link
to={subItem.path}
className={`flex items-center px-3 py-2 rounded-lg transition-all duration-200 ${
isActive(subItem.path)
? 'bg-slate-600 text-white font-semibold'
: 'text-slate-400 hover:bg-slate-700/50 hover:text-slate-200'
}`}
>
<span className="text-lg flex-shrink-0 mr-2">
{subItem.icon}
</span>
<span className="whitespace-nowrap overflow-hidden">
{subItem.label}
</span>
</Link>
</li>
))}
</ul>
)}
</li>
{menuItems.map((item) => (
<li key={item.path}>
<Link

View File

@@ -0,0 +1,75 @@
import { useEffect } from 'react'
const Toast = ({ message, type = 'error', onClose, duration = 4000 }) => {
useEffect(() => {
if (duration > 0) {
const timer = setTimeout(() => {
onClose()
}, duration)
return () => clearTimeout(timer)
}
}, [duration, onClose])
const typeStyles = {
error: 'bg-red-500/90 border-red-400/50 text-white',
warning: 'bg-amber-500/90 border-amber-400/50 text-white',
info: 'bg-blue-500/90 border-blue-400/50 text-white',
success: 'bg-emerald-500/90 border-emerald-400/50 text-white'
}
const iconStyles = {
error: 'text-red-200',
warning: 'text-amber-200',
info: 'text-blue-200',
success: 'text-emerald-200'
}
const icons = {
error: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
),
warning: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
),
info: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
),
success: (
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
)
}
return (
<div
className={`flex items-start gap-3 px-4 py-3 rounded-lg border-2 shadow-2xl backdrop-blur-sm min-w-[300px] max-w-[500px] animate-slide-in-right transition-all duration-300 ${typeStyles[type]}`}
role="alert"
>
<div className={`flex-shrink-0 ${iconStyles[type]}`}>
{icons[type]}
</div>
<div className="flex-1 text-sm font-medium">
{message}
</div>
<button
onClick={onClose}
className="flex-shrink-0 text-white/80 hover:text-white transition-colors"
aria-label="Schließen"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
)
}
export default Toast