import React, { useEffect, useState, useCallback } from 'react'; import { Alert, Autocomplete, Box, Button, Chip, CircularProgress, Container, Grid, Paper, TextField, Typography, } from '@mui/material'; import { Save } from '@mui/icons-material'; import { useNavigate, useParams } from 'react-router-dom'; import DashboardLayout from '../components/dashboard/DashboardLayout'; import { PageHeader } from '../components/templates'; import GermanDateField from '../components/shared/GermanDateField'; import { vehiclesApi } from '../services/vehicles'; import { fahrzeugTypenApi } from '../services/fahrzeugTypen'; import type { FahrzeugTyp } from '../types/checklist.types'; import { CreateFahrzeugPayload, UpdateFahrzeugPayload, } from '../types/vehicle.types'; import { usePermissions } from '../hooks/usePermissions'; // ── Form state shape ────────────────────────────────────────────────────────── interface FormState { bezeichnung: string; kurzname: string; amtliches_kennzeichen: string; fahrgestellnummer: string; baujahr: string; hersteller: string; typ_schluessel: string; besatzung_soll: string; standort: string; bild_url: string; paragraph57a_faellig_am: string; naechste_wartung_am: string; } const EMPTY_FORM: FormState = { bezeichnung: '', kurzname: '', amtliches_kennzeichen: '', fahrgestellnummer: '', baujahr: '', hersteller: '', typ_schluessel: '', besatzung_soll: '', standort: 'Feuerwehrhaus', bild_url: '', paragraph57a_faellig_am: '', naechste_wartung_am: '', }; // ── Helpers ─────────────────────────────────────────────────────────────────── /** Convert a Date ISO string like '2026-03-15T00:00:00.000Z' to 'YYYY-MM-DD' for type="date" inputs */ function toDateInput(iso: string | null | undefined): string { if (!iso) return ''; const m = iso.match(/^(\d{4})-(\d{2})-(\d{2})/); if (m) return `${m[1]}-${m[2]}-${m[3]}`; return ''; } // ── Component ───────────────────────────────────────────────────────────────── function FahrzeugForm() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { isAdmin } = usePermissions(); const isEditMode = Boolean(id); const [form, setForm] = useState(EMPTY_FORM); const [loading, setLoading] = useState(isEditMode); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [saveError, setSaveError] = useState(null); const [fieldErrors, setFieldErrors] = useState>>({}); const [allTypes, setAllTypes] = useState([]); const [selectedTypes, setSelectedTypes] = useState([]); useEffect(() => { fahrzeugTypenApi.getAll().then(setAllTypes).catch(() => {}); }, []); const fetchVehicle = useCallback(async () => { if (!id) return; try { setLoading(true); setError(null); const [vehicle, types] = await Promise.all([ vehiclesApi.getById(id), fahrzeugTypenApi.getTypesForVehicle(id).catch(() => [] as FahrzeugTyp[]), ]); setForm({ bezeichnung: vehicle.bezeichnung, kurzname: vehicle.kurzname ?? '', amtliches_kennzeichen: vehicle.amtliches_kennzeichen ?? '', fahrgestellnummer: vehicle.fahrgestellnummer ?? '', baujahr: vehicle.baujahr?.toString() ?? '', hersteller: vehicle.hersteller ?? '', typ_schluessel: vehicle.typ_schluessel ?? '', besatzung_soll: vehicle.besatzung_soll ?? '', standort: vehicle.standort, bild_url: vehicle.bild_url ?? '', paragraph57a_faellig_am: toDateInput(vehicle.paragraph57a_faellig_am), naechste_wartung_am: toDateInput(vehicle.naechste_wartung_am), }); setSelectedTypes(types); } catch { setError('Fahrzeug konnte nicht geladen werden.'); } finally { setLoading(false); } }, [id]); useEffect(() => { if (isEditMode) fetchVehicle(); }, [isEditMode, fetchVehicle]); // ── Permission guard: only admins may create or edit vehicles ────────────── if (!isAdmin) { return ( Keine Berechtigung Sie haben nicht die erforderlichen Rechte, um Fahrzeuge zu bearbeiten. ); } const validate = (): boolean => { const errors: Partial> = {}; if (!form.bezeichnung.trim()) { errors.bezeichnung = 'Bezeichnung ist erforderlich.'; } setFieldErrors(errors); return Object.keys(errors).length === 0; }; const handleSubmit = async () => { if (!validate()) return; try { setSaving(true); setSaveError(null); if (isEditMode && id) { const payload: UpdateFahrzeugPayload = { bezeichnung: form.bezeichnung.trim() || undefined, kurzname: form.kurzname.trim() || null, amtliches_kennzeichen: form.amtliches_kennzeichen.trim() || null, fahrgestellnummer: form.fahrgestellnummer.trim() || null, baujahr: form.baujahr ? Number(form.baujahr) : null, hersteller: form.hersteller.trim() || null, typ_schluessel: form.typ_schluessel.trim() || null, besatzung_soll: form.besatzung_soll.trim() || null, standort: form.standort.trim() || 'Feuerwehrhaus', bild_url: form.bild_url.trim() || null, paragraph57a_faellig_am: form.paragraph57a_faellig_am || null, naechste_wartung_am: form.naechste_wartung_am || null, }; await vehiclesApi.update(id, payload); await fahrzeugTypenApi.setTypesForVehicle(id, selectedTypes.map((t) => t.id)); navigate(`/fahrzeuge/${id}`); } else { const payload: CreateFahrzeugPayload = { bezeichnung: form.bezeichnung.trim(), kurzname: form.kurzname.trim() || undefined, amtliches_kennzeichen: form.amtliches_kennzeichen.trim() || undefined, fahrgestellnummer: form.fahrgestellnummer.trim() || undefined, baujahr: form.baujahr ? Number(form.baujahr) : undefined, hersteller: form.hersteller.trim() || undefined, typ_schluessel: form.typ_schluessel.trim() || undefined, besatzung_soll: form.besatzung_soll.trim() || undefined, standort: form.standort.trim() || 'Feuerwehrhaus', bild_url: form.bild_url.trim() || undefined, paragraph57a_faellig_am: form.paragraph57a_faellig_am || undefined, naechste_wartung_am: form.naechste_wartung_am || undefined, }; const newVehicle = await vehiclesApi.create(payload); await fahrzeugTypenApi.setTypesForVehicle(newVehicle.id, selectedTypes.map((t) => t.id)); navigate(`/fahrzeuge/${newVehicle.id}`); } } catch { setSaveError( isEditMode ? 'Fahrzeug konnte nicht gespeichert werden.' : 'Fahrzeug konnte nicht erstellt werden.' ); } finally { setSaving(false); } }; const f = (field: keyof FormState) => ({ value: form[field] as string, onChange: (e: React.ChangeEvent) => setForm((prev) => ({ ...prev, [field]: e.target.value })), error: Boolean(fieldErrors[field]), helperText: fieldErrors[field], }); if (loading) { return ( ); } if (error) { return ( {error} ); } return ( {saveError && {saveError}} Stammdaten o.name} value={selectedTypes} onChange={(_e, val) => setSelectedTypes(val)} isOptionEqualToValue={(a, b) => a.id === b.id} renderTags={(value, getTagProps) => value.map((option, index) => ( )) } renderInput={(params) => ( )} /> Prüf- und Wartungsfristen setForm((prev) => ({ ...prev, paragraph57a_faellig_am: iso }))} helperText="Periodische Begutachtung (§57a StVO)" /> setForm((prev) => ({ ...prev, naechste_wartung_am: iso }))} helperText="Nächster geplanter Servicetermin" /> Bild ); } export default FahrzeugForm;