- 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>
122 lines
3.7 KiB
TypeScript
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;
|