push newest version

This commit is contained in:
2025-11-20 17:59:34 +01:00
parent c0e2df2430
commit 97ccd7bfbf
21 changed files with 3978 additions and 65 deletions

View File

@@ -1,6 +1,8 @@
import { useState, useEffect } from 'react'
import { useAuth } from '../contexts/AuthContext'
const ProvidersSection = () => {
const { authFetch } = useAuth()
const [providers, setProviders] = useState([])
const [loading, setLoading] = useState(true)
const [showConfigModal, setShowConfigModal] = useState(false)
@@ -11,11 +13,11 @@ const ProvidersSection = () => {
useEffect(() => {
fetchProviders()
}, [])
}, [authFetch])
const fetchProviders = async () => {
try {
const response = await fetch('/api/providers')
const response = await authFetch('/api/providers')
if (response.ok) {
const data = await response.json()
// Definiere feste Reihenfolge der Provider
@@ -35,7 +37,7 @@ const ProvidersSection = () => {
const handleToggleProvider = async (providerId, currentEnabled) => {
try {
const response = await fetch(`/api/providers/${providerId}/enabled`, {
const response = await authFetch(`/api/providers/${providerId}/enabled`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
@@ -60,7 +62,7 @@ const ProvidersSection = () => {
// Lade aktuelle Konfiguration
try {
const response = await fetch(`/api/providers/${provider.id}`)
const response = await authFetch(`/api/providers/${provider.id}`)
if (response.ok) {
const data = await response.json()
// Initialisiere Config-Werte
@@ -109,7 +111,7 @@ const ProvidersSection = () => {
setTestResult(null)
try {
const response = await fetch(`/api/providers/${selectedProvider.id}/test`, {
const response = await authFetch(`/api/providers/${selectedProvider.id}/test`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@@ -134,7 +136,7 @@ const ProvidersSection = () => {
if (!selectedProvider) return
try {
const response = await fetch(`/api/providers/${selectedProvider.id}/config`, {
const response = await authFetch(`/api/providers/${selectedProvider.id}/config`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',

View File

@@ -1,14 +1,32 @@
import { Link, useLocation } from 'react-router-dom'
import { Link, useLocation, useNavigate } from 'react-router-dom'
import { useAuth } from '../contexts/AuthContext'
import { useState, useEffect } from 'react'
const Sidebar = ({ isOpen, setIsOpen }) => {
const location = useLocation()
const navigate = useNavigate()
const { user, logout } = useAuth()
const [expandedMenus, setExpandedMenus] = useState({})
const menuItems = [
{ path: '/', label: 'Home', icon: '🏠' },
{ path: '/spaces', label: 'Spaces', icon: '📁' },
{ path: '/audit-logs', label: 'Audit Log', icon: '📋' },
{ path: '/impressum', label: 'Impressum', icon: '' },
]
// Settings mit Unterpunkten
const settingsMenu = {
label: 'Settings',
icon: '⚙️',
path: '/settings',
subItems: [
{ path: '/settings/users', label: 'User', icon: '👥' },
]
}
const profileItem = { path: '/profile', label: 'Profil', icon: '👤' }
const isActive = (path) => {
if (path === '/') {
return location.pathname === '/'
@@ -16,6 +34,27 @@ const Sidebar = ({ isOpen, setIsOpen }) => {
return location.pathname.startsWith(path)
}
const toggleMenu = (menuPath) => {
setExpandedMenus(prev => ({
...prev,
[menuPath]: !prev[menuPath]
}))
}
const isMenuExpanded = (menuPath) => {
return expandedMenus[menuPath] || false
}
// Automatisch Settings-Menü expandieren, wenn auf einer Settings-Seite
useEffect(() => {
if (location.pathname.startsWith('/settings')) {
setExpandedMenus(prev => ({
...prev,
'/settings': true
}))
}
}, [location.pathname])
return (
<>
{/* Overlay for mobile */}
@@ -63,8 +102,8 @@ const Sidebar = ({ isOpen, setIsOpen }) => {
</div>
</button>
</div>
<nav className="px-2 py-4 overflow-hidden">
<ul className="space-y-2">
<nav className="px-2 py-4 overflow-hidden flex flex-col h-[calc(100%-4rem)]">
<ul className="space-y-2 flex-1">
{menuItems.map((item) => (
<li key={item.path}>
<Link
@@ -87,7 +126,104 @@ const Sidebar = ({ isOpen, setIsOpen }) => {
</Link>
</li>
))}
{/* Settings Menu mit Unterpunkten */}
<li>
<button
onClick={() => isOpen && toggleMenu(settingsMenu.path)}
className={`w-full flex items-center px-3 py-3 rounded-lg transition-all duration-200 ${
isActive(settingsMenu.path)
? 'bg-slate-700 text-white font-semibold shadow-md'
: 'text-slate-300 hover:bg-slate-700/50 hover:text-white'
}`}
title={!isOpen ? settingsMenu.label : ''}
>
<span className={`text-xl flex-shrink-0 ${isOpen ? 'mr-3' : 'mx-auto'}`}>
{settingsMenu.icon}
</span>
{isOpen && (
<>
<span className="whitespace-nowrap overflow-hidden">
{settingsMenu.label}
</span>
<svg
className={`w-4 h-4 ml-auto flex-shrink-0 transition-transform duration-200 ${
isMenuExpanded(settingsMenu.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>
{isOpen && isMenuExpanded(settingsMenu.path) && settingsMenu.subItems && (
<ul className="ml-4 mt-1 space-y-1">
{settingsMenu.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>
</ul>
{/* Profil-Eintrag und Logout am unteren Ende */}
<div className="mt-auto pt-2 border-t border-slate-700/50 space-y-2">
<Link
to={profileItem.path}
className={`flex items-center px-3 py-3 rounded-lg transition-all duration-200 ${
isActive(profileItem.path)
? 'bg-slate-700 text-white font-semibold shadow-md'
: 'text-slate-300 hover:bg-slate-700/50 hover:text-white'
}`}
title={!isOpen ? (user?.username || profileItem.label) : ''}
>
<span className={`text-xl flex-shrink-0 ${isOpen ? 'mr-3' : 'mx-auto'}`}>
{profileItem.icon}
</span>
{isOpen && (
<span className="whitespace-nowrap overflow-hidden">
{user?.username || profileItem.label}
</span>
)}
</Link>
<button
onClick={() => {
logout()
navigate('/login')
}}
className={`w-full flex items-center px-3 py-3 rounded-lg transition-all duration-200 text-slate-300 hover:bg-red-600/20 hover:text-red-400 ${
isOpen ? '' : 'justify-center'
}`}
title={!isOpen ? 'Abmelden' : ''}
>
<span className={`text-xl flex-shrink-0 ${isOpen ? 'mr-3' : ''}`}>
🚪
</span>
{isOpen && (
<span className="whitespace-nowrap overflow-hidden">
Abmelden
</span>
)}
</button>
</div>
</nav>
</aside>
</>