adding chat features, admin features and bug fixes

This commit is contained in:
Matthias Hochmeister
2026-03-12 08:16:34 +01:00
parent 7b14e3d5ba
commit 31f1414e06
43 changed files with 2610 additions and 16 deletions

View File

@@ -10,6 +10,7 @@ import {
ListItemIcon,
Divider,
Box,
Tooltip,
} from '@mui/material';
import {
LocalFireDepartment,
@@ -17,10 +18,15 @@ import {
Settings,
Logout,
Menu as MenuIcon,
Launch,
Chat,
} from '@mui/icons-material';
import { useQuery } from '@tanstack/react-query';
import { useAuth } from '../../contexts/AuthContext';
import { useNavigate } from 'react-router-dom';
import NotificationBell from './NotificationBell';
import { configApi } from '../../services/config';
import { useLayout } from '../../contexts/LayoutContext';
interface HeaderProps {
onMenuClick: () => void;
@@ -29,7 +35,16 @@ interface HeaderProps {
function Header({ onMenuClick }: HeaderProps) {
const { user, logout } = useAuth();
const navigate = useNavigate();
const { toggleChatPanel } = useLayout();
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const [toolsAnchorEl, setToolsAnchorEl] = useState<null | HTMLElement>(null);
const { data: externalLinks } = useQuery({
queryKey: ['external-links'],
queryFn: () => configApi.getExternalLinks(),
staleTime: 10 * 60 * 1000,
enabled: !!user,
});
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
@@ -39,6 +54,14 @@ function Header({ onMenuClick }: HeaderProps) {
setAnchorEl(null);
};
const handleToolsOpen = (event: React.MouseEvent<HTMLElement>) => {
setToolsAnchorEl(event.currentTarget);
};
const handleToolsClose = () => {
setToolsAnchorEl(null);
};
const handleProfile = () => {
handleMenuClose();
navigate('/profile');
@@ -54,6 +77,11 @@ function Header({ onMenuClick }: HeaderProps) {
logout();
};
const handleOpenExternal = (url: string) => {
handleToolsClose();
window.open(url, '_blank', 'noopener,noreferrer');
};
// Get initials for avatar
const getInitials = () => {
if (!user) return '?';
@@ -61,6 +89,16 @@ function Header({ onMenuClick }: HeaderProps) {
return initials || user.name?.[0] || '?';
};
const linkEntries = externalLinks
? Object.entries(externalLinks).filter(([, url]) => !!url)
: [];
const linkLabels: Record<string, string> = {
nextcloud: 'Nextcloud',
bookstack: 'BookStack',
vikunja: 'Vikunja',
};
return (
<AppBar
position="fixed"
@@ -71,7 +109,7 @@ function Header({ onMenuClick }: HeaderProps) {
<Toolbar>
<IconButton
color="inherit"
aria-label="Men� �ffnen"
aria-label="Menü öffnen"
edge="start"
onClick={onMenuClick}
sx={{ mr: 2, display: { sm: 'none' } }}
@@ -86,6 +124,63 @@ function Header({ onMenuClick }: HeaderProps) {
{user && (
<>
{linkEntries.length > 0 && (
<>
<Tooltip title="Externe Tools">
<IconButton
color="inherit"
onClick={handleToolsOpen}
size="small"
aria-label="Externe Tools"
aria-controls="tools-menu"
aria-haspopup="true"
>
<Launch />
</IconButton>
</Tooltip>
<Menu
id="tools-menu"
anchorEl={toolsAnchorEl}
open={Boolean(toolsAnchorEl)}
onClose={handleToolsClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
PaperProps={{
elevation: 3,
sx: { minWidth: 180, mt: 1 },
}}
>
{linkEntries.map(([key, url]) => (
<MenuItem key={key} onClick={() => handleOpenExternal(url)}>
<ListItemIcon>
<Launch fontSize="small" />
</ListItemIcon>
{linkLabels[key] || key}
</MenuItem>
))}
</Menu>
</>
)}
<Tooltip title="Chat">
<IconButton
color="inherit"
onClick={toggleChatPanel}
size="small"
aria-label="Chat öffnen"
sx={{ ml: 0.5 }}
>
<Chat />
</IconButton>
</Tooltip>
<NotificationBell />
<IconButton