new features

This commit is contained in:
Matthias Hochmeister
2026-03-23 14:01:39 +01:00
parent d2dc64d54a
commit 3326156b15
35 changed files with 1341 additions and 257 deletions

View File

@@ -43,6 +43,7 @@ import {
DirectionsCar,
Edit,
Error as ErrorIcon,
History,
LocalFireDepartment,
MoreHoriz,
PauseCircle,
@@ -121,6 +122,58 @@ function fmtDatetime(iso: string | Date | null | undefined): string {
return fmtDate(iso ? new Date(iso).toISOString() : null);
}
// ── Status History Section ────────────────────────────────────────────────────
const StatusHistorySection: React.FC<{ vehicleId: string }> = ({ vehicleId }) => {
const [history, setHistory] = useState<{ alter_status: string; neuer_status: string; bemerkung?: string; geaendert_von_name?: string; erstellt_am: string }[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
vehiclesApi.getStatusHistory(vehicleId)
.then(setHistory)
.catch(() => setHistory([]))
.finally(() => setLoading(false));
}, [vehicleId]);
if (loading || history.length === 0) return null;
return (
<>
<Typography variant="h6" sx={{ mt: 3, mb: 1.5, display: 'flex', alignItems: 'center', gap: 1 }}>
<History fontSize="small" /> Status-Historie
</Typography>
<TableContainer component={Paper} variant="outlined">
<Table size="small">
<TableHead>
<TableRow>
<TableCell>Datum</TableCell>
<TableCell>Von</TableCell>
<TableCell>Nach</TableCell>
<TableCell>Bemerkung</TableCell>
<TableCell>Geändert von</TableCell>
</TableRow>
</TableHead>
<TableBody>
{history.map((h, idx) => (
<TableRow key={idx}>
<TableCell>{fmtDatetime(h.erstellt_am)}</TableCell>
<TableCell>
<Chip size="small" label={FahrzeugStatusLabel[h.alter_status as FahrzeugStatus] || h.alter_status} color={STATUS_CHIP_COLOR[h.alter_status as FahrzeugStatus] || 'default'} />
</TableCell>
<TableCell>
<Chip size="small" label={FahrzeugStatusLabel[h.neuer_status as FahrzeugStatus] || h.neuer_status} color={STATUS_CHIP_COLOR[h.neuer_status as FahrzeugStatus] || 'default'} />
</TableCell>
<TableCell>{h.bemerkung || '—'}</TableCell>
<TableCell>{h.geaendert_von_name || '—'}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</>
);
};
// ── Übersicht Tab ─────────────────────────────────────────────────────────────
interface UebersichtTabProps {
@@ -148,7 +201,7 @@ const UebersichtTab: React.FC<UebersichtTabProps> = ({ vehicle, onStatusUpdated,
const openDialog = () => {
setNewStatus(vehicle.status);
setBemerkung(vehicle.status_bemerkung ?? '');
setBemerkung('');
setAusserDienstVon(
vehicle.ausser_dienst_von ? new Date(vehicle.ausser_dienst_von).toISOString().slice(0, 16) : ''
);
@@ -323,6 +376,9 @@ const UebersichtTab: React.FC<UebersichtTabProps> = ({ vehicle, onStatusUpdated,
})}
</Grid>
{/* Status history */}
<StatusHistorySection vehicleId={vehicle.id} />
{/* Status change dialog */}
<Dialog open={statusDialogOpen} onClose={closeDialog} maxWidth="sm" fullWidth>
<DialogTitle>Fahrzeugstatus ändern</DialogTitle>
@@ -475,6 +531,42 @@ const WartungTab: React.FC<WartungTabProps> = ({ fahrzeugId, wartungslog, onAdde
entry.externe_werkstatt && entry.externe_werkstatt,
].filter(Boolean).join(' · ')}
</Typography>
{entry.dokument_url ? (
<Chip
label="Dokument"
size="small"
color="info"
variant="outlined"
component="a"
href={`/api/uploads/${entry.dokument_url.split('/uploads/')[1] || entry.dokument_url}`}
target="_blank"
clickable
sx={{ mt: 0.5 }}
/>
) : canWrite ? (
<Button
size="small"
component="label"
sx={{ mt: 0.5, textTransform: 'none', fontSize: '0.75rem' }}
>
Dokument hochladen
<input
type="file"
hidden
accept=".pdf,.doc,.docx,.jpg,.png"
onChange={async (e) => {
const file = e.target.files?.[0];
if (!file) return;
try {
await vehiclesApi.uploadWartungFile(entry.id, file);
onAdded();
} catch {
// silent fail — user can retry
}
}}
/>
</Button>
) : null}
</Box>
</Box>
);