Fahrzeugdetails war nie technisch auf Fahrzeuge beschränkt, aber es gab keinen Weg, den Zählerstand überhaupt zu setzen (nur Formular-Ersatz-PUT ohne Validierung, keine Frontend-Anbindung). Neuer PATCH-Endpunkt mit Monoton-Validierung (Rückwärtslauf nur Administrator), neues ZaehlerstandPanel in der Akte - bewusst ohne Fahrzeug-Gate, damit Stromerzeuger/Pumpen denselben Mechanismus nutzen können. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDMvneg6wKfXdXvTYNd3dN
87 lines
2.9 KiB
TypeScript
87 lines
2.9 KiB
TypeScript
import { useState } from "react";
|
||
|
||
import { apiRequest, ApiError } from "../api/client";
|
||
import type { Fahrzeugdetails } from "../api/types";
|
||
|
||
interface Props {
|
||
objektId: number;
|
||
fahrzeugdetails: Fahrzeugdetails | null;
|
||
onAktualisiert: () => Promise<void>;
|
||
}
|
||
|
||
// ASSET-009: generischer Nutzungszähler - bewusst NICHT auf Fahrzeuge
|
||
// beschränkt (siehe Kachel-Nachtrag), deshalb hier ohne istFahrzeug-Prüfung
|
||
// immer angezeigt. Kilometerstand bleibt bei einem Nicht-Fahrzeug (z.B.
|
||
// Stromerzeuger) einfach ungenutzt/leer.
|
||
export function ZaehlerstandPanel({ objektId, fahrzeugdetails, onAktualisiert }: Props) {
|
||
const [kilometerstand, setKilometerstand] = useState("");
|
||
const [betriebsstunden, setBetriebsstunden] = useState("");
|
||
const [wirdGespeichert, setWirdGespeichert] = useState(false);
|
||
const [fehler, setFehler] = useState<string | null>(null);
|
||
|
||
async function melden() {
|
||
if (!kilometerstand && !betriebsstunden) return;
|
||
setWirdGespeichert(true);
|
||
setFehler(null);
|
||
try {
|
||
await apiRequest(`/objekte/${objektId}/zaehlerstand`, {
|
||
method: "PATCH",
|
||
body: {
|
||
kilometerstand: kilometerstand ? Number(kilometerstand) : null,
|
||
betriebsstunden: betriebsstunden || null,
|
||
},
|
||
});
|
||
setKilometerstand("");
|
||
setBetriebsstunden("");
|
||
await onAktualisiert();
|
||
} catch (e) {
|
||
if (e instanceof ApiError && e.status === 409) {
|
||
setFehler("Wert liegt unter dem bisherigen Zählerstand - nur Administratoren dürfen zurücksetzen.");
|
||
} else {
|
||
setFehler("Zählerstand konnte nicht gespeichert werden.");
|
||
}
|
||
} finally {
|
||
setWirdGespeichert(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<div className="card">
|
||
<h3 style={{ marginTop: 0 }}>Zählerstand</h3>
|
||
<div style={{ marginBottom: "0.5rem" }}>
|
||
<span className="text-muted">Kilometerstand: </span>
|
||
{fahrzeugdetails?.kilometerstand ?? "–"} km
|
||
<span className="text-muted" style={{ marginLeft: "1rem" }}>
|
||
Betriebsstunden:{" "}
|
||
</span>
|
||
{fahrzeugdetails?.betriebsstunden ?? "–"} h
|
||
</div>
|
||
<div className="row" style={{ flexWrap: "wrap", gap: "0.5rem" }}>
|
||
<input
|
||
className="input"
|
||
type="number"
|
||
placeholder="Neuer Kilometerstand"
|
||
value={kilometerstand}
|
||
onChange={(e) => setKilometerstand(e.target.value)}
|
||
/>
|
||
<input
|
||
className="input"
|
||
type="number"
|
||
step="0.1"
|
||
placeholder="Neue Betriebsstunden"
|
||
value={betriebsstunden}
|
||
onChange={(e) => setBetriebsstunden(e.target.value)}
|
||
/>
|
||
<button
|
||
className="btn btn-primary"
|
||
onClick={melden}
|
||
disabled={wirdGespeichert || (!kilometerstand && !betriebsstunden)}
|
||
>
|
||
Melden
|
||
</button>
|
||
</div>
|
||
{fehler && <p style={{ color: "var(--color-danger)", marginBottom: 0 }}>{fehler}</p>}
|
||
</div>
|
||
);
|
||
}
|