import React, { useEffect, useState, useCallback } from 'react';
import {
Alert,
Box,
Button,
Card,
CardActionArea,
CardContent,
CardMedia,
Chip,
CircularProgress,
Container,
Grid,
InputAdornment,
TextField,
Tooltip,
Typography,
} from '@mui/material';
import {
Add,
CheckCircle,
DirectionsCar,
Error as ErrorIcon,
FileDownload,
PauseCircle,
School,
Search,
Warning,
ReportProblem,
} from '@mui/icons-material';
import { useNavigate } from 'react-router-dom';
import DashboardLayout from '../components/dashboard/DashboardLayout';
import ChatAwareFab from '../components/shared/ChatAwareFab';
import { vehiclesApi } from '../services/vehicles';
import { equipmentApi } from '../services/equipment';
import type { VehicleEquipmentWarning } from '../types/equipment.types';
import { AusruestungStatus, AusruestungStatusLabel } from '../types/equipment.types';
import {
FahrzeugListItem,
FahrzeugStatus,
FahrzeugStatusLabel,
} from '../types/vehicle.types';
import { usePermissions } from '../hooks/usePermissions';
// ── Status chip config ────────────────────────────────────────────────────────
const STATUS_CONFIG: Record<
FahrzeugStatus,
{ color: 'success' | 'warning' | 'error' | 'info'; icon: React.ReactElement }
> = {
[FahrzeugStatus.Einsatzbereit]: { color: 'success', icon: },
[FahrzeugStatus.AusserDienstWartung]: { color: 'warning', icon: },
[FahrzeugStatus.AusserDienstSchaden]: { color: 'error', icon: },
};
// ── Inspection badge helpers ──────────────────────────────────────────────────
type InspBadgeColor = 'success' | 'warning' | 'error' | 'default';
function inspBadgeColor(tage: number | null): InspBadgeColor {
if (tage === null) return 'default';
if (tage < 0) return 'error';
if (tage <= 30) return 'warning';
return 'success';
}
function inspBadgeLabel(art: string, tage: number | null, faelligAm: string | null): string {
if (faelligAm === null) return '';
const date = new Date(faelligAm).toLocaleDateString('de-DE', {
day: '2-digit', month: '2-digit', year: '2-digit',
});
if (tage === null) return `${art}: ${date}`;
if (tage < 0) return `${art}: ÜBERFÄLLIG (${date})`;
if (tage === 0) return `${art}: heute (${date})`;
return `${art}: ${date}`;
}
function inspTooltipTitle(fullLabel: string, tage: number | null, faelligAm: string | null): string {
if (!faelligAm) return fullLabel;
const date = new Date(faelligAm).toLocaleDateString('de-DE');
if (tage !== null && tage < 0) {
return `${fullLabel}: Seit ${Math.abs(tage)} Tagen überfällig!`;
}
return `${fullLabel}: Fällig am ${date}`;
}
// ── Vehicle Card ──────────────────────────────────────────────────────────────
interface VehicleCardProps {
vehicle: FahrzeugListItem;
onClick: (id: string) => void;
warnings?: VehicleEquipmentWarning[];
}
const VehicleCard: React.FC = ({ vehicle, onClick, warnings = [] }) => {
const status = vehicle.status as FahrzeugStatus;
const statusCfg = STATUS_CONFIG[status] ?? STATUS_CONFIG[FahrzeugStatus.Einsatzbereit];
const isSchaden = status === FahrzeugStatus.AusserDienstSchaden;
const inspBadges = [
{
art: '§57a',
fullLabel: '§57a Periodische Prüfung',
tage: vehicle.paragraph57a_tage_bis_faelligkeit,
faelligAm: vehicle.paragraph57a_faellig_am,
},
{
art: 'Wartung',
fullLabel: 'Nächste Wartung / Service',
tage: vehicle.wartung_tage_bis_faelligkeit,
faelligAm: vehicle.naechste_wartung_am,
},
].filter((b) => b.faelligAm !== null);
return (
{isSchaden && (
)}
onClick(vehicle.id)}
sx={{ flexGrow: 1, display: 'flex', flexDirection: 'column', alignItems: 'stretch' }}
>
{vehicle.bild_url ? (
) : (
)}
{vehicle.bezeichnung}
{vehicle.kurzname && (
({vehicle.kurzname})
)}
{vehicle.amtliches_kennzeichen && (
{vehicle.amtliches_kennzeichen}
)}
{vehicle.aktiver_lehrgang && (
}
label="In Lehrgang"
color="info"
size="small"
variant="outlined"
/>
)}
{(status === FahrzeugStatus.AusserDienstWartung || status === FahrzeugStatus.AusserDienstSchaden) &&
vehicle.ausser_dienst_bis && (
Bis ca. {new Date(vehicle.ausser_dienst_bis).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })}
)}
{inspBadges.length > 0 && (
{inspBadges.map((b) => {
const color = inspBadgeColor(b.tage);
const label = inspBadgeLabel(b.art, b.tage, b.faelligAm);
if (!label) return null;
return (
: undefined}
sx={{ fontSize: '0.7rem' }}
/>
);
})}
)}
{warnings.length > 0 && (
{warnings.slice(0, warnings.length > 3 ? 2 : 3).map((w) => {
const isError =
w.status === AusruestungStatus.Beschaedigt ||
w.status === AusruestungStatus.AusserDienst;
return (
}
label={w.bezeichnung}
color={isError ? 'error' : 'warning'}
sx={{ fontSize: '0.7rem', maxWidth: 160 }}
/>
);
})}
{warnings.length > 3 && (
`${w.bezeichnung}: ${AusruestungStatusLabel[w.status]}`)
.join('\n')}
>
}
label={`+${warnings.length - 2} weitere`}
color={
warnings
.slice(2)
.some(
(w) =>
w.status === AusruestungStatus.Beschaedigt ||
w.status === AusruestungStatus.AusserDienst
)
? 'error'
: 'warning'
}
sx={{ fontSize: '0.7rem' }}
/>
)}
)}
);
};
// ── Main Page ─────────────────────────────────────────────────────────────────
function Fahrzeuge() {
const navigate = useNavigate();
const { isAdmin } = usePermissions();
const [vehicles, setVehicles] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [search, setSearch] = useState('');
const [equipmentWarnings, setEquipmentWarnings] = useState