adding chat features, admin features and bug fixes
This commit is contained in:
67
frontend/src/components/dashboard/AdminStatusWidget.tsx
Normal file
67
frontend/src/components/dashboard/AdminStatusWidget.tsx
Normal 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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user