149 lines
4.4 KiB
TypeScript
149 lines
4.4 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import {
|
|
Box,
|
|
Card,
|
|
CardContent,
|
|
CircularProgress,
|
|
Link,
|
|
Typography,
|
|
} from '@mui/material';
|
|
import { Link as RouterLink } from 'react-router-dom';
|
|
import { atemschutzApi } from '../../services/atemschutz';
|
|
import type { AtemschutzStats } from '../../types/atemschutz.types';
|
|
|
|
interface AtemschutzDashboardCardProps {
|
|
hideWhenEmpty?: boolean;
|
|
}
|
|
|
|
const AtemschutzDashboardCard: React.FC<AtemschutzDashboardCardProps> = ({
|
|
hideWhenEmpty = false,
|
|
}) => {
|
|
const [stats, setStats] = useState<AtemschutzStats | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
let mounted = true;
|
|
const fetchStats = async () => {
|
|
try {
|
|
setLoading(true);
|
|
setError(null);
|
|
const data = await atemschutzApi.getStats();
|
|
if (mounted) setStats(data);
|
|
} catch {
|
|
if (mounted) setError('Atemschutzstatus konnte nicht geladen werden.');
|
|
} finally {
|
|
if (mounted) setLoading(false);
|
|
}
|
|
};
|
|
fetchStats();
|
|
return () => {
|
|
mounted = false;
|
|
};
|
|
}, []);
|
|
|
|
if (loading) {
|
|
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>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<Card>
|
|
<CardContent>
|
|
<Typography variant="body2" color="error">
|
|
{error}
|
|
</Typography>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
if (!stats) return null;
|
|
|
|
// Determine if there are any concerns
|
|
const hasConcerns =
|
|
stats.untersuchungAbgelaufen > 0 ||
|
|
stats.leistungstestAbgelaufen > 0 ||
|
|
stats.untersuchungBaldFaellig > 0 ||
|
|
stats.leistungstestBaldFaellig > 0;
|
|
|
|
const allGood = stats.einsatzbereit === stats.total && !hasConcerns;
|
|
|
|
// If hideWhenEmpty and everything is fine, render nothing
|
|
if (hideWhenEmpty && allGood) return null;
|
|
|
|
return (
|
|
<Card>
|
|
<CardContent>
|
|
<Typography variant="h6" gutterBottom>
|
|
Atemschutz
|
|
</Typography>
|
|
|
|
{/* Main metric */}
|
|
<Typography variant="h4" fontWeight={700} color={allGood ? 'success.main' : 'text.primary'}>
|
|
{stats.einsatzbereit}/{stats.total}
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 1.5 }}>
|
|
einsatzbereit
|
|
</Typography>
|
|
|
|
{/* Concerns list */}
|
|
{hasConcerns && (
|
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.5 }}>
|
|
{stats.untersuchungAbgelaufen > 0 && (
|
|
<Typography variant="body2" color="error.main">
|
|
{stats.untersuchungAbgelaufen} Untersuchung{stats.untersuchungAbgelaufen !== 1 ? 'en' : ''} abgelaufen
|
|
</Typography>
|
|
)}
|
|
{stats.leistungstestAbgelaufen > 0 && (
|
|
<Typography variant="body2" color="error.main">
|
|
{stats.leistungstestAbgelaufen} Leistungstest{stats.leistungstestAbgelaufen !== 1 ? 's' : ''} abgelaufen
|
|
</Typography>
|
|
)}
|
|
{stats.untersuchungBaldFaellig > 0 && (
|
|
<Typography variant="body2" color="warning.main">
|
|
{stats.untersuchungBaldFaellig} Untersuchung{stats.untersuchungBaldFaellig !== 1 ? 'en' : ''} bald fällig
|
|
</Typography>
|
|
)}
|
|
{stats.leistungstestBaldFaellig > 0 && (
|
|
<Typography variant="body2" color="warning.main">
|
|
{stats.leistungstestBaldFaellig} Leistungstest{stats.leistungstestBaldFaellig !== 1 ? 's' : ''} bald fällig
|
|
</Typography>
|
|
)}
|
|
</Box>
|
|
)}
|
|
|
|
{/* All good message */}
|
|
{allGood && (
|
|
<Typography variant="body2" color="success.main">
|
|
Alle Atemschutzträger einsatzbereit
|
|
</Typography>
|
|
)}
|
|
|
|
{/* Link to management page */}
|
|
<Box sx={{ mt: 2 }}>
|
|
<Link
|
|
component={RouterLink}
|
|
to="/atemschutz"
|
|
underline="hover"
|
|
variant="body2"
|
|
>
|
|
Zur Verwaltung
|
|
</Link>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
};
|
|
|
|
export default AtemschutzDashboardCard;
|