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

@@ -0,0 +1,67 @@
import { Card, CardContent, Typography, Box, Chip } from '@mui/material';
import { useQuery } from '@tanstack/react-query';
import { useNavigate } from 'react-router-dom';
import { MonitorHeartOutlined } from '@mui/icons-material';
import { adminApi } from '../../services/admin';
import { useCountUp } from '../../hooks/useCountUp';
import { useAuth } from '../../contexts/AuthContext';
function AdminStatusWidget() {
const { user } = useAuth();
const navigate = useNavigate();
const isAdmin = user?.groups?.includes('dashboard_admin') ?? false;
const { data } = useQuery({
queryKey: ['admin-status-summary'],
queryFn: () => adminApi.getStatusSummary(),
refetchInterval: 30_000,
enabled: isAdmin,
});
const up = useCountUp(data?.up ?? 0);
const total = useCountUp(data?.total ?? 0);
if (!isAdmin) return null;
const allUp = data && data.up === data.total;
const majorityDown = data && data.total > 0 && data.up < data.total / 2;
const color = allUp ? 'success' : majorityDown ? 'error' : 'warning';
return (
<Card
sx={{ cursor: 'pointer', '&:hover': { boxShadow: 4 } }}
onClick={() => navigate('/admin')}
>
<CardContent>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}>
<MonitorHeartOutlined color={color} />
<Typography variant="h6" component="div">
Service Status
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'baseline', gap: 0.5, mb: 1 }}>
<Typography variant="h3" component="span" sx={{ fontWeight: 700 }}>
{up}
</Typography>
<Typography variant="h5" component="span" color="text.secondary">
/ {total}
</Typography>
<Typography variant="body2" color="text.secondary" sx={{ ml: 0.5 }}>
Services online
</Typography>
</Box>
<Chip
label={allUp ? 'Alle aktiv' : majorityDown ? 'Kritisch' : 'Teilweise gestört'}
color={color}
size="small"
variant="outlined"
/>
</CardContent>
</Card>
);
}
export default AdminStatusWidget;

View File

@@ -4,14 +4,17 @@ import Header from '../shared/Header';
import Sidebar from '../shared/Sidebar';
import { useAuth } from '../../contexts/AuthContext';
import Loading from '../shared/Loading';
import { LayoutProvider, useLayout, DRAWER_WIDTH, DRAWER_WIDTH_COLLAPSED } from '../../contexts/LayoutContext';
import ChatPanel from '../chat/ChatPanel';
interface DashboardLayoutProps {
children: ReactNode;
}
function DashboardLayout({ children }: DashboardLayoutProps) {
function DashboardLayoutInner({ children }: DashboardLayoutProps) {
const [mobileOpen, setMobileOpen] = useState(false);
const { isLoading } = useAuth();
const { sidebarCollapsed, chatPanelOpen } = useLayout();
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
@@ -21,6 +24,9 @@ function DashboardLayout({ children }: DashboardLayoutProps) {
return <Loading message="Lade Dashboard..." />;
}
const sidebarWidth = sidebarCollapsed ? DRAWER_WIDTH_COLLAPSED : DRAWER_WIDTH;
const chatWidth = chatPanelOpen ? 360 : 60;
return (
<Box sx={{ display: 'flex' }}>
<Header onMenuClick={handleDrawerToggle} />
@@ -31,16 +37,27 @@ function DashboardLayout({ children }: DashboardLayoutProps) {
sx={{
flexGrow: 1,
p: 3,
width: { sm: `calc(100% - 240px)` },
width: { sm: `calc(100% - ${sidebarWidth}px - ${chatWidth}px)` },
minHeight: '100vh',
backgroundColor: 'background.default',
transition: 'width 225ms cubic-bezier(0.4, 0, 0.6, 1)',
}}
>
<Toolbar />
{children}
</Box>
<ChatPanel />
</Box>
);
}
function DashboardLayout({ children }: DashboardLayoutProps) {
return (
<LayoutProvider>
<DashboardLayoutInner>{children}</DashboardLayoutInner>
</LayoutProvider>
);
}
export default DashboardLayout;

View File

@@ -9,3 +9,4 @@ export { default as BookStackSearchWidget } from './BookStackSearchWidget';
export { default as VikunjaMyTasksWidget } from './VikunjaMyTasksWidget';
export { default as VikunjaQuickAddWidget } from './VikunjaQuickAddWidget';
export { default as VikunjaOverdueNotifier } from './VikunjaOverdueNotifier';
export { default as AdminStatusWidget } from './AdminStatusWidget';