widget icon rework, widget grouping rework
This commit is contained in:
@@ -3,16 +3,16 @@ import {
|
||||
Alert,
|
||||
AlertTitle,
|
||||
Box,
|
||||
Card,
|
||||
CardContent,
|
||||
CircularProgress,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import AirIcon from '@mui/icons-material/Air';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { atemschutzApi } from '../../services/atemschutz';
|
||||
import { useCountUp } from '../../hooks/useCountUp';
|
||||
import type { AtemschutzStats } from '../../types/atemschutz.types';
|
||||
import { WidgetCard } from '../templates/WidgetCard';
|
||||
import { StatSkeleton } from '../templates/SkeletonPresets';
|
||||
|
||||
interface AtemschutzDashboardCardProps {
|
||||
hideWhenEmpty?: boolean;
|
||||
@@ -30,102 +30,74 @@ const AtemschutzDashboardCard: React.FC<AtemschutzDashboardCardProps> = ({
|
||||
const animatedReady = useCountUp(stats?.einsatzbereit ?? 0);
|
||||
const animatedTotal = useCountUp(stats?.total ?? 0);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent sx={{ display: 'flex', alignItems: 'center', gap: 1, py: 2 }}>
|
||||
<CircularProgress size={16} />
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
Atemschutzstatus wird geladen...
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
const hasConcerns = stats
|
||||
? stats.untersuchungAbgelaufen > 0 ||
|
||||
stats.leistungstestAbgelaufen > 0 ||
|
||||
stats.untersuchungBaldFaellig > 0 ||
|
||||
stats.leistungstestBaldFaellig > 0
|
||||
: false;
|
||||
const allGood = stats ? stats.einsatzbereit === stats.total && !hasConcerns : false;
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Typography variant="body2" color="error">
|
||||
Atemschutzstatus konnte nicht geladen werden.
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
if (!stats) return null;
|
||||
|
||||
const hasConcerns =
|
||||
stats.untersuchungAbgelaufen > 0 ||
|
||||
stats.leistungstestAbgelaufen > 0 ||
|
||||
stats.untersuchungBaldFaellig > 0 ||
|
||||
stats.leistungstestBaldFaellig > 0;
|
||||
|
||||
const allGood = stats.einsatzbereit === stats.total && !hasConcerns;
|
||||
|
||||
if (hideWhenEmpty && allGood) return null;
|
||||
if (!isLoading && !isError && stats && hideWhenEmpty && allGood) return null;
|
||||
|
||||
return (
|
||||
<Card sx={{ height: '100%', cursor: 'pointer' }} onClick={() => navigate('/atemschutz')}>
|
||||
<CardContent>
|
||||
<Typography variant="h6" gutterBottom>
|
||||
Atemschutz
|
||||
</Typography>
|
||||
<WidgetCard
|
||||
title="Atemschutz"
|
||||
icon={<AirIcon />}
|
||||
isLoading={isLoading}
|
||||
isError={isError}
|
||||
errorMessage="Atemschutzstatus konnte nicht geladen werden."
|
||||
skeleton={<StatSkeleton />}
|
||||
onClick={() => navigate('/atemschutz')}
|
||||
>
|
||||
<Typography variant="h4" fontWeight={700} color={allGood ? 'success.main' : 'text.primary'}>
|
||||
{animatedReady}/{animatedTotal}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 1.5 }}>
|
||||
einsatzbereit
|
||||
</Typography>
|
||||
|
||||
{/* Main metric */}
|
||||
<Typography variant="h4" fontWeight={700} color={allGood ? 'success.main' : 'text.primary'}>
|
||||
{animatedReady}/{animatedTotal}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 1.5 }}>
|
||||
einsatzbereit
|
||||
</Typography>
|
||||
{hasConcerns && stats && (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, mt: 1 }}>
|
||||
{(stats.untersuchungAbgelaufen > 0 || stats.leistungstestAbgelaufen > 0) && (
|
||||
<Alert severity="error" variant="outlined" sx={{ py: 0.5 }}>
|
||||
<AlertTitle sx={{ fontWeight: 600, mb: 0.5 }}>Abgelaufen</AlertTitle>
|
||||
{stats.untersuchungAbgelaufen > 0 && (
|
||||
<Typography variant="body2">
|
||||
{stats.untersuchungAbgelaufen} Untersuchung{stats.untersuchungAbgelaufen !== 1 ? 'en' : ''} abgelaufen
|
||||
</Typography>
|
||||
)}
|
||||
{stats.leistungstestAbgelaufen > 0 && (
|
||||
<Typography variant="body2">
|
||||
{stats.leistungstestAbgelaufen} Leistungstest{stats.leistungstestAbgelaufen !== 1 ? 's' : ''} abgelaufen
|
||||
</Typography>
|
||||
)}
|
||||
</Alert>
|
||||
)}
|
||||
{(stats.untersuchungBaldFaellig > 0 || stats.leistungstestBaldFaellig > 0) && (
|
||||
<Alert severity="warning" variant="outlined" sx={{ py: 0.5 }}>
|
||||
<AlertTitle sx={{ fontWeight: 600, mb: 0.5 }}>Bald fällig</AlertTitle>
|
||||
{stats.untersuchungBaldFaellig > 0 && (
|
||||
<Typography variant="body2">
|
||||
{stats.untersuchungBaldFaellig} Untersuchung{stats.untersuchungBaldFaellig !== 1 ? 'en' : ''} bald fällig
|
||||
</Typography>
|
||||
)}
|
||||
{stats.leistungstestBaldFaellig > 0 && (
|
||||
<Typography variant="body2">
|
||||
{stats.leistungstestBaldFaellig} Leistungstest{stats.leistungstestBaldFaellig !== 1 ? 's' : ''} bald fällig
|
||||
</Typography>
|
||||
)}
|
||||
</Alert>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Concerns list — using Alert components for consistent warning styling */}
|
||||
{hasConcerns && (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, mt: 1 }}>
|
||||
{(stats.untersuchungAbgelaufen > 0 || stats.leistungstestAbgelaufen > 0) && (
|
||||
<Alert severity="error" variant="outlined" sx={{ py: 0.5 }}>
|
||||
<AlertTitle sx={{ fontWeight: 600, mb: 0.5 }}>Abgelaufen</AlertTitle>
|
||||
{stats.untersuchungAbgelaufen > 0 && (
|
||||
<Typography variant="body2">
|
||||
{stats.untersuchungAbgelaufen} Untersuchung{stats.untersuchungAbgelaufen !== 1 ? 'en' : ''} abgelaufen
|
||||
</Typography>
|
||||
)}
|
||||
{stats.leistungstestAbgelaufen > 0 && (
|
||||
<Typography variant="body2">
|
||||
{stats.leistungstestAbgelaufen} Leistungstest{stats.leistungstestAbgelaufen !== 1 ? 's' : ''} abgelaufen
|
||||
</Typography>
|
||||
)}
|
||||
</Alert>
|
||||
)}
|
||||
{(stats.untersuchungBaldFaellig > 0 || stats.leistungstestBaldFaellig > 0) && (
|
||||
<Alert severity="warning" variant="outlined" sx={{ py: 0.5 }}>
|
||||
<AlertTitle sx={{ fontWeight: 600, mb: 0.5 }}>Bald fällig</AlertTitle>
|
||||
{stats.untersuchungBaldFaellig > 0 && (
|
||||
<Typography variant="body2">
|
||||
{stats.untersuchungBaldFaellig} Untersuchung{stats.untersuchungBaldFaellig !== 1 ? 'en' : ''} bald fällig
|
||||
</Typography>
|
||||
)}
|
||||
{stats.leistungstestBaldFaellig > 0 && (
|
||||
<Typography variant="body2">
|
||||
{stats.leistungstestBaldFaellig} Leistungstest{stats.leistungstestBaldFaellig !== 1 ? 's' : ''} bald fällig
|
||||
</Typography>
|
||||
)}
|
||||
</Alert>
|
||||
)}
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* All good message */}
|
||||
{allGood && (
|
||||
<Typography variant="body2" color="success.main">
|
||||
Alle Atemschutzträger einsatzbereit
|
||||
</Typography>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
{allGood && (
|
||||
<Typography variant="body2" color="success.main">
|
||||
Alle Atemschutzträger einsatzbereit
|
||||
</Typography>
|
||||
)}
|
||||
</WidgetCard>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user