bug fixes
This commit is contained in:
@@ -1,174 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
Typography,
|
|
||||||
List,
|
|
||||||
ListItem,
|
|
||||||
ListItemAvatar,
|
|
||||||
ListItemText,
|
|
||||||
Avatar,
|
|
||||||
Box,
|
|
||||||
} from '@mui/material';
|
|
||||||
import {
|
|
||||||
LocalFireDepartment,
|
|
||||||
Person,
|
|
||||||
DirectionsCar,
|
|
||||||
Assignment,
|
|
||||||
} from '@mui/icons-material';
|
|
||||||
|
|
||||||
interface Activity {
|
|
||||||
id: string;
|
|
||||||
type: 'incident' | 'member' | 'vehicle' | 'task';
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
timestamp: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Placeholder activities
|
|
||||||
const placeholderActivities: Activity[] = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
type: 'incident',
|
|
||||||
title: 'Brandeinsatz',
|
|
||||||
description: 'Kleinbrand in der Hauptstraße',
|
|
||||||
timestamp: 'Vor 2 Stunden',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
type: 'member',
|
|
||||||
title: 'Neues Mitglied',
|
|
||||||
description: 'Max Mustermann ist der Feuerwehr beigetreten',
|
|
||||||
timestamp: 'Vor 5 Stunden',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
type: 'vehicle',
|
|
||||||
title: 'Fahrzeugwartung',
|
|
||||||
description: 'LF 16/12 - Wartung abgeschlossen',
|
|
||||||
timestamp: 'Gestern',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
type: 'task',
|
|
||||||
title: 'Aufgabe zugewiesen',
|
|
||||||
description: 'Neue Aufgabe: Inventur Atemschutzgeräte',
|
|
||||||
timestamp: 'Vor 2 Tagen',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const ActivityFeed: React.FC = () => {
|
|
||||||
const getActivityIcon = (type: Activity['type']) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'incident':
|
|
||||||
return <LocalFireDepartment />;
|
|
||||||
case 'member':
|
|
||||||
return <Person />;
|
|
||||||
case 'vehicle':
|
|
||||||
return <DirectionsCar />;
|
|
||||||
case 'task':
|
|
||||||
return <Assignment />;
|
|
||||||
default:
|
|
||||||
return <LocalFireDepartment />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getActivityColor = (type: Activity['type']) => {
|
|
||||||
switch (type) {
|
|
||||||
case 'incident':
|
|
||||||
return 'error.main';
|
|
||||||
case 'member':
|
|
||||||
return 'success.main';
|
|
||||||
case 'vehicle':
|
|
||||||
return 'warning.main';
|
|
||||||
case 'task':
|
|
||||||
return 'info.main';
|
|
||||||
default:
|
|
||||||
return 'primary.main';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card sx={{ height: '100%', overflow: 'hidden' }}>
|
|
||||||
<CardContent>
|
|
||||||
<Typography variant="h6" gutterBottom>
|
|
||||||
Letzte Aktivitäten
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<List sx={{ pt: 2 }}>
|
|
||||||
{placeholderActivities.map((activity, index) => (
|
|
||||||
<React.Fragment key={activity.id}>
|
|
||||||
<ListItem
|
|
||||||
alignItems="flex-start"
|
|
||||||
sx={{
|
|
||||||
px: 0,
|
|
||||||
position: 'relative',
|
|
||||||
'&::before':
|
|
||||||
index < placeholderActivities.length - 1
|
|
||||||
? {
|
|
||||||
content: '""',
|
|
||||||
position: 'absolute',
|
|
||||||
left: 19,
|
|
||||||
top: 56,
|
|
||||||
bottom: 0,
|
|
||||||
width: 2,
|
|
||||||
bgcolor: 'divider',
|
|
||||||
}
|
|
||||||
: {},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ListItemAvatar>
|
|
||||||
<Avatar
|
|
||||||
sx={{
|
|
||||||
bgcolor: getActivityColor(activity.type),
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{getActivityIcon(activity.type)}
|
|
||||||
</Avatar>
|
|
||||||
</ListItemAvatar>
|
|
||||||
<ListItemText
|
|
||||||
primary={
|
|
||||||
<Typography variant="subtitle2" component="span">
|
|
||||||
{activity.title}
|
|
||||||
</Typography>
|
|
||||||
}
|
|
||||||
secondary={
|
|
||||||
<Box>
|
|
||||||
<Typography
|
|
||||||
variant="body2"
|
|
||||||
color="text.secondary"
|
|
||||||
component="span"
|
|
||||||
sx={{ display: 'block' }}
|
|
||||||
>
|
|
||||||
{activity.description}
|
|
||||||
</Typography>
|
|
||||||
<Typography
|
|
||||||
variant="caption"
|
|
||||||
color="text.secondary"
|
|
||||||
component="span"
|
|
||||||
>
|
|
||||||
{activity.timestamp}
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</ListItem>
|
|
||||||
</React.Fragment>
|
|
||||||
))}
|
|
||||||
</List>
|
|
||||||
|
|
||||||
{placeholderActivities.length === 0 && (
|
|
||||||
<Box sx={{ textAlign: 'center', py: 4 }}>
|
|
||||||
<Typography variant="body2" color="text.secondary">
|
|
||||||
Keine Aktivitäten vorhanden
|
|
||||||
</Typography>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ActivityFeed;
|
|
||||||
@@ -61,6 +61,9 @@ const UserProfile: React.FC<UserProfileProps> = ({ user }) => {
|
|||||||
{/* User Info */}
|
{/* User Info */}
|
||||||
<Box sx={{ flex: 1, textAlign: { xs: 'center', sm: 'left' } }}>
|
<Box sx={{ flex: 1, textAlign: { xs: 'center', sm: 'left' } }}>
|
||||||
<Typography variant="h5" component="div" gutterBottom>
|
<Typography variant="h5" component="div" gutterBottom>
|
||||||
|
Willkommen zurück, {user.given_name || user.name.split(' ')[0]}!
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2" sx={{ opacity: 0.75, mb: 0.5 }}>
|
||||||
{user.name}
|
{user.name}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography variant="body2" sx={{ opacity: 0.9 }}>
|
<Typography variant="body2" sx={{ opacity: 0.9 }}>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
export { default as UserProfile } from './UserProfile';
|
export { default as UserProfile } from './UserProfile';
|
||||||
export { default as ServiceCard } from './ServiceCard';
|
export { default as ServiceCard } from './ServiceCard';
|
||||||
export { default as StatsCard } from './StatsCard';
|
export { default as StatsCard } from './StatsCard';
|
||||||
export { default as ActivityFeed } from './ActivityFeed';
|
|
||||||
export { default as DashboardLayout } from './DashboardLayout';
|
export { default as DashboardLayout } from './DashboardLayout';
|
||||||
export { default as PersonalWarningsBanner } from './PersonalWarningsBanner';
|
export { default as PersonalWarningsBanner } from './PersonalWarningsBanner';
|
||||||
export { default as UpcomingEventsWidget } from './UpcomingEventsWidget';
|
export { default as UpcomingEventsWidget } from './UpcomingEventsWidget';
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { useState, useEffect } from 'react';
|
|||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
Box,
|
Box,
|
||||||
Typography,
|
|
||||||
Grid,
|
|
||||||
Fade,
|
Fade,
|
||||||
} from '@mui/material';
|
} from '@mui/material';
|
||||||
import { useAuth } from '../contexts/AuthContext';
|
import { useAuth } from '../contexts/AuthContext';
|
||||||
@@ -12,7 +10,6 @@ import SkeletonCard from '../components/shared/SkeletonCard';
|
|||||||
import UserProfile from '../components/dashboard/UserProfile';
|
import UserProfile from '../components/dashboard/UserProfile';
|
||||||
import NextcloudTalkWidget from '../components/dashboard/NextcloudTalkWidget';
|
import NextcloudTalkWidget from '../components/dashboard/NextcloudTalkWidget';
|
||||||
import UpcomingEventsWidget from '../components/dashboard/UpcomingEventsWidget';
|
import UpcomingEventsWidget from '../components/dashboard/UpcomingEventsWidget';
|
||||||
import ActivityFeed from '../components/dashboard/ActivityFeed';
|
|
||||||
import AtemschutzDashboardCard from '../components/atemschutz/AtemschutzDashboardCard';
|
import AtemschutzDashboardCard from '../components/atemschutz/AtemschutzDashboardCard';
|
||||||
import EquipmentDashboardCard from '../components/equipment/EquipmentDashboardCard';
|
import EquipmentDashboardCard from '../components/equipment/EquipmentDashboardCard';
|
||||||
import VehicleDashboardCard from '../components/vehicles/VehicleDashboardCard';
|
import VehicleDashboardCard from '../components/vehicles/VehicleDashboardCard';
|
||||||
@@ -33,25 +30,22 @@ function Dashboard() {
|
|||||||
return (
|
return (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
<Container maxWidth={false} disableGutters>
|
<Container maxWidth={false} disableGutters>
|
||||||
<Grid container spacing={3}>
|
<Box
|
||||||
{/* Welcome Message */}
|
sx={{
|
||||||
<Grid item xs={12}>
|
display: 'grid',
|
||||||
{dataLoading ? (
|
gridTemplateColumns: {
|
||||||
<SkeletonCard variant="basic" />
|
xs: '1fr',
|
||||||
) : (
|
sm: 'repeat(2, 1fr)',
|
||||||
<Fade in={true} timeout={600}>
|
lg: 'repeat(3, 1fr)',
|
||||||
<Box>
|
xl: 'repeat(4, 1fr)',
|
||||||
<Typography variant="h4" gutterBottom>
|
},
|
||||||
Willkommen zurück, {user?.given_name || user?.name.split(' ')[0]}!
|
gap: 2.5,
|
||||||
</Typography>
|
alignItems: 'start',
|
||||||
</Box>
|
}}
|
||||||
</Fade>
|
>
|
||||||
)}
|
{/* User Profile Card — full width, contains welcome greeting */}
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* User Profile Card */}
|
|
||||||
{user && (
|
{user && (
|
||||||
<Grid item xs={12}>
|
<Box sx={{ gridColumn: '1 / -1' }}>
|
||||||
{dataLoading ? (
|
{dataLoading ? (
|
||||||
<SkeletonCard variant="detailed" />
|
<SkeletonCard variant="detailed" />
|
||||||
) : (
|
) : (
|
||||||
@@ -61,82 +55,69 @@ function Dashboard() {
|
|||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Personal Atemschutz Warnings — shown only when relevant */}
|
{/* Personal Warnings Banner — full width, conditionally rendered */}
|
||||||
{user && (
|
{user && (
|
||||||
<Grid item xs={12}>
|
<Box sx={{ gridColumn: '1 / -1' }}>
|
||||||
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '150ms' }}>
|
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '150ms' }}>
|
||||||
<Box>
|
<Box>
|
||||||
<PersonalWarningsBanner user={user} />
|
<PersonalWarningsBanner user={user} />
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
</Grid>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Vehicle Status Card */}
|
{/* Vehicle Status Card */}
|
||||||
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
|
<Box>
|
||||||
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '380ms' }}>
|
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '300ms' }}>
|
||||||
<Box sx={{ height: '100%' }}>
|
<Box>
|
||||||
<VehicleDashboardCard />
|
<VehicleDashboardCard />
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
</Grid>
|
</Box>
|
||||||
|
|
||||||
{/* Equipment Status Card */}
|
{/* Equipment Status Card */}
|
||||||
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
|
<Box>
|
||||||
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '450ms' }}>
|
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '350ms' }}>
|
||||||
<Box sx={{ height: '100%' }}>
|
<Box>
|
||||||
<EquipmentDashboardCard />
|
<EquipmentDashboardCard />
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
</Grid>
|
</Box>
|
||||||
|
|
||||||
{/* Atemschutz Status Card */}
|
{/* Atemschutz Status Card */}
|
||||||
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
|
<Box>
|
||||||
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '420ms' }}>
|
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '400ms' }}>
|
||||||
<Box sx={{ height: '100%' }}>
|
<Box>
|
||||||
<AtemschutzDashboardCard />
|
<AtemschutzDashboardCard />
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
</Grid>
|
</Box>
|
||||||
|
|
||||||
{/* Upcoming Events Widget */}
|
{/* Upcoming Events Widget */}
|
||||||
<Grid item xs={12} md={6} sx={{ display: 'flex' }}>
|
<Box>
|
||||||
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '440ms' }}>
|
<Fade in={!dataLoading} timeout={600} style={{ transitionDelay: '440ms' }}>
|
||||||
<Box sx={{ height: '100%' }}>
|
<Box>
|
||||||
<UpcomingEventsWidget />
|
<UpcomingEventsWidget />
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
</Grid>
|
</Box>
|
||||||
|
|
||||||
{/* Nextcloud Talk Widget */}
|
{/* Nextcloud Talk Widget */}
|
||||||
<Grid item xs={12} md={6} lg={5} sx={{ display: 'flex' }}>
|
<Box>
|
||||||
{dataLoading ? (
|
{dataLoading ? (
|
||||||
<SkeletonCard variant="basic" />
|
<SkeletonCard variant="basic" />
|
||||||
) : (
|
) : (
|
||||||
<Fade in={true} timeout={600} style={{ transitionDelay: '520ms' }}>
|
<Fade in={true} timeout={600} style={{ transitionDelay: '480ms' }}>
|
||||||
<Box sx={{ height: '100%' }}>
|
<Box>
|
||||||
<NextcloudTalkWidget />
|
<NextcloudTalkWidget />
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Fade>
|
||||||
)}
|
)}
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{/* Activity Feed */}
|
|
||||||
<Grid item xs={12} md={6} lg={7} sx={{ display: 'flex' }}>
|
|
||||||
{dataLoading ? (
|
|
||||||
<SkeletonCard variant="detailed" />
|
|
||||||
) : (
|
|
||||||
<Fade in={true} timeout={600} style={{ transitionDelay: '550ms' }}>
|
|
||||||
<Box sx={{ height: '100%' }}>
|
|
||||||
<ActivityFeed />
|
|
||||||
</Box>
|
</Box>
|
||||||
</Fade>
|
</Box>
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Container>
|
</Container>
|
||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user