/* ===================================================================== aether-police / Ansicht: Einsätze & Dispatch Zwei Spalten: links das Einsatzaufkommen, rechts die verfügbaren Einheiten. Zugewiesen wird nur auf ausdrückliche Anweisung der Leitstelle — und auch dann entscheidet die Einheit selbst, ob sie beitritt. ===================================================================== */ "use strict"; (() => { const { sicher, zeit, aufruf, meldung, fenster, bestaetigen, $, anClient } = App; const z = { daten: null, offenerEinsatz: null, nurOffene: true, }; /* ---- Bausteine --------------------------------------------------- */ const statusText = { offen: "Offen", zugewiesen: "Zugewiesen", vorort: "Vor Ort", abgeschlossen: "Abgeschlossen", }; const statusArt = { offen: "kritisch", zugewiesen: "warn", vorort: "info", abgeschlossen: "neutral", }; const quelleText = { manuell: "manuell erfasst", notruf: "Notruf", panik: "Panikknopf", extern: "extern", }; /** Farbe einer Kategorie aus der Serverkonfiguration */ function kategorieFarbe(id) { const treffer = (z.daten.kategorien || []).find((k) => k.id === id); return treffer ? treffer.farbe : "#8a93a5"; } function kategorieName(id) { const treffer = (z.daten.kategorien || []).find((k) => k.id === id); return treffer ? treffer.name : id; } function einsatzKarte(e) { return `
${sicher(e.titel)} ${e.ichDabei > 0 ? 'Du bist dabei' : ""}
${sicher(kategorieName(e.kategorie))} ${sicher(e.nummer)} · ${sicher(e.ort || "Ort unbekannt")} · ${zeit(e.erstellt_am)} ${e.quelle !== "manuell" ? " · " + sicher(quelleText[e.quelle] || e.quelle) : ""}
${sicher(statusText[e.status] || e.status)} ${e.einheiten || 0} 🚓
`; } function einheitenListe(beamte) { if (!beamte.length) return `
Niemand im Dienst.
`; const farbe = { verfuegbar: "gut", einsatz: "warn", pause: "neutral", nicht: "kritisch" }; const text = { verfuegbar: "Verfügbar", einsatz: "Im Einsatz", pause: "Pause", nicht: "Nicht verfügbar" }; return `
${beamte.map((b) => `
${sicher(b.dienstnummer)}
${sicher(b.name)} ${b.leitstelle ? 'Leitstelle' : ""}
${sicher(text[b.status] || b.status)} · ${sicher(b.abteilung)}
`).join("")}
`; } /* ---- Übersicht --------------------------------------------------- */ function uebersicht(behaelter) { const d = z.daten; const offene = d.einsaetze.filter((e) => e.status === "offen"); const laufende = d.einsaetze.filter((e) => e.status !== "offen" && e.status !== "abgeschlossen"); const erledigt = d.einsaetze.filter((e) => e.status === "abgeschlossen"); behaelter.innerHTML = `

Einsätze & Dispatch

${d.istLeitstelle ? "Du führst die Leitstelle — du entscheidest über die Zuweisung." : "Einsätze werden nicht automatisch verteilt. Tritt bei, wenn du übernimmst."}

Offen — keine Einheit zugeordnet ${offene.length}
${offene.length ? offene.map(einsatzKarte).join("") : `
Keine offenen Einsätze.
`}
In Bearbeitung ${laufende.length}
${laufende.length ? laufende.map(einsatzKarte).join("") : `
Keine laufenden Einsätze.
`}
${!z.nurOffene ? `
Abgeschlossen ${erledigt.length}
${erledigt.length ? erledigt.map(einsatzKarte).join("") : `
Nichts abgeschlossen.
`}
` : ""}
Einheiten im Dienst ${d.beamteImDienst.length}
${einheitenListe(d.beamteImDienst)}
ℹ
Eine Zuweisung ist eine Aufforderung, kein Befehl an das System. Die Einheit entscheidet selbst, ob sie den Einsatz übernimmt.
`; $("e-neu").addEventListener("click", () => einsatzAnlegen(behaelter)); $("e-filter").addEventListener("click", () => { z.nurOffene = !z.nurOffene; rendern(behaelter); }); behaelter.querySelectorAll("[data-einsatz]").forEach((el) => el.addEventListener("click", () => oeffneEinsatz(behaelter, +el.dataset.einsatz))); } /* ---- Einzelansicht ------------------------------------------------ */ function einsatzAnsicht(behaelter, d) { const e = d.einsatz; const aktiv = e.status !== "abgeschlossen"; const beteiligt = (e.beamte || []).filter((b) => !b.verlassen); behaelter.innerHTML = `

${sicher(e.titel)}

${sicher(e.nummer)} · ${sicher(kategorieName(e.kategorie))} · ${sicher(quelleText[e.quelle] || e.quelle)} · ${zeit(e.erstellt_am)}

Lage ${sicher(statusText[e.status] || e.status)}
Priorität${sicher(e.prioritaet)} ${e.prioritaet === 1 ? "(höchste)" : ""} Ort${sicher(e.ort || "unbekannt")} Gemeldet von${e.erstellt_von ? sicher((e.ersteller_vorname || "") + " " + (e.ersteller_nachname || "")) : sicher(quelleText[e.quelle] || "extern")}
${e.beschreibung ? `
${sicher(e.beschreibung).replace(/\n/g, "
")}
` : ""}
${e.pos_x ? `` : ""} ${aktiv && !e.ichDabei ? `` : ""} ${aktiv && e.ichDabei ? `` : ""} ${aktiv && e.ichDabei ? `` : ""} ${aktiv && d.rechte.schliessen ? `` : ""}
Einsatzverlauf ${aktiv ? `` : ""}
${(e.notizen || []).length ? `
${e.notizen.map((n) => `
${zeit(n.am).slice(-5)}
${sicher(n.text)}
${sicher(n.dienstnummer || "")} ${sicher((n.firstname || "") + " " + (n.lastname || ""))}
`).join("")}
` : `
Noch keine Einträge.
`}
Beteiligte Einheiten ${beteiligt.length}
${beteiligt.length ? `
${beteiligt.map((b) => `
${sicher(b.dienstnummer || "—")}
${sicher((b.firstname || "") + " " + (b.lastname || ""))}
seit ${zeit(b.beigetreten)}
`).join("")}
` : `
Keine Einheit vor Ort.
`}
${aktiv && d.rechte.zuweisen ? `
Einheit anfordern
${d.beamteImDienst.map((b) => `
${sicher(b.dienstnummer)}
${sicher(b.name)}
${sicher(b.status)}
`).join("")}
ℹ
Die Einheit erhält eine Anfrage mit Wegpunkt. Ob sie übernimmt, entscheidet sie selbst.
` : ""}
`; /* ---- Aktionen ---- */ $("e-zurueck").addEventListener("click", () => { z.offenerEinsatz = null; rendern(behaelter); }); const route = $("e-route"); if (route) route.addEventListener("click", () => anClient("wegpunkt", { x: e.pos_x, y: e.pos_y })); const beitreten = $("e-beitreten"); if (beitreten) beitreten.addEventListener("click", async () => { try { await aufruf("einsatz", "beitreten", { id: e.id }); meldung("Du hast den Einsatz übernommen.", "gut"); oeffneEinsatz(behaelter, e.id); } catch (f) { meldung(f.message, "fehler"); } }); const verlassen = $("e-verlassen"); if (verlassen) verlassen.addEventListener("click", async () => { try { await aufruf("einsatz", "verlassen", { id: e.id }); meldung("Du hast den Einsatz verlassen."); oeffneEinsatz(behaelter, e.id); } catch (f) { meldung(f.message, "fehler"); } }); const vorort = $("e-vorort"); if (vorort) vorort.addEventListener("click", async () => { try { await aufruf("einsatz", "status", { id: e.id, status: "vorort" }); oeffneEinsatz(behaelter, e.id); } catch (f) { meldung(f.message, "fehler"); } }); const notiz = $("e-notiz"); if (notiz) notiz.addEventListener("click", () => { fenster({ titel: "Notiz zum Einsatz", inhalt: `
Der Verlauf hilft später beim Bericht.
`, knoepfe: [ { text: "Abbrechen", klick: (_, zu) => zu() }, { text: "Eintragen", klick: async (el, zu) => { const text = el.querySelector("#en-text").value.trim(); if (text.length < 2) { meldung("Bitte einen Text eingeben.", "fehler"); return; } try { await aufruf("einsatz", "notiz", { id: e.id, text }); zu(); oeffneEinsatz(behaelter, e.id); } catch (f) { meldung(f.message, "fehler"); } }}, ], }); }); behaelter.querySelectorAll("[data-zuweisen]").forEach((b) => b.addEventListener("click", async () => { try { await aufruf("einsatz", "zuweisen", { id: e.id, quelle: +b.dataset.zuweisen }); meldung("Anfrage an die Einheit gesendet.", "gut"); } catch (f) { meldung(f.message, "fehler"); } })); const schliessen = $("e-schliessen"); if (schliessen) schliessen.addEventListener("click", () => { bestaetigen({ titel: "Einsatz abschließen", inhalt: `

${sicher(e.nummer)} — ${sicher(e.titel)}
Alle Beteiligungen werden beendet. Es wird festgehalten, dass du den Einsatz abgeschlossen hast.

${d.rechte.bericht ? ` ` : ""}`, bestaetigenText: "Einsatz abschließen", bestaetigt: async (el, zu) => { const kasten = el.querySelector("#es-bericht"); try { const ergebnis = await aufruf("einsatz", "abschliessen", { id: e.id, grund: el.querySelector("#es-grund").value.trim(), berichtAnlegen: kasten ? kasten.checked : false, }); zu(); meldung(ergebnis.berichtNummer ? "Einsatz abgeschlossen · Entwurf " + ergebnis.berichtNummer + " angelegt." : "Einsatz abgeschlossen.", "gut"); z.offenerEinsatz = null; rendern(behaelter); } catch (f) { meldung(f.message, "fehler"); } }, }); }); } async function oeffneEinsatz(behaelter, einsatzId) { try { const d = await aufruf("einsatz", "oeffnen", { id: einsatzId }); z.offenerEinsatz = einsatzId; // Kategorien für die Anzeige mitführen if (!z.daten) z.daten = { kategorien: [] }; d.kategorien = z.daten.kategorien; einsatzAnsicht(behaelter, d); } catch (f) { meldung(f.message, "fehler"); } } /* ---- Anlegen ------------------------------------------------------ */ function einsatzAnlegen(behaelter) { const kategorien = (z.daten && z.daten.kategorien) || []; fenster({ titel: "Einsatz anlegen", inhalt: `
`, knoepfe: [ { text: "Abbrechen", klick: (_, zu) => zu() }, { text: "Einsatz anlegen", klick: async (el, zu) => { try { const ergebnis = await aufruf("einsatz", "erstellen", { titel: el.querySelector("#ne-titel").value.trim(), kategorie: el.querySelector("#ne-kategorie").value, prioritaet: +el.querySelector("#ne-prio").value, ort: el.querySelector("#ne-ort").value.trim(), beschreibung: el.querySelector("#ne-text").value, eigenePosition: el.querySelector("#ne-hier").checked, }); zu(); meldung("Einsatz " + ergebnis.nummer + " angelegt.", "gut"); oeffneEinsatz(behaelter, ergebnis.id); } catch (f) { meldung(f.message, "fehler"); } }}, ], }); } /* ---- Laden -------------------------------------------------------- */ async function rendern(behaelter) { try { z.daten = await aufruf("einsatz", "liste", { nurOffene: z.nurOffene }); // Markierungen im Spiel setzen anClient("einsatzBlips", { einsaetze: z.daten.einsaetze }); if (z.offenerEinsatz) await oeffneEinsatz(behaelter, z.offenerEinsatz); else uebersicht(behaelter); } catch (f) { behaelter.innerHTML = `

Einsätze

Konnte nicht geladen werden: ${sicher(f.message)}

`; } } /* ---- Nachrichten vom Client -------------------------------------- */ window.addEventListener("message", (ereignis) => { const n = ereignis.data || {}; // Eine Einheit wurde angefordert — der Beamte entscheidet selbst if (n.aktion === "einsatzZuweisung") { const d = n.daten || {}; bestaetigen({ titel: "Einsatz übernehmen?", inhalt: `

${sicher(d.von)} bittet dich um Übernahme:

${sicher(d.nummer)} — ${sicher(d.titel)}
${d.ort ? "Ort: " + sicher(d.ort) : "Ort unbekannt"}

Ein Wegpunkt wurde bereits gesetzt.

`, bestaetigenText: "Übernehmen", bestaetigt: async (_, zu) => { try { await aufruf("einsatz", "beitreten", { id: d.id }); zu(); meldung("Einsatz übernommen.", "gut"); if (App.zustand.aktivesModul === "einsatz") rendern($("inhalt")); } catch (f) { meldung(f.message, "fehler"); } }, }); } // Im Hintergrund hat sich etwas geändert if (n.aktion === "einsatzAenderung" && App.zustand.aktivesModul === "einsatz") { rendern($("inhalt")); } }); /* ---- Registrierung ------------------------------------------------ */ App.registriereAnsicht("einsatz", { rendern, aktualisieren: () => { if (App.zustand.aktivesModul === "einsatz") rendern($("inhalt")); }, }); })();