/* =====================================================================
aether-police / Ansicht: Fuhrpark
Zeigt Standort, Tankstand, Zustand und letzten Fahrer — das sind
gemeldete Tatsachen. Zuweisung, Status und Wartung setzt immer ein
Beamter; es gibt keine automatische Fahrzeugvergabe.
===================================================================== */
"use strict";
(() => {
const { sicher, zeit, aufruf, meldung, fenster, bestaetigen, $, anClient } = App;
const z = {
daten: null,
offen: null,
filterAbteilung: "",
filterStatus: "",
filterWartung: "",
suche: "",
};
const statusArt = {
verfuegbar: "gut", im_einsatz: "warn",
werkstatt: "kritisch", ausser_dienst: "neutral",
};
const wartungArt = { ok: "gut", faellig: "warn", werkstatt: "kritisch" };
/* ---- Bausteine --------------------------------------------------- */
/** Balken für Tank oder Zustand */
function balken(anteil, art) {
const wert = Math.max(0, Math.min(100, anteil));
const farbe = wert < 20 ? "var(--kritisch)" : wert < 50 ? "var(--warn)" : "var(--gut)";
return `
`;
}
function statusName(id) {
return (z.daten.statusliste && z.daten.statusliste[id]) || id;
}
function wartungName(id) {
return (z.daten.wartungsliste && z.daten.wartungsliste[id]) || id;
}
function abteilungName(id) {
const treffer = (z.daten.abteilungen || []).find((a) => a.id === id);
return treffer ? treffer.name : id;
}
/* ---- Übersicht ---------------------------------------------------- */
function uebersicht(behaelter) {
const d = z.daten;
behaelter.innerHTML = `
Fuhrpark
Standort, Tank und Zustand werden gemeldet — Zuweisung und Status setzt ein Beamter.
${d.rechte.wartung ? `
` : ""}
Verfügbar
${d.bestand.verfuegbar || 0}
Im Einsatz
${d.bestand.im_einsatz || 0}
Werkstatt
${d.bestand.werkstatt || 0}
Wartung offen
${d.wartungFaellig}
${d.fahrzeuge.length ? `
${d.fahrzeuge.map((f) => `
${sicher(f.kennzeichen)}
${sicher(f.bezeichnung)}
${f.wartung !== "ok"
? `${sicher(wartungName(f.wartung))}` : ""}
${sicher(f.modell)} · ${sicher(abteilungName(f.abteilung))}
${f.z_nummer ? " · zugewiesen: " + sicher(f.z_nummer) : ""}
⛽ ${Math.round(f.tank)} %${balken(f.tank, "Tank")}
🔧 ${Math.round(f.zustand / 10)} %${balken(f.zustand / 10, "Zustand")}
${sicher(statusName(f.status))}
${f.pos_x ? `` : ""}
`).join("")}
`
: `
Kein Fahrzeug im Fuhrpark.
`}
`;
const neu = $("fz-neu");
if (neu) neu.addEventListener("click", () => anlegenDialog(behaelter));
const suchfeld = $("fz-suche");
suchfeld.addEventListener("keydown", (e) => {
if (e.key === "Enter") { z.suche = suchfeld.value.trim(); rendern(behaelter); }
});
$("fz-abteilung").addEventListener("change", (e) => {
z.filterAbteilung = e.target.value; rendern(behaelter);
});
$("fz-status").addEventListener("change", (e) => {
z.filterStatus = e.target.value; rendern(behaelter);
});
// Standort auf der Karte markieren, ohne die Zeile zu öffnen
behaelter.querySelectorAll("[data-weg]").forEach((b) =>
b.addEventListener("click", (e) => {
e.stopPropagation();
const [x, y] = b.dataset.weg.split(",").map(Number);
anClient("wegpunkt", { x, y });
}));
behaelter.querySelectorAll("[data-fahrzeug]").forEach((el) =>
el.addEventListener("click", () => oeffne(behaelter, +el.dataset.fahrzeug)));
}
/* ---- Einzelansicht ------------------------------------------------ */
function einzelAnsicht(behaelter, d) {
const f = d.fahrzeug;
behaelter.innerHTML = `
${sicher(f.bezeichnung)}
${sicher(f.kennzeichen)} · ${sicher(f.modell)} · ${sicher(abteilungName(f.abteilung))}
${f.pos_x ? `` : ""}
${d.rechte.zuweisen ? `` : ""}
${d.rechte.wartung ? `` : ""}
${d.rechte.zuweisen ? `` : ""}
${f.wartung !== "ok" ? `
🔧
${sicher(wartungName(f.wartung))} — das Fahrzeug sollte vor dem
nächsten Einsatz geprüft werden.
` : ""}
Zustand
${sicher(statusName(f.status))}
⛽ Tankstand
${Math.round(f.tank)} %
${balken(f.tank, "Tank")}
🔧 Karosserie
${Math.round(f.zustand / 10)} %
${balken(f.zustand / 10, "Zustand")}
Wartung${sicher(wartungName(f.wartung))}
Zugewiesen an${f.z_nummer
? sicher(f.z_nummer + " " + (f.z_vorname || "") + " " + (f.z_nachname || ""))
: "— frei —"}
Letzter Fahrer${f.l_nummer
? sicher(f.l_nummer + " " + (f.l_vorname || "") + " " + (f.l_nachname || ""))
: "— nicht erfasst —"}
Standort${f.pos_x
? `${Math.round(f.pos_x)}, ${Math.round(f.pos_y)}`
: "— nicht gemeldet —"}
ℹ
Tank, Zustand, Standort und letzter Fahrer werden vom Fahrzeug
gemeldet. Status, Zuweisung und Wartung setzt ausschließlich ein Beamter.
Verwaltung
${d.rechte.wartung && !f.zugewiesen_an
? `` : ""}
${f.zugewiesen_an ? `
${sicher(f.z_nummer || "")}
${sicher((f.z_vorname || "") + " " + (f.z_nachname || ""))}
Fahrzeug ist dieser Person zugewiesen
${d.rechte.zuweisen
? `
` : ""}
`
: `
Das Fahrzeug ist keiner Person zugewiesen.
`}
Vorgangshistorie
${d.historie.length ? `
${d.historie.map((h) => `
${sicher(h.aktion)}
${sicher(h.dienstnummer || "")}
${sicher((h.firstname || "") + " " + (h.lastname || ""))} · ${zeit(h.am)}
${h.grund ? " · " + sicher(h.grund) : ""}
`).join("")}
`
: `
Keine Einträge.
`}
`;
verdrahteEinzel(behaelter, d);
}
function verdrahteEinzel(behaelter, d) {
const f = d.fahrzeug;
$("fz-zurueck").addEventListener("click", () => { z.offen = null; rendern(behaelter); });
const standort = $("fz-standort");
if (standort) standort.addEventListener("click", () =>
anClient("wegpunkt", { x: f.pos_x, y: f.pos_y }));
const zuweisen = $("fz-zuweisen");
if (zuweisen) zuweisen.addEventListener("click", () => {
const beamte = d.beamteImDienst || [];
fenster({
titel: "Fahrzeug zuweisen",
inhalt: `
${sicher(f.bezeichnung)} (${sicher(f.kennzeichen)})
${beamte.length ? `${beamte.map((b) =>
`
${sicher(b.dienstnummer)}
${sicher(b.name)}
${sicher(b.rolleName)} · ${sicher(b.abteilung)}
`).join("")}
`
: `Niemand im Dienst.
`}
`,
nachOeffnen: (el) => {
el.querySelectorAll("[data-wahl]").forEach((zeile) =>
zeile.addEventListener("click", async () => {
try {
await aufruf("fahrzeug", "zuweisen", {
id: f.id, charakterId: +zeile.dataset.wahl,
grund: el.querySelector("#fzz-grund").value.trim(),
});
el.remove(); meldung("Zugewiesen.", "gut"); oeffne(behaelter, f.id);
} catch (fehler) { meldung(fehler.message, "fehler"); }
}));
},
});
});
const freigeben = $("fz-freigeben");
if (freigeben) freigeben.addEventListener("click", async () => {
try {
await aufruf("fahrzeug", "zuweisen", { id: f.id, freigeben: true });
meldung("Zuweisung aufgehoben."); oeffne(behaelter, f.id);
} catch (fehler) { meldung(fehler.message, "fehler"); }
});
const statusSetzen = $("fz-status-setzen");
if (statusSetzen) statusSetzen.addEventListener("click", () => {
fenster({
titel: "Status setzen",
inhalt: `
`,
knoepfe: [
{ text: "Abbrechen", klick: (_, zu) => zu() },
{ text: "Speichern", klick: async (el, zu) => {
try {
await aufruf("fahrzeug", "status", {
id: f.id, status: el.querySelector("#fzs-status").value,
grund: el.querySelector("#fzs-grund").value.trim(),
});
zu(); meldung("Status gesetzt.", "gut"); oeffne(behaelter, f.id);
} catch (fehler) { meldung(fehler.message, "fehler"); }
}},
],
});
});
const wartung = $("fz-wartung");
if (wartung) wartung.addEventListener("click", () => {
fenster({
titel: "Wartungsstatus",
inhalt: `
`,
knoepfe: [
{ text: "Abbrechen", klick: (_, zu) => zu() },
{ text: "Speichern", klick: async (el, zu) => {
try {
await aufruf("fahrzeug", "wartung", {
id: f.id, wartung: el.querySelector("#fzw-wartung").value,
vermerk: el.querySelector("#fzw-vermerk").value.trim(),
});
zu(); meldung("Wartungsstatus gesetzt.", "gut"); oeffne(behaelter, f.id);
} catch (fehler) { meldung(fehler.message, "fehler"); }
}},
],
});
});
const entfernen = $("fz-entfernen");
if (entfernen) entfernen.addEventListener("click", () => {
bestaetigen({
titel: "Fahrzeug ausmustern",
gefahr: true,
inhalt: `
${sicher(f.bezeichnung)} (${sicher(f.kennzeichen)}) wird aus dem Fuhrpark
entfernt. Der Vorgang bleibt im Protokoll sichtbar.
`,
bestaetigenText: "Ausmustern",
bestaetigt: async (el, zu) => {
try {
await aufruf("fahrzeug", "entfernen", {
id: f.id, grund: el.querySelector("#fze-grund").value.trim(),
});
zu(); meldung("Ausgemustert."); z.offen = null; rendern(behaelter);
} catch (fehler) { meldung(fehler.message, "fehler"); }
},
});
});
}
/* ---- Anlegen ------------------------------------------------------ */
function anlegenDialog(behaelter) {
fenster({
titel: "Fahrzeug aufnehmen",
inhalt: `
Das Kennzeichen muss mit dem im Spiel übereinstimmen — nur dann können
Tank, Zustand und Standort gemeldet werden.
`,
knoepfe: [
{ text: "Abbrechen", klick: (_, zu) => zu() },
{ text: "Aufnehmen", klick: async (el, zu) => {
try {
const ergebnis = await aufruf("fahrzeug", "anlegen", {
kennzeichen: el.querySelector("#fn-kz").value.trim(),
modell: el.querySelector("#fn-modell").value.trim(),
bezeichnung: el.querySelector("#fn-bez").value.trim(),
abteilung: el.querySelector("#fn-abteilung").value,
});
zu(); meldung("Fahrzeug aufgenommen.", "gut");
oeffne(behaelter, ergebnis.id);
} catch (fehler) { meldung(fehler.message, "fehler"); }
}},
],
});
}
/* ---- Laden -------------------------------------------------------- */
async function oeffne(behaelter, fahrzeugId) {
try {
const d = await aufruf("fahrzeug", "oeffnen", { id: fahrzeugId });
z.offen = fahrzeugId;
einzelAnsicht(behaelter, d);
} catch (f) { meldung(f.message, "fehler"); }
}
async function rendern(behaelter) {
try {
z.daten = await aufruf("fahrzeug", "liste", {
abteilung: z.filterAbteilung, status: z.filterStatus,
wartung: z.filterWartung, begriff: z.suche,
});
if (z.offen) await oeffne(behaelter, z.offen);
else uebersicht(behaelter);
} catch (f) {
behaelter.innerHTML = `Fuhrpark
Konnte nicht geladen werden: ${sicher(f.message)}
`;
}
}
App.registriereAnsicht("fahrzeug", { rendern });
App.registriereSuche(async (begriff) => {
try {
const d = await aufruf("fahrzeug", "liste", { begriff });
return (d.fahrzeuge || []).slice(0, 5).map((f) => ({
bereich: "Dienstfahrzeug", titel: f.bezeichnung,
unter: f.kennzeichen + " · " + f.modell,
}));
} catch (e) { return []; }
});
})();