aether-framework/resources/[aether]/aether-medical/html/js/module/fahrzeug.js
Jerrit Fritzsche e4b82dd685 Aether-Framework: Fachmodule, Admin-Panel und Docker-Datenbank
- aether-medical: Stationen/Belegung, Intensivstation, Blutbank, Fuhrpark,
  Nachrichten/Aushänge, Berichte und Textbausteine (server + NUI)
- aether-admin: vollständiges Server-Admin-Panel als echtes FiveM-Resource,
  an aether-core gekoppelt (Rechte über aether_users.admin_group), mit
  WebRTC-Live-Kameras (Bildschirmfreigabe) und separatem Media-Server (SFU)
- Prototypen admin/ vervollständigt
- SQL-Schema aether_admin.sql
- Docker-Compose mit MariaDB und idempotenter Auto-Migration (schema_migrations)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-27 18:55:32 +02:00

622 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* =====================================================================
aether-medical / Ansicht: Fuhrpark
Zwei Sorten von Angaben, streng getrennt dargestellt:
GEMELDET (Tank, Zustand, Position) kommt aus dem Spiel und trägt
immer sein Alter mit sich. EINGETRAGEN (Status, Wartung, Zuweisung)
stammt von Menschen. Kein gemeldeter Wert ändert einen eingetragenen.
===================================================================== */
"use strict";
(() => {
const { sicher, zeit, minuten, aufruf, meldung, fenster, bestaetigen, anClient } = App;
const z = {
ordner: "dienst", // dienst | werkstatt | ausser
art: "", // Filter auf eine Fahrzeugart
daten: null,
};
// Marker-Art je Einsatzstatus
const statusArt = {
verfuegbar: "gut", im_einsatz: "warn",
werkstatt: "info", ausser_dienst: "neutral",
};
/* ---- Kleine Helfer ---------------------------------------------------- */
/** Anzeigename aus einer Konfigurationsliste */
function nameAus(liste, kennung) {
const treffer = (liste || []).find((e) => e.id === kennung);
return treffer ? treffer.name : kennung;
}
/** Farbe eines Balkens nach seinem Wert */
function balkenFarbe(wert, grenze) {
if (wert === null || wert === undefined) return "var(--text-leise)";
if (wert <= grenze / 2) return "var(--kritisch)";
if (wert <= grenze) return "var(--warn)";
return "var(--gut)";
}
/**
* Ein Balken für einen gemeldeten Wert.
* Ist die Meldung alt, wird der Balken blass — der Wert sagt dann
* nichts über den jetzigen Zustand.
*/
function balken(titel, wert, grenze, veraltet) {
const hat = wert !== null && wert !== undefined;
return `<div class="fz-wert ${veraltet ? "alt" : ""}">
<div class="fz-wert-kopf">
<span>${sicher(titel)}</span>
<b>${hat ? wert + " %" : "nie gemeldet"}</b>
</div>
<div class="fz-balken">
<i style="width:${hat ? wert : 0}%; background:${balkenFarbe(wert, grenze)}"></i>
</div>
</div>`;
}
/* ---- Gerüst ------------------------------------------------------------ */
function reiterleiste(d) {
const eintraege = [
{ id: "dienst", text: "Im Dienst", zahl: d.zaehler.verfuegbar + d.zaehler.imEinsatz },
{ id: "werkstatt", text: "Wartung", zahl: d.zaehler.werkstatt },
{ id: "ausser", text: "Außer Dienst", zahl: d.zaehler.ausserDienst },
];
return `<div class="reiter">
${eintraege.map((e) => `
<button class="${z.ordner === e.id ? "aktiv" : ""}" data-reiter="${e.id}">
${sicher(e.text)}<span class="mdt-zahl">${e.zahl}</span>
</button>`).join("")}
</div>`;
}
function kennzahlen(d) {
const zk = d.zaehler;
return `<div class="raster k4">
<div class="kennzahl ${zk.verfuegbar === 0 ? "hoch" : "gut"}">
<div class="kz-titel">Verfügbar</div>
<div class="kz-wert">${zk.verfuegbar}</div>
<div class="kz-zusatz">so eingetragen</div>
</div>
<div class="kennzahl">
<div class="kz-titel">Im Einsatz</div>
<div class="kz-wert">${zk.imEinsatz}</div>
<div class="kz-zusatz">${zk.zugewiesen} Fahrzeuge zugewiesen</div>
</div>
<div class="kennzahl ${zk.werkstatt > 0 ? "warn" : ""}">
<div class="kz-titel">Wartung offen</div>
<div class="kz-wert">${zk.werkstatt}</div>
<div class="kz-zusatz">Werkstatt oder fällig</div>
</div>
<div class="kennzahl">
<div class="kz-titel">Außer Dienst</div>
<div class="kz-wert">${zk.ausserDienst}</div>
<div class="kz-zusatz">fahren nicht</div>
</div>
</div>`;
}
/* ---- Übersicht ---------------------------------------------------------- */
function uebersicht(behaelter) {
const d = z.daten;
behaelter.innerHTML = `
<div class="seiten-kopf">
<div>
<h2>Fuhrpark</h2>
<p>Tank, Zustand und Position werden gemeldet, während jemand fährt.
Status, Wartung und Zuweisung trägt ein Mensch ein — das eine ändert
das andere nie.</p>
</div>
${d.rechte.pflegen
? `<button class="knopf" data-neu="1">+ Fahrzeug aufnehmen</button>` : ""}
</div>
${kennzahlen(d)}
${reiterleiste(d)}
<div class="filterleiste">
<select data-artfilter="1">
<option value="">Alle Fahrzeugarten</option>
${d.arten.map((a) =>
`<option value="${sicher(a.id)}" ${a.id === z.art ? "selected" : ""}>
${sicher(a.name)}</option>`).join("")}
</select>
<span class="feld-hinweis">Eine Meldung älter als ${d.grenzen.meldung} Minuten
wird blass dargestellt.</span>
</div>
${d.liste.length
? `<div class="fz-raster">${d.liste.map((f) => karte(f, d)).join("")}</div>`
: `<div class="karte"><div class="liste-leer">Kein Fahrzeug in diesem Ordner.</div></div>`}
`;
verdrahteUebersicht(behaelter);
}
function karte(f, d) {
return `<div class="fz-karte status-${sicher(f.status)}" data-oeffne="${f.id}">
<div class="fz-kopf">
<div>
<div class="fz-name">${sicher(f.bezeichnung)}</div>
<div class="fz-kennzeichen">${sicher(f.kennzeichen)}</div>
</div>
<span class="marker ${statusArt[f.status] || "neutral"}">
${sicher(f.statusName)}</span>
</div>
<div class="fz-zeile">
<span class="marker neutral">${sicher(f.artName)}</span>
${f.wartung !== "ok"
? `<span class="marker warn">${sicher(f.wartungName)}</span>` : ""}
${f.zugewiesenAn
? `<span class="marker info">${sicher(f.zugewiesenAn)}</span>` : ""}
</div>
<div class="fz-werte">
${balken("Tank", f.tank, d.grenzen.tank, f.meldungVeraltet)}
${balken("Zustand", f.zustand, d.grenzen.zustand, f.meldungVeraltet)}
</div>
<div class="fz-fuss">
${f.gemeldet_am
? `gemeldet vor ${sicher(minuten(f.meldungAlter))}${
f.letzterFahrer ? " · " + sicher(f.letzterFahrer) : ""}`
: "noch nie gemeldet"}
${f.notiz ? ` · ${sicher(f.notiz)}` : ""}
</div>
</div>`;
}
function verdrahteUebersicht(behaelter) {
behaelter.querySelectorAll("[data-reiter]").forEach((el) => {
el.addEventListener("click", () => {
z.ordner = el.dataset.reiter;
rendern(behaelter);
});
});
behaelter.querySelector("[data-artfilter]").addEventListener("change", (e) => {
z.art = e.target.value;
rendern(behaelter);
});
behaelter.querySelectorAll("[data-oeffne]").forEach((el) => {
el.addEventListener("click", () => fahrzeugFenster(behaelter, +el.dataset.oeffne));
});
const neu = behaelter.querySelector("[data-neu]");
if (neu) neu.addEventListener("click", () => bearbeitenFenster(behaelter, null));
}
/* ---- Einzelnes Fahrzeug ---------------------------------------------------- */
async function fahrzeugFenster(behaelter, fahrzeugId) {
let d;
try {
d = await aufruf("fahrzeug", "oeffnen", { id: fahrzeugId });
} catch (f) { return meldung(f.message, "fehler"); }
const f = d.fahrzeug;
const ausstattung = String(f.ausstattung || "")
.split(",").map((t) => t.trim()).filter(Boolean);
const dialog = fenster({
titel: `${f.bezeichnung} · ${f.kennzeichen}`,
inhalt: `
${f.wartung !== "ok" ? `
<div class="warnung warn">
<span class="w-symbol">🔧</span>
<div><b>${sicher(f.wartungName)}</b>${f.notiz ? " — " + sicher(f.notiz) : ""}.
Eingetragen von Hand; ob das Fahrzeug fährt, steht im Einsatzstatus.</div>
</div>` : ""}
${f.meldungVeraltet ? `
<div class="warnung">
<span class="w-symbol">🕓</span>
<div>Die letzte Meldung ist ${sicher(minuten(f.meldungAlter))} alt.
Tank und Zustand sagen nichts über den jetzigen Stand.</div>
</div>` : ""}
<div class="werte-liste">
<div><span>Einsatzstatus</span><b>${sicher(f.statusName)}</b></div>
<div><span>Wartung</span><b>${sicher(f.wartungName)}</b></div>
<div><span>Art</span><b>${sicher(f.artName)}</b></div>
<div><span>Modell</span><b>${sicher(f.modell)}</b></div>
<div><span>Station</span><b>${sicher(f.station || "—")}</b></div>
<div><span>Zugewiesen an</span>
<b>${sicher(f.zugewiesenAn || "niemand")}</b></div>
</div>
<div class="karte-kopf" style="margin-top:14px">
<span class="karte-titel">Gemeldet</span>
<span class="feld-hinweis">${f.gemeldetAm
? "vor " + sicher(minuten(f.meldungAlter)) + " von "
+ sicher(f.letzterFahrer || "unbekannt")
: "noch nie"}</span>
</div>
<div class="fz-werte ${f.meldungVeraltet ? "alt" : ""}">
${balken("Tank", f.tank, 20, f.meldungVeraltet)}
${balken("Zustand", f.zustand, 60, f.meldungVeraltet)}
</div>
${f.position
? `<button class="knopf leise klein" data-wegpunkt="1"
style="margin-top:8px">Wegpunkt zur letzten Position</button>`
: `<div class="feld-hinweis" style="margin-top:8px">Keine Position gemeldet.</div>`}
<div class="karte-kopf" style="margin-top:14px">
<span class="karte-titel">Ausstattung</span>
<span class="feld-hinweis">notiert, nicht geprüft</span>
</div>
${ausstattung.length
? `<div class="fz-ausstattung">${ausstattung.map((a) =>
`<span class="fz-teil">${sicher(a)}</span>`).join("")}</div>`
: `<div class="beh-text leer">Nichts eingetragen.</div>`}
<div class="feld-reihe" style="margin-top:14px">
${d.rechte.zuweisen
? `<button class="knopf klein" data-status="1">Status setzen</button>
<button class="knopf leise klein" data-zuweisen="1">Zuweisen</button>` : ""}
${d.rechte.wartung
? `<button class="knopf leise klein" data-wartung="1">Wartung</button>` : ""}
${d.rechte.pflegen
? `<button class="knopf leise klein" data-bearbeiten="1">Bearbeiten</button>` : ""}
${d.rechte.pflegen && f.status === "ausser_dienst"
? `<button class="knopf gefahr klein" data-entfernen="1">Aus dem Fuhrpark nehmen</button>` : ""}
</div>
<div class="karte-kopf" style="margin-top:14px">
<span class="karte-titel">Eingetragene Vorgänge</span>
<span class="feld-hinweis">aus dem Protokoll, unveränderlich</span>
</div>
<div class="liste">
${d.spur.length ? d.spur.map((s) => `
<div class="liste-zeile">
<div class="lz-haupt">
<div class="lz-titel">${sicher(s.aktion)}</div>
<div class="lz-unter">${sicher(s.autor)}
${s.grund ? " · " + sicher(s.grund) : ""}</div>
</div>
<span class="db-zeit">${zeit(s.am)}</span>
</div>`).join("")
: `<div class="liste-leer">Noch kein Eintrag.</div>`}
</div>
`,
nachOeffnen: (hg) => {
const wegpunkt = hg.querySelector("[data-wegpunkt]");
if (wegpunkt) {
wegpunkt.addEventListener("click", () => {
anClient("wegpunkt", { x: f.position.x, y: f.position.y });
meldung("Wegpunkt zur letzten gemeldeten Position gesetzt.");
});
}
const knopf = (name, funktion) => {
const el = hg.querySelector(`[data-${name}]`);
if (el) el.addEventListener("click", () => { dialog.schliessen(); funktion(); });
};
knopf("status", () => statusFenster(behaelter, f, d));
knopf("zuweisen", () => zuweisenFenster(behaelter, f));
knopf("wartung", () => wartungFenster(behaelter, f, d));
knopf("bearbeiten", () => bearbeitenFenster(behaelter, f));
knopf("entfernen", () => entfernenFenster(behaelter, f));
},
});
}
/* ---- Einsatzstatus ----------------------------------------------------------- */
function statusFenster(behaelter, f, d) {
bestaetigen({
titel: "Einsatzstatus setzen",
inhalt: `
<p>Aktuell eingetragen: <b>${sicher(f.statusName)}</b></p>
<div class="feld"><label>Neuer Status</label>
<select name="status">
${d.status.map((s) =>
`<option value="${sicher(s.id)}" ${s.id === f.status ? "selected" : ""}>
${sicher(s.name)}</option>`).join("")}
</select>
<div class="feld-hinweis">Werkstatt und Außer Dienst darf nur eintragen,
wer die Wartung führt — und beides braucht einen Grund.</div></div>
<div class="feld"><label>Grund</label>
<input name="grund" maxlength="255"
placeholder="Warum ändert sich der Status?"></div>
${f.zugewiesenAn ? `
<div class="warnung warn">
<span class="w-symbol">🧍</span>
<div>Das Fahrzeug ist <b>${sicher(f.zugewiesenAn)}</b> zugewiesen.
Bei Werkstatt oder Außer Dienst wird das gemeldet — die Zuweisung
selbst bleibt bestehen.</div>
</div>` : ""}`,
bestaetigenText: "Status setzen",
bestaetigt: async (el, zu) => {
try {
await aufruf("fahrzeug", "status", {
id: f.id,
status: el.querySelector("[name=status]").value,
grund: el.querySelector("[name=grund]").value.trim(),
});
zu();
meldung("Status gesetzt.", "gut");
rendern(behaelter);
} catch (fehler) { meldung(fehler.message, "fehler"); }
},
});
}
/* ---- Zuweisung ------------------------------------------------------------------ */
async function zuweisenFenster(behaelter, f) {
let k;
try {
k = await aufruf("fahrzeug", "kollegen", {});
} catch (fehler) { return meldung(fehler.message, "fehler"); }
let gewaehlt = f.zugewiesen || null;
let aufheben = false;
bestaetigen({
titel: `${f.bezeichnung} zuweisen`,
inhalt: `
<p>Zugewiesen ist zurzeit: <b>${sicher(f.zugewiesenAn || "niemand")}</b></p>
<div class="warnung">
<span class="w-symbol">ℹ</span>
<div>Eine Zuweisung ist ein Eintrag im Fahrzeugbuch. Sie spawnt kein
Fahrzeug, schließt keines auf und gibt keinen Schlüssel aus.</div>
</div>
<div class="feld"><label>Im Dienst</label>
<div class="liste pat-trefferliste" data-kollegen="1">
${k.liste.length ? k.liste.map((p) => `
<div class="liste-zeile ${p.charakterId === f.zugewiesen ? "gewaehlt" : ""}"
data-person="${p.charakterId}">
<div class="lz-haupt">
<div class="lz-titel">${sicher(p.name)}
${p.selbst ? `<span class="marker info">du</span>` : ""}</div>
<div class="lz-unter">${sicher(p.rolleName)} ·
${sicher(p.personalnummer || "")}</div>
</div>
<span class="marker neutral">${sicher(p.funktion || "")}</span>
</div>`).join("")
: `<div class="liste-leer">Niemand im Dienst.</div>`}
</div></div>
<div class="feld"><label>
<input type="checkbox" class="haken" name="aufheben">
Zuweisung aufheben (niemand ist danach eingetragen)</label></div>
<div class="feld"><label>Vermerk / Grund</label>
<input name="grund" maxlength="255"
placeholder="Schichtübergabe, Fahrzeugwechsel…"></div>`,
bestaetigenText: "Eintragen",
bestaetigt: async (el, zu) => {
const grund = el.querySelector("[name=grund]").value.trim();
if (aufheben && !grund) {
return meldung("Bitte angeben, warum die Zuweisung entfällt.", "fehler");
}
if (!aufheben && !gewaehlt) {
return meldung("Bitte eine Person wählen.", "fehler");
}
try {
await aufruf("fahrzeug", "zuweisen", {
id: f.id,
charakter: aufheben ? null : gewaehlt,
grund,
});
zu();
meldung(aufheben ? "Zuweisung aufgehoben." : "Fahrzeug zugewiesen.", "gut");
rendern(behaelter);
} catch (fehler) { meldung(fehler.message, "fehler"); }
},
nachOeffnen: (hg) => {
hg.querySelectorAll("[data-person]").forEach((el) => {
el.addEventListener("click", () => {
gewaehlt = +el.dataset.person;
hg.querySelectorAll("[data-person]")
.forEach((zeile) => zeile.classList.remove("gewaehlt"));
el.classList.add("gewaehlt");
});
});
const haken = hg.querySelector("[name=aufheben]");
haken.addEventListener("change", () => {
aufheben = haken.checked;
hg.querySelector("[data-kollegen]").style.opacity = aufheben ? ".45" : "";
});
},
});
}
/* ---- Wartung ---------------------------------------------------------------------- */
function wartungFenster(behaelter, f, d) {
bestaetigen({
titel: "Wartungsstatus",
inhalt: `
<p>Aktuell eingetragen: <b>${sicher(f.wartungName)}</b></p>
<div class="warnung">
<span class="w-symbol">ℹ</span>
<div>Ein gemeldeter Schaden setzt diesen Status nicht. Was das Spiel
meldet, ist eine Beobachtung — ob gewartet wird, beurteilst du.</div>
</div>
<div class="feld"><label>Wartungsstatus</label>
<select name="wartung">
${d.wartungsstatus.map((s) =>
`<option value="${sicher(s.id)}" ${s.id === f.wartung ? "selected" : ""}>
${sicher(s.name)}</option>`).join("")}
</select></div>
<div class="feld"><label>Notiz</label>
<input name="notiz" maxlength="255" value="${sicher(f.notiz || "")}"
placeholder="Was ist zu tun, was wurde gemacht?"></div>
<div class="feld-hinweis">Ein leeres Feld löscht die Notiz.</div>`,
bestaetigenText: "Eintragen",
bestaetigt: async (el, zu) => {
try {
await aufruf("fahrzeug", "wartung", {
id: f.id,
wartung: el.querySelector("[name=wartung]").value,
notiz: el.querySelector("[name=notiz]").value.trim(),
});
zu();
meldung("Wartungsstatus eingetragen.", "gut");
rendern(behaelter);
} catch (fehler) { meldung(fehler.message, "fehler"); }
},
});
}
/* ---- Fahrzeug anlegen und bearbeiten ------------------------------------------------ */
function bearbeitenFenster(behaelter, f) {
const d = z.daten;
const neu = !f;
const vorhanden = String((f && f.ausstattung) || "")
.split(",").map((t) => t.trim()).filter(Boolean);
const eigene = vorhanden.filter((a) => !d.ausstattung.includes(a));
bestaetigen({
titel: neu ? "Fahrzeug aufnehmen" : `${f.bezeichnung} bearbeiten`,
inhalt: `
<div class="feld-reihe">
<div class="feld"><label>Kennzeichen</label>
<input name="kennzeichen" maxlength="12" placeholder="LSMC 003"
value="${neu ? "" : sicher(f.kennzeichen)}"></div>
<div class="feld"><label>Bezeichnung</label>
<input name="bezeichnung" maxlength="64" placeholder="RTW 3"
value="${neu ? "" : sicher(f.bezeichnung)}"></div>
</div>
<div class="feld-hinweis">Über das Kennzeichen findet die Telemetrie das
Fahrzeug wieder — es muss genau dem im Spiel entsprechen.</div>
<div class="feld-reihe" style="margin-top:10px">
<div class="feld"><label>Modell (Spawnname)</label>
<input name="modell" maxlength="48" placeholder="ambulance"
value="${neu ? "" : sicher(f.modell)}"></div>
<div class="feld"><label>Art</label>
<select name="art">
${d.arten.map((a) =>
`<option value="${sicher(a.id)}" ${!neu && a.id === f.art ? "selected" : ""}>
${sicher(a.name)}</option>`).join("")}
</select></div>
</div>
<div class="feld"><label>Station</label>
<select name="station">
<option value="">keine Zuordnung</option>
${d.stationen.map((s) =>
`<option value="${sicher(s.id)}" ${!neu && s.id === f.station ? "selected" : ""}>
${sicher(s.name)}</option>`).join("")}
</select></div>
<div class="feld"><label>Ausstattung</label>
<div class="fz-ausstattungswahl">
${d.ausstattung.map((a) => `
<button class="fz-teil-knopf ${vorhanden.includes(a) ? "gewaehlt" : ""}"
data-teil="${sicher(a)}">${sicher(a)}</button>`).join("")}
</div></div>
<div class="feld"><label>Weitere Ausstattung (mit Komma trennen)</label>
<input name="eigene" maxlength="512" value="${sicher(eigene.join(", "))}"></div>`,
bestaetigenText: neu ? "Aufnehmen" : "Speichern",
bestaetigt: async (el, zu) => {
const gewaehlt = Array.from(el.querySelectorAll(".fz-teil-knopf.gewaehlt"))
.map((k) => k.dataset.teil);
const weitere = el.querySelector("[name=eigene]").value
.split(",").map((t) => t.trim()).filter(Boolean);
try {
await aufruf("fahrzeug", "speichern", {
id: neu ? null : f.id,
kennzeichen: el.querySelector("[name=kennzeichen]").value.trim(),
bezeichnung: el.querySelector("[name=bezeichnung]").value.trim(),
modell: el.querySelector("[name=modell]").value.trim(),
art: el.querySelector("[name=art]").value,
station: el.querySelector("[name=station]").value,
ausstattung: gewaehlt.concat(weitere).join(", "),
});
zu();
meldung(neu ? "Fahrzeug aufgenommen." : "Fahrzeug gespeichert.", "gut");
rendern(behaelter);
} catch (fehler) { meldung(fehler.message, "fehler"); }
},
nachOeffnen: (hg) => {
hg.querySelectorAll("[data-teil]").forEach((el) => {
el.addEventListener("click", () => el.classList.toggle("gewaehlt"));
});
},
});
}
function entfernenFenster(behaelter, f) {
bestaetigen({
titel: `${f.bezeichnung} aus dem Fuhrpark nehmen`,
gefahr: true,
inhalt: `
<p>Das Fahrzeug verschwindet aus dem Fuhrpark. Die Protokolleinträge
dazu bleiben erhalten.</p>
<div class="feld"><label>Grund (Pflicht)</label>
<input name="grund" maxlength="255"
placeholder="Verkauft, verschrottet, ersetzt…"></div>`,
bestaetigenText: "Entfernen",
bestaetigt: async (el, zu) => {
const grund = el.querySelector("[name=grund]").value.trim();
if (!grund) return meldung("Bitte einen Grund angeben.", "fehler");
try {
await aufruf("fahrzeug", "entfernen", { id: f.id, grund });
zu();
meldung("Fahrzeug entfernt.", "gut");
rendern(behaelter);
} catch (fehler) { meldung(fehler.message, "fehler"); }
},
});
}
/* ---- Laden ---------------------------------------------------------------------- */
async function rendern(behaelter) {
try {
z.daten = await aufruf("fahrzeug", "uebersicht", { ordner: z.ordner, art: z.art });
uebersicht(behaelter);
// Sprungziel aus einem Hinweis, z. B. „Tankstand gemeldet"
const ziel = App.zustand.modulZiel;
if (ziel && ziel.fahrzeug) {
App.zustand.modulZiel = null;
return fahrzeugFenster(behaelter, ziel.fahrzeug);
}
} catch (f) {
behaelter.innerHTML = `<div class="seiten-kopf"><div><h2>Fuhrpark</h2>
<p>Konnte nicht geladen werden: ${sicher(f.message)}</p></div></div>`;
}
}
App.registriereAnsicht("fahrzeug", {
rendern,
aktualisieren() {
// Kein offenes Fenster überschreiben
if (App.zustand.aktivesModul === "fahrzeug" && !document.querySelector(".fenster-hg")) {
rendern(App.$("inhalt"));
}
},
});
})();