Files
dashboard/frontend/src/pages/Dashboard.tsx
Matthias Hochmeister b3a2fd9ff9 feat: responsive widgets, atemschutz permission UX, event hard-delete
- fix dashboard grid: use auto-fill instead of auto-fit for equal-width widgets
- atemschutz: skip stats/members API calls for non-privileged users, hide
  empty Aktionen column, add personal status subtitle
- kalender: add permanent delete option for events with confirmation dialog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-03 14:59:08 +01:00

122 lines
3.7 KiB
TypeScript

import { useState, useEffect } from 'react';
import {
Container,
Box,
Fade,
} from '@mui/material';
import { useAuth } from '../contexts/AuthContext';
import DashboardLayout from '../components/dashboard/DashboardLayout';
import SkeletonCard from '../components/shared/SkeletonCard';
import UserProfile from '../components/dashboard/UserProfile';
import NextcloudTalkWidget from '../components/dashboard/NextcloudTalkWidget';
import UpcomingEventsWidget from '../components/dashboard/UpcomingEventsWidget';
import AtemschutzDashboardCard from '../components/atemschutz/AtemschutzDashboardCard';
import EquipmentDashboardCard from '../components/equipment/EquipmentDashboardCard';
import VehicleDashboardCard from '../components/vehicles/VehicleDashboardCard';
import PersonalWarningsBanner from '../components/dashboard/PersonalWarningsBanner';
function Dashboard() {
const { user } = useAuth();
const [dataLoading, setDataLoading] = useState(true);
useEffect(() => {
const timer = setTimeout(() => {
setDataLoading(false);
}, 800);
return () => clearTimeout(timer);
}, []);
return (
<DashboardLayout>
<Container maxWidth={false} disableGutters>
<Box
sx={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))',
gap: 2.5,
alignItems: 'start',
}}
>
{/* User Profile Card — full width, contains welcome greeting */}
{user && (
<Box sx={{ gridColumn: '1 / -1' }}>
{dataLoading ? (
<SkeletonCard variant="detailed" />
) : (
<Fade in={true} timeout={600} style={{ transitionDelay: '100ms' }}>
<Box>
<UserProfile user={user} />
</Box>
</Fade>
)}
</Box>
)}
{/* Personal Warnings Banner — full width, conditionally rendered */}
{user && (
<Box sx={{ gridColumn: '1 / -1' }}>
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '150ms' }}>
<Box>
<PersonalWarningsBanner user={user} />
</Box>
</Fade>
</Box>
)}
{/* Vehicle Status Card */}
<Box>
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '300ms' }}>
<Box>
<VehicleDashboardCard />
</Box>
</Fade>
</Box>
{/* Equipment Status Card */}
<Box>
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '350ms' }}>
<Box>
<EquipmentDashboardCard />
</Box>
</Fade>
</Box>
{/* Atemschutz Status Card */}
<Box>
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '400ms' }}>
<Box>
<AtemschutzDashboardCard />
</Box>
</Fade>
</Box>
{/* Upcoming Events Widget */}
<Box>
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '440ms' }}>
<Box>
<UpcomingEventsWidget />
</Box>
</Fade>
</Box>
{/* Nextcloud Talk Widget */}
<Box>
{dataLoading ? (
<SkeletonCard variant="basic" />
) : (
<Fade in={true} timeout={600} style={{ transitionDelay: '480ms' }}>
<Box>
<NextcloudTalkWidget />
</Box>
</Fade>
)}
</Box>
</Box>
</Container>
</DashboardLayout>
);
}
export default Dashboard;