/* =====================================================================
aether-police / Ansicht: Dashboard
Zeigt die Lage der Dienststelle. Sämtliche Zahlen kommen bei jedem
Aufruf frisch vom Server — nichts ist fest hinterlegt.
Die Ansicht bewertet nichts und löst keine Maßnahme aus.
===================================================================== */
"use strict";
(() => {
const { sicher, zeit, dauer, aufruf, meldung, anClient } = App;
/** Zwischenspeicher der zuletzt geladenen Daten (für Auffrischungen) */
let letzteDaten = null;
/* ---- Bausteine --------------------------------------------------- */
function kennzahl(titel, wert, zusatz, art) {
return `
${sicher(titel)}
${sicher(wert)}
${zusatz ? `
${sicher(zusatz)}
` : ""}
`;
}
/** Übersetzt eine Prioritätszahl in eine Streifenklasse */
const prioKlasse = (p) => "p" + Math.min(4, Math.max(1, p || 3));
function warnungen(liste) {
if (!liste || !liste.length) return "";
const symbol = { hoch: "⚠", normal: "ℹ", niedrig: "•" };
return liste.map((w) =>
`
${symbol[w.prioritaet] || "•"}
${sicher(w.text)}
`
).join("");
}
function beamtenListe(beamte) {
if (!beamte.length) return `Niemand im Dienst.
`;
const statusFarbe = {
verfuegbar: "gut", einsatz: "warn", pause: "neutral", nicht: "kritisch",
};
return `${beamte.map((b) =>
`
${sicher(b.dienstnummer)}
${sicher(b.name)}${b.leitstelle ? ' Leitstelle ' : ""}
${sicher(b.rolleName)} · ${sicher(b.abteilung)}
${dauer((Date.now() / 1000) - b.seit)}
`).join("")}
`;
}
function einsatzListe(einsaetze) {
if (!einsaetze.length) return `Keine offenen Einsätze.
`;
return `${einsaetze.map((e) =>
`
${sicher(e.titel)}
${sicher(e.nummer)} · ${sicher(e.ort || "Ort unbekannt")} · ${zeit(e.erstellt_am)}
${sicher(e.status)}
${e.pos_x ? `
Route ` : ""}
`).join("")}
`;
}
function gewahrsamListe(eintraege) {
if (!eintraege.length) return `Niemand in Gewahrsam.
`;
return `${eintraege.map((g) => {
const rest = Number(g.rest) || 0;
const abgelaufen = rest <= 0;
return `
${sicher(g.firstname + " " + g.lastname)}
${sicher(g.grund || "—")}${g.zelle ? " · Zelle " + sicher(g.zelle) : ""}
${abgelaufen ? "Frist abgelaufen" : rest + " Min"}
`;
}).join("")}
`;
}
function fahndungsListe(fahndungen) {
if (!fahndungen.length) return `Keine aktiven Fahndungen.
`;
const gefahrKlasse = { gefaehrlich: "kritisch", bewaffnet: "warn", normal: "neutral" };
return `${fahndungen.map((f) =>
`
${sicher(f.bezeichnung)}
${sicher(f.grund)}
${sicher(f.gefahr)}
`).join("")}
`;
}
function mitteilungsListe(mitteilungen) {
if (!mitteilungen.length) return `Keine Mitteilungen.
`;
return `${mitteilungen.map((m) =>
`
${sicher(m.betreff || "Mitteilung")}
${sicher((m.firstname || "") + " " + (m.lastname || ""))} · ${zeit(m.gesendet)}
`).join("")}
`;
}
/* ---- Aufbau ------------------------------------------------------ */
function zeichne(behaelter, d) {
const k = d.kennzahlen;
behaelter.innerHTML = `
Übersicht
Lage der Dienststelle — Stand ${new Date().toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" })}
↻ Aktualisieren
${warnungen(d.warnungen)}
${kennzahl("Im Dienst", k.imDienst, `${k.verfuegbar} verfügbar`, k.imDienst > 0 ? "gut" : "")}
${kennzahl("Offene Einsätze", k.einsaetzeOffen, null, k.einsaetzeOffen > 0 ? "warn" : "")}
${kennzahl("Anzeigen (Entwurf)", k.anzeigenOffen, "warten auf Bestätigung")}
${kennzahl("Fahndungen", k.fahndungen)}
${kennzahl("In Gewahrsam", k.gefangene,
k.haftAbgelaufen > 0 ? `${k.haftAbgelaufen} Frist abgelaufen` : null,
k.haftAbgelaufen > 0 ? "hoch" : "")}
${kennzahl("Dienstfahrzeuge", `${k.fahrzeugeEinsatz}/${k.fahrzeugeGesamt}`, "im Einsatz")}
Offene Einsätze
${d.einsaetze.length}
${einsatzListe(d.einsaetze)}
Beamte im Dienst
${d.beamte.length}
${beamtenListe(d.beamte)}
Gewahrsam
${d.gewahrsam.length}
${gewahrsamListe(d.gewahrsam)}
ℹ
Fristen dienen nur der Erinnerung . Eine Entlassung erfolgt
ausschließlich manuell durch einen Beamten.
Aktive Fahndungen
${d.fahndungen.length}
${fahndungsListe(d.fahndungen)}
Meine offenen Berichte
${d.berichte.length}
${d.berichte.length
? `
${d.berichte.map((b) =>
`
${sicher(b.titel)}
${sicher(b.nummer)} · ${zeit(b.erstellt_am)}
Entwurf
`).join("")}
`
: `
Keine offenen Berichte.
`}
Neueste Mitteilungen
${mitteilungsListe(d.mitteilungen)}
`;
// Aktualisieren
App.$("db-aktualisieren").addEventListener("click", () => laden(behaelter));
// Route zu einem Einsatzort setzen
behaelter.querySelectorAll("[data-weg]").forEach((b) => {
b.addEventListener("click", () => {
const [x, y] = b.dataset.weg.split(",").map(Number);
anClient("wegpunkt", { x, y });
});
});
}
/* ---- Laden ------------------------------------------------------- */
async function laden(behaelter) {
try {
const daten = await aufruf("dashboard", "laden", {});
letzteDaten = daten;
App.zustand.hinweise = daten.hinweise || [];
zeichne(behaelter, daten);
} catch (fehler) {
behaelter.innerHTML =
`Übersicht
Die Daten konnten nicht geladen werden: ${sicher(fehler.message)}
`;
}
}
/* ---- Registrierung ----------------------------------------------- */
App.registriereAnsicht("dashboard", {
rendern: (behaelter) => laden(behaelter),
// Wird aufgerufen, wenn sich im Hintergrund etwas geändert hat
aktualisieren: () => {
const behaelter = App.$("inhalt");
if (App.zustand.aktivesModul === "dashboard" && behaelter) laden(behaelter);
},
});
})();