new features
This commit is contained in:
@@ -65,8 +65,12 @@ import {
|
||||
FahrzeugStatus,
|
||||
FahrzeugStatusLabel,
|
||||
CreateWartungslogPayload,
|
||||
UpdateWartungslogPayload,
|
||||
UpdateStatusPayload,
|
||||
WartungslogArt,
|
||||
WartungslogErgebnis,
|
||||
WartungslogErgebnisLabel,
|
||||
WartungslogErgebnisColor,
|
||||
OverlappingBooking,
|
||||
} from '../types/vehicle.types';
|
||||
import type { AusruestungListItem } from '../types/equipment.types';
|
||||
@@ -470,6 +474,7 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [saveError, setSaveError] = useState<string | null>(null);
|
||||
const [editingWartungId, setEditingWartungId] = useState<string | null>(null);
|
||||
|
||||
const emptyForm: CreateWartungslogPayload = {
|
||||
datum: '',
|
||||
@@ -479,10 +484,36 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
kraftstoff_liter: undefined,
|
||||
kosten: undefined,
|
||||
externe_werkstatt: '',
|
||||
ergebnis: undefined,
|
||||
naechste_faelligkeit: '',
|
||||
};
|
||||
|
||||
const [form, setForm] = useState<CreateWartungslogPayload>(emptyForm);
|
||||
|
||||
const openCreateDialog = () => {
|
||||
setEditingWartungId(null);
|
||||
setForm(emptyForm);
|
||||
setSaveError(null);
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
const openEditDialog = (entry: FahrzeugWartungslog) => {
|
||||
setEditingWartungId(entry.id);
|
||||
setForm({
|
||||
datum: entry.datum ? new Date(entry.datum).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) : '',
|
||||
art: entry.art ?? undefined,
|
||||
beschreibung: entry.beschreibung,
|
||||
km_stand: entry.km_stand ?? undefined,
|
||||
kraftstoff_liter: entry.kraftstoff_liter ?? undefined,
|
||||
kosten: entry.kosten ?? undefined,
|
||||
externe_werkstatt: entry.externe_werkstatt ?? '',
|
||||
ergebnis: entry.ergebnis ?? undefined,
|
||||
naechste_faelligkeit: entry.naechste_faelligkeit ? entry.naechste_faelligkeit.slice(0, 10) : '',
|
||||
});
|
||||
setSaveError(null);
|
||||
setDialogOpen(true);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!form.datum || !form.beschreibung.trim()) {
|
||||
setSaveError('Datum und Beschreibung sind erforderlich.');
|
||||
@@ -491,13 +522,29 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
try {
|
||||
setSaving(true);
|
||||
setSaveError(null);
|
||||
await vehiclesApi.addWartungslog(fahrzeugId, {
|
||||
...form,
|
||||
datum: fromGermanDate(form.datum) || form.datum,
|
||||
externe_werkstatt: form.externe_werkstatt || undefined,
|
||||
});
|
||||
const isoDate = fromGermanDate(form.datum) || form.datum;
|
||||
if (editingWartungId) {
|
||||
const payload: UpdateWartungslogPayload = {
|
||||
datum: isoDate,
|
||||
art: form.art,
|
||||
beschreibung: form.beschreibung,
|
||||
km_stand: form.km_stand,
|
||||
externe_werkstatt: form.externe_werkstatt || undefined,
|
||||
ergebnis: form.ergebnis,
|
||||
naechste_faelligkeit: form.naechste_faelligkeit || undefined,
|
||||
};
|
||||
await vehiclesApi.updateWartungslog(fahrzeugId, editingWartungId, payload);
|
||||
} else {
|
||||
await vehiclesApi.addWartungslog(fahrzeugId, {
|
||||
...form,
|
||||
datum: isoDate,
|
||||
externe_werkstatt: form.externe_werkstatt || undefined,
|
||||
naechste_faelligkeit: form.naechste_faelligkeit || undefined,
|
||||
});
|
||||
}
|
||||
setDialogOpen(false);
|
||||
setForm(emptyForm);
|
||||
setEditingWartungId(null);
|
||||
onAdded();
|
||||
} catch {
|
||||
setSaveError('Wartungseintrag konnte nicht gespeichert werden.');
|
||||
@@ -521,14 +568,20 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 0.25 }}>
|
||||
<Typography variant="subtitle2">{fmtDate(entry.datum)}</Typography>
|
||||
{entry.art && <Chip label={entry.art} size="small" variant="outlined" />}
|
||||
{entry.ergebnis && (
|
||||
<Chip
|
||||
label={WartungslogErgebnisLabel[entry.ergebnis]}
|
||||
size="small"
|
||||
color={WartungslogErgebnisColor[entry.ergebnis]}
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
<Typography variant="body2">{entry.beschreibung}</Typography>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.25 }}>
|
||||
{[
|
||||
entry.km_stand != null && `${entry.km_stand.toLocaleString('de-DE')} km`,
|
||||
entry.kraftstoff_liter != null && `${Number(entry.kraftstoff_liter).toFixed(1)} L`,
|
||||
entry.kosten != null && `${Number(entry.kosten).toFixed(2)} €`,
|
||||
entry.externe_werkstatt && entry.externe_werkstatt,
|
||||
entry.naechste_faelligkeit && `Nächste Fälligkeit: ${fmtDate(entry.naechste_faelligkeit)}`,
|
||||
].filter(Boolean).join(' · ')}
|
||||
</Typography>
|
||||
{entry.dokument_url ? (
|
||||
@@ -568,6 +621,11 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
</Button>
|
||||
) : null}
|
||||
</Box>
|
||||
{canWrite && (
|
||||
<IconButton size="small" onClick={() => openEditDialog(entry)} aria-label="Bearbeiten">
|
||||
<Edit fontSize="small" />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
@@ -578,14 +636,14 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
<ChatAwareFab
|
||||
size="small"
|
||||
aria-label="Wartung eintragen"
|
||||
onClick={() => { setForm(emptyForm); setDialogOpen(true); }}
|
||||
onClick={openCreateDialog}
|
||||
>
|
||||
<Add />
|
||||
</ChatAwareFab>
|
||||
)}
|
||||
|
||||
<Dialog open={dialogOpen} onClose={() => setDialogOpen(false)} maxWidth="sm" fullWidth>
|
||||
<DialogTitle>Wartung / Service eintragen</DialogTitle>
|
||||
<DialogTitle>{editingWartungId ? 'Wartungseintrag bearbeiten' : 'Wartung / Service eintragen'}</DialogTitle>
|
||||
<DialogContent>
|
||||
{saveError && <Alert severity="error" sx={{ mb: 2 }}>{saveError}</Alert>}
|
||||
<Grid container spacing={2} sx={{ mt: 0.5 }}>
|
||||
@@ -624,7 +682,7 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
onChange={(e) => setForm((f) => ({ ...f, beschreibung: e.target.value }))}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
label="km-Stand"
|
||||
type="number"
|
||||
@@ -634,27 +692,7 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
inputProps={{ min: 0 }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<TextField
|
||||
label="Kraftstoff (L)"
|
||||
type="number"
|
||||
fullWidth
|
||||
value={form.kraftstoff_liter ?? ''}
|
||||
onChange={(e) => setForm((f) => ({ ...f, kraftstoff_liter: e.target.value ? Number(e.target.value) : undefined }))}
|
||||
inputProps={{ min: 0, step: 0.1 }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={4}>
|
||||
<TextField
|
||||
label="Kosten (€)"
|
||||
type="number"
|
||||
fullWidth
|
||||
value={form.kosten ?? ''}
|
||||
onChange={(e) => setForm((f) => ({ ...f, kosten: e.target.value ? Number(e.target.value) : undefined }))}
|
||||
inputProps={{ min: 0, step: 0.01 }}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
label="Externe Werkstatt"
|
||||
fullWidth
|
||||
@@ -663,6 +701,31 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
|
||||
placeholder="Name der Werkstatt (wenn extern)"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<FormControl fullWidth>
|
||||
<InputLabel>Ergebnis</InputLabel>
|
||||
<Select
|
||||
label="Ergebnis"
|
||||
value={form.ergebnis ?? ''}
|
||||
onChange={(e) => setForm((f) => ({ ...f, ergebnis: (e.target.value || undefined) as WartungslogErgebnis | undefined }))}
|
||||
>
|
||||
<MenuItem value="">— Kein Ergebnis —</MenuItem>
|
||||
<MenuItem value="bestanden">Bestanden</MenuItem>
|
||||
<MenuItem value="bestanden_mit_maengeln">Bestanden mit Mängeln</MenuItem>
|
||||
<MenuItem value="nicht_bestanden">Nicht bestanden</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12} sm={6}>
|
||||
<TextField
|
||||
label="Nächste Fälligkeit"
|
||||
type="date"
|
||||
fullWidth
|
||||
value={form.naechste_faelligkeit ?? ''}
|
||||
onChange={(e) => setForm((f) => ({ ...f, naechste_faelligkeit: e.target.value }))}
|
||||
InputLabelProps={{ shrink: true }}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
|
||||
Reference in New Issue
Block a user