Karte 14 Schritt 2 (Admin-Teil) + Frontend für Anhänger-Kopplung
CI / backend-tests (push) Failing after 1m21s
CI / frontend-build (push) Successful in 22s

Backend: geraet_instanz-CRUD (Liste/Anlegen/Status ändern inkl. optionalem
Fehlbestand pro Gerät/Löschen), pruefintervall_monate in Objektposition-
Schemas verdrahtet (Prüfdatum -> naechste_pruefung wird automatisch
berechnet, Monatsrechnung ohne neue Dependency).

Frontend: GeraeteInstanzenListe ersetzt das Einzel-SN-Feld im Admin-Portal
(ObjektPositionenPanel) durch eine Instanz-Liste mit Status/Prüfdatum/
Bemerkung je Exemplar - löst das Karte-14-Kernproblem (mehrere Geräte pro
Position). ObjektSection: Anhänger-Kopplung bei Anlage und nachträglich
änderbar.

Kontroll-Erfassung (PositionCard/KontrollPage) bleibt unverändert - die
komplett neue Erfassungs-UI für geraet_sn (Umsetzungsschritt 4, Karte 14)
ist bewusst nicht Teil dieses Commits.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
This commit is contained in:
2026-09-04 20:55:09 +02:00
co-authored by Claude Sonnet 5
parent eff9fe4956
commit 6934276e8a
14 changed files with 685 additions and 7 deletions
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { apiRequest } from "../../api/client";
import type { Material, Objektposition } from "../../api/types";
import { GeraeteInstanzenListe } from "./GeraeteInstanzenListe";
interface Props {
objektId: number;
@@ -12,14 +13,14 @@ interface Props {
interface ZeilenFormular {
ablaufdatum: string;
chargennummer: string;
seriennummer: string;
pruefintervallMonate: string;
}
function leeresFormular(p: Objektposition): ZeilenFormular {
return {
ablaufdatum: p.ablaufdatum ?? "",
chargennummer: p.chargennummer ?? "",
seriennummer: p.seriennummer ?? "",
pruefintervallMonate: p.pruefintervall_monate?.toString() ?? "",
};
}
@@ -62,7 +63,7 @@ export function ObjektPositionenPanel({ objektId, materialien, onFehler }: Props
body: {
ablaufdatum: formular.ablaufdatum || null,
chargennummer: formular.chargennummer || null,
seriennummer: formular.seriennummer || null,
pruefintervall_monate: formular.pruefintervallMonate ? Number(formular.pruefintervallMonate) : null,
},
});
setPositionen((bisherige) => bisherige.map((p) => (p.id === position.id ? aktualisiert : p)));
@@ -113,18 +114,30 @@ export function ObjektPositionenPanel({ objektId, materialien, onFehler }: Props
)}
{typ === "geraet_sn" && (
<input
type="number"
min="0"
className="input"
placeholder="Seriennummer"
value={formular.seriennummer}
placeholder="Prüfintervall (Monate)"
value={formular.pruefintervallMonate}
onChange={(e) =>
setFormulare((f) => ({ ...f, [p.id]: { ...formular, seriennummer: e.target.value } }))
setFormulare((f) => ({
...f,
[p.id]: { ...formular, pruefintervallMonate: e.target.value },
}))
}
/>
)}
<button className="btn btn-primary" onClick={() => speichern(p)} disabled={speichertId === p.id}>
Speichern
{typ === "geraet_sn" ? "Prüfintervall speichern" : "Speichern"}
</button>
</div>
{typ === "geraet_sn" && (
<GeraeteInstanzenListe
objektpositionId={p.id}
pruefintervallMonate={p.pruefintervall_monate}
onFehler={onFehler}
/>
)}
</li>
);
})}