diff --git a/frontend/src/pages/admin/ObjektAnlegenFormular.tsx b/frontend/src/pages/admin/ObjektAnlegenFormular.tsx new file mode 100644 index 0000000..9b45605 --- /dev/null +++ b/frontend/src/pages/admin/ObjektAnlegenFormular.tsx @@ -0,0 +1,145 @@ +import { useState } from "react"; + +import { apiRequest } from "../../api/client"; +import type { Beladungsvorlage, Objekt, Objekttyp, Standort } from "../../api/types"; + +interface Props { + objekte: Objekt[]; + objekttypen: Objekttyp[]; + vorlagen: Beladungsvorlage[]; + standorte: Standort[]; + onGeaendert: () => Promise; + onFehler: (text: string) => void; +} + +export function ObjektAnlegenFormular({ objekte, objekttypen, vorlagen, standorte, onGeaendert, onFehler }: Props) { + const [praefix, setPraefix] = useState(""); + const [code, setCode] = useState(""); + const [name, setName] = useState(""); + const [vorlageId, setVorlageId] = useState(""); + const [standortId, setStandortId] = useState(""); + const [fahrzeugId, setFahrzeugId] = useState(""); + const [wirdAngelegt, setWirdAngelegt] = useState(false); + + // Option A+C (Karte 11 Nachtrag): Dropdown zeigt nur Zugfahrzeuge (Objekttyp- + // Flag ist_zugfahrzeug) ohne eigene Kopplung - serverseitig zusätzlich per + // 422 erzwungen, das ist hier nur die Vorauswahl fürs Formular. + const zugfahrzeuge = objekte.filter((x) => { + const typ = objekttypen.find((t) => t.id === x.objekttyp_id); + return typ?.ist_zugfahrzeug && x.fahrzeug_id === null; + }); + + async function codeVorschlagen() { + if (!praefix) { + onFehler("Präfix eingeben (z. B. RK, FZ)."); + return; + } + try { + const antwort = await apiRequest<{ code: string }>( + `/objekte/naechster-code?praefix=${encodeURIComponent(praefix.toUpperCase())}` + ); + setCode(antwort.code); + } catch { + onFehler("Code-Vorschlag fehlgeschlagen."); + } + } + + async function anlegen() { + if (vorlageId === "" || standortId === "") { + onFehler("Vorlage und Standort auswählen."); + return; + } + setWirdAngelegt(true); + const vorlage = vorlagen.find((v) => v.id === vorlageId); + try { + await apiRequest("/objekte", { + method: "POST", + body: { + code, + name, + objekttyp_id: vorlage?.objekttyp_id, + vorlage_id: vorlageId, + standort_id: standortId, + fahrzeug_id: fahrzeugId === "" ? null : fahrzeugId, + }, + }); + setPraefix(""); + setCode(""); + setName(""); + setFahrzeugId(""); + await onGeaendert(); + } catch { + onFehler("Objekt konnte nicht angelegt werden (Code evtl. schon vergeben)."); + } finally { + setWirdAngelegt(false); + } + } + + return ( +
+

Objekt aus Vorlage anlegen

+ {vorlagen.length === 0 ? ( +

Keine aktive Vorlage vorhanden – erst Excel-Import oder Vorlage per API anlegen.

+ ) : ( +
+
+ + +
+
+ + +
+
+ +
+ setPraefix(e.target.value)} + placeholder="Präfix (RK, FZ…)" + /> + + setCode(e.target.value)} /> +
+
+
+ + setName(e.target.value)} /> +
+
+ + +
+ +
+ )} +
+ ); +} diff --git a/frontend/src/pages/admin/ObjektSection.tsx b/frontend/src/pages/admin/ObjektSection.tsx index 7ce8322..02f2c12 100644 --- a/frontend/src/pages/admin/ObjektSection.tsx +++ b/frontend/src/pages/admin/ObjektSection.tsx @@ -1,9 +1,9 @@ import { useState } from "react"; +import { Link } from "react-router-dom"; import { apiRequest } from "../../api/client"; -import type { Beladungsvorlage, Fahrzeugdetails, Material, Objekt, Objekttyp, Standort } from "../../api/types"; -import { DokumentePanel } from "../../components/DokumentePanel"; -import { ObjektPositionenPanel } from "./ObjektPositionenPanel"; +import type { Beladungsvorlage, Material, Objekt, Objekttyp, Standort } from "../../api/types"; +import { ObjektAnlegenFormular } from "./ObjektAnlegenFormular"; const STATUS_OPTIONEN = [ { wert: "aktiv", label: "Aktiv" }, @@ -21,7 +21,8 @@ interface Props { // (Ablauf/Charge/SN/Prüfintervall/Geräte-Instanzen), nicht Objekte anlegen/ // duplizieren/koppeln (require_roles(administration) dort) - Anlegen-Formular // und Duplizieren/Kopplung blenden wir entsprechend aus statt sie ins Leere - // laufen zu lassen (403). + // laufen zu lassen (403). Feinpflege (Ablauf/Charge/SN) läuft jetzt über die + // Akte, nicht mehr hier - "nurPflegen" hat daher hier nichts mehr zu tun. nurPflegen?: boolean; onGeaendert: () => Promise; onFehler: (text: string) => void; @@ -32,20 +33,10 @@ export function ObjektSection({ objekttypen, vorlagen, standorte, - materialien, nurPflegen = false, onGeaendert, onFehler, }: Props) { - const [praefix, setPraefix] = useState(""); - const [code, setCode] = useState(""); - const [name, setName] = useState(""); - const [vorlageId, setVorlageId] = useState(""); - const [standortId, setStandortId] = useState(""); - const [fahrzeugId, setFahrzeugId] = useState(""); - const [wirdAngelegt, setWirdAngelegt] = useState(false); - const [ausgeklapptId, setAusgeklapptId] = useState(null); - const [dokumenteOffenId, setDokumenteOffenId] = useState(null); const [duplizierId, setDuplizierId] = useState(null); const [neuerPraefix, setNeuerPraefix] = useState(""); const [neuerCode, setNeuerCode] = useState(""); @@ -54,8 +45,6 @@ export function ObjektSection({ const [fahrzeugBearbeitenId, setFahrzeugBearbeitenId] = useState(null); const [fahrzeugAuswahl, setFahrzeugAuswahl] = useState(""); const [suche, setSuche] = useState(""); - const [detailsOffenId, setDetailsOffenId] = useState(null); - const [details, setDetails] = useState>({}); const [verschiebenId, setVerschiebenId] = useState(null); const [zielStandortId, setZielStandortId] = useState(""); const [verschiebenGrund, setVerschiebenGrund] = useState(""); @@ -96,45 +85,6 @@ export function ObjektSection({ } } - async function fahrzeugdetailsOeffnen(o: Objekt) { - if (detailsOffenId === o.id) { - setDetailsOffenId(null); - return; - } - setDetailsOffenId(o.id); - try { - const daten = await apiRequest(`/objekte/${o.id}/fahrzeugdetails`); - setDetails(daten); - } catch { - setDetails({}); - } - } - - async function fahrzeugdetailsSpeichern(objektId: number) { - try { - await apiRequest(`/objekte/${objektId}/fahrzeugdetails`, { - method: "PUT", - body: { - kennzeichen: details.kennzeichen || null, - funkrufname: details.funkrufname || null, - funkkenner: details.funkkenner || null, - baujahr: details.baujahr || null, - kilometerstand: details.kilometerstand || null, - betriebsstunden: details.betriebsstunden || null, - naechste_hu: details.naechste_hu || null, - naechste_uvv: details.naechste_uvv || null, - }, - }); - setDetailsOffenId(null); - onFehler(""); - } catch { - onFehler("Fahrzeugdetails konnten nicht gespeichert werden."); - } - } - - // Option A+C (Karte 11 Nachtrag): Dropdown zeigt nur Zugfahrzeuge (Objekttyp- - // Flag ist_zugfahrzeug) ohne eigene Kopplung - serverseitig zusätzlich per - // 422 erzwungen, das ist hier nur die Vorauswahl fürs Formular. const zugfahrzeuge = objekte.filter((x) => { const typ = objekttypen.find((t) => t.id === x.objekttyp_id); return typ?.ist_zugfahrzeug && x.fahrzeug_id === null; @@ -168,21 +118,6 @@ export function ObjektSection({ } } - async function codeVorschlagen() { - if (!praefix) { - onFehler("Präfix eingeben (z. B. RK, FZ)."); - return; - } - try { - const antwort = await apiRequest<{ code: string }>( - `/objekte/naechster-code?praefix=${encodeURIComponent(praefix.toUpperCase())}` - ); - setCode(antwort.code); - } catch { - onFehler("Code-Vorschlag fehlgeschlagen."); - } - } - async function neuerCodeVorschlagen() { if (!neuerPraefix) { onFehler("Präfix eingeben (z. B. RK, FZ)."); @@ -198,37 +133,6 @@ export function ObjektSection({ } } - async function anlegen() { - if (vorlageId === "" || standortId === "") { - onFehler("Vorlage und Standort auswählen."); - return; - } - setWirdAngelegt(true); - const vorlage = vorlagen.find((v) => v.id === vorlageId); - try { - await apiRequest("/objekte", { - method: "POST", - body: { - code, - name, - objekttyp_id: vorlage?.objekttyp_id, - vorlage_id: vorlageId, - standort_id: standortId, - fahrzeug_id: fahrzeugId === "" ? null : fahrzeugId, - }, - }); - setPraefix(""); - setCode(""); - setName(""); - setFahrzeugId(""); - await onGeaendert(); - } catch { - onFehler("Objekt konnte nicht angelegt werden (Code evtl. schon vergeben)."); - } finally { - setWirdAngelegt(false); - } - } - function duplizierenStarten(o: Objekt) { setDuplizierId(duplizierId === o.id ? null : o.id); setNeuerPraefix(""); @@ -265,334 +169,171 @@ export function ObjektSection({

Objekte

- {!nurPflegen && ( -
-

Objekt aus Vorlage anlegen

- {vorlagen.length === 0 ? ( -

- Keine aktive Vorlage vorhanden – erst Excel-Import oder Vorlage per API anlegen. -

- ) : ( -
-
- - -
-
- - -
-
- -
- setPraefix(e.target.value)} - placeholder="Präfix (RK, FZ…)" - /> - - setCode(e.target.value)} /> -
-
-
- - setName(e.target.value)} /> -
-
- - -
- -
+ {!nurPflegen && ( + )} -
- )} -
- setSuche(e.target.value)} - /> - {gefilterteObjekte.length === 0 &&

Keine Treffer.

} - {gefilterteObjekte.length > 0 && ( -
    - {gefilterteObjekte.map((o) => ( -
  • -
    - - {o.name} ({o.code}) - {o.fahrzeug_id !== null && ( - – Fahrzeug: {objektName(o.fahrzeug_id)} - )} - - {" "} - –{" "} - {o.letzte_kontrolle_am - ? `zuletzt kontrolliert ${new Date(o.letzte_kontrolle_am).toLocaleDateString("de-DE")}` - : "noch nie kontrolliert"} +
    + setSuche(e.target.value)} + /> + {gefilterteObjekte.length === 0 &&

    Keine Treffer.

    } + {gefilterteObjekte.length > 0 && ( +
      + {gefilterteObjekte.map((o) => ( +
    • +
      + + {o.name} ({o.code}) + {o.fahrzeug_id !== null && ( + – Fahrzeug: {objektName(o.fahrzeug_id)} + )} + + {" "} + –{" "} + {o.letzte_kontrolle_am + ? `zuletzt kontrolliert ${new Date(o.letzte_kontrolle_am).toLocaleDateString("de-DE")}` + : "noch nie kontrolliert"} + - -
      - {!nurPflegen && ( +
      + + Akte ansehen + + {!nurPflegen && ( + + )} + {!nurPflegen && ( + + )} + {!nurPflegen && ( + + )} + {!nurPflegen && ( + + )} +
      +
      + {verschiebenId === o.id && ( +
      - )} - {objekttypen.find((t) => t.id === o.objekttyp_id)?.ist_zugfahrzeug && ( - - )} - {!nurPflegen && ( - - )} - - - {!nurPflegen && ( - - )} - {!nurPflegen && ( - - )} -
      -
      - {detailsOffenId === o.id && ( -
      - setDetails((d) => ({ ...d, kennzeichen: e.target.value }))} - /> - setDetails((d) => ({ ...d, funkrufname: e.target.value }))} - /> - setDetails((d) => ({ ...d, funkkenner: e.target.value }))} - /> -
      setDetails((d) => ({ ...d, baujahr: Number(e.target.value) || undefined }))} + value={verschiebenGrund} + onChange={(e) => setVerschiebenGrund(e.target.value)} + placeholder="Grund (optional)" + /> + +
      + )} + {fahrzeugBearbeitenId === o.id && ( +
      + + +
      + )} + {duplizierId === o.id && ( +
      + setNeuerPraefix(e.target.value)} + placeholder="Präfix (RK, FZ…)" + /> + + setNeuerCode(e.target.value)} + placeholder="Neuer Code (eindeutig)" /> - setDetails((d) => ({ ...d, kilometerstand: Number(e.target.value) || undefined })) - } + value={neuerName} + onChange={(e) => setNeuerName(e.target.value)} + placeholder="Neuer Name" /> -
      -
      -
      - - setDetails((d) => ({ ...d, naechste_hu: e.target.value }))} - /> -
      -
      - - setDetails((d) => ({ ...d, naechste_uvv: e.target.value }))} - /> -
      -
      - -
      - )} - {verschiebenId === o.id && ( -
      - setNeuerStandortId(Number(e.target.value) || "")} + > + {standorte.map((s) => ( ))} - - setVerschiebenGrund(e.target.value)} - placeholder="Grund (optional)" - /> - -
      - )} - {fahrzeugBearbeitenId === o.id && ( -
      - - -
      - )} - {ausgeklapptId === o.id && ( -
      - -
      - )} - {dokumenteOffenId === o.id && ( -
      - -
      - )} - {duplizierId === o.id && ( -
      - setNeuerPraefix(e.target.value)} - placeholder="Präfix (RK, FZ…)" - /> - - setNeuerCode(e.target.value)} - placeholder="Neuer Code (eindeutig)" - /> - setNeuerName(e.target.value)} - placeholder="Neuer Name" - /> - - -
      - )} -
    • - ))} -
    - )} -
    + + +
    + )} +
  • + ))} +
+ )} +
);