/* ===================================================================== aether-police / Ansicht: Administration Module an- und abschalten, Strafenkatalog pflegen, Protokoll lesen. Die Reiter richten sich nach den Rechten des Beamten — wer nur das Protokoll lesen darf, sieht auch nur dieses. ===================================================================== */ "use strict"; (() => { const { sicher, zeit, euro, aufruf, meldung, fenster, bestaetigen } = App; const z = { bereiche: null, // welche Reiter erlaubt sind reiter: null, daten: null, katalogSuche: "", katalogKategorie: "", katalogStillgelegt: false, protokollModul: "", protokollNummer: "", }; /* ---- Reiter ---------------------------------------------------------- */ function reiterleiste() { const moeglich = [ { id: "module", text: "Module", erlaubt: z.bereiche.module }, { id: "katalog", text: "Strafenkatalog", erlaubt: z.bereiche.katalog }, { id: "protokoll", text: "Protokoll", erlaubt: z.bereiche.protokoll }, ].filter((e) => e.erlaubt); return `
${moeglich.map((e) => ` `).join("")}
`; } function verdrahteReiter(behaelter) { behaelter.querySelectorAll("[data-reiter]").forEach((el) => { el.addEventListener("click", () => { z.reiter = el.dataset.reiter; rendern(behaelter); }); }); } /* ---- Module ----------------------------------------------------------- */ function moduleAnsicht(behaelter) { const d = z.daten; const aktiv = d.module.filter((m) => m.aktiv).length; behaelter.innerHTML = `

Module

Jeder Baustein lässt sich einzeln abschalten. Abgeschaltete Module verschwinden aus der Seitenleiste — ihre Daten bleiben unangetastet.

${reiterleiste()}
${aktiv}/${d.module.length} Module aktiv
${d.beamteImDienst} Beamte im Dienst
${d.module.reduce((s, m) => s + m.aktionen, 0)} registrierte Aktionen
Registrierte Module
${d.module.map(modulZeile).join("")}
Konfiguration Steht in config.lua — Änderung erfordert Neustart
${d.konfiguration.map((k) => `
${sicher(k.name)}${sicher(k.wert)}
`).join("")}
`; verdrahteModule(behaelter); } function modulZeile(m) { return `
${sicher(m.icon)}
${sicher(m.name)}
${sicher(m.beschreibung || "")} ${m.rechtName ? ` · Recht: ${sicher(m.rechtName)}` : m.recht ? ` · Recht: ${sicher(String(m.recht))}` : " · für alle sichtbar"}
${m.aktionen} Aktionen ${m.unverzichtbar ? ` unverzichtbar` : ``}
`; } function verdrahteModule(behaelter) { verdrahteReiter(behaelter); behaelter.querySelectorAll("[data-schalten]").forEach((el) => { el.addEventListener("click", () => { const id = el.dataset.schalten; const einschalten = el.dataset.wert === "1"; const modul = z.daten.module.find((m) => m.id === id); bestaetigen({ titel: einschalten ? "Modul einschalten" : "Modul abschalten", gefahr: !einschalten, inhalt: `

${sicher(modul.name)} wird ${einschalten ? "wieder in der Seitenleiste erscheinen" : "für alle Beamten aus der Seitenleiste verschwinden"}.

${einschalten ? "" : `

Vorhandene Daten bleiben erhalten und sind nach dem Wiedereinschalten unverändert da. Beamte, die das Tablet gerade offen haben, sehen die Änderung beim nächsten Öffnen.

`}
`, bestaetigenText: einschalten ? "Einschalten" : "Abschalten", bestaetigt: async (dialog, zu) => { try { await aufruf("admin", "modulSchalten", { id, aktiv: einschalten, grund: dialog.querySelector("[name=grund]").value.trim(), }); zu(); meldung(einschalten ? "Modul eingeschaltet." : "Modul abgeschaltet.", "gut"); rendern(behaelter); } catch (f) { meldung(f.message, "fehler"); } }, }); }); }); } /* ---- Strafenkatalog ---------------------------------------------------- */ function katalogAnsicht(behaelter) { const d = z.daten; // Delikte nach Kategorie gruppieren const gruppen = {}; d.delikte.forEach((delikt) => { (gruppen[delikt.kategorie] = gruppen[delikt.kategorie] || []).push(delikt); }); behaelter.innerHTML = `

Strafenkatalog

Die Werte hier sind Vorschläge. Sie werden nie automatisch verhängt — ein Beamter kann jederzeit begründet davon abweichen.

${reiterleiste()}
${Object.keys(gruppen).length ? Object.keys(gruppen).map((kategorie) => `
${sicher(kategorie)} ${gruppen[kategorie].length} Delikte
${gruppen[kategorie].map(deliktZeile).join("")}
`).join("") : `
Kein Delikt gefunden.
`} `; verdrahteKatalog(behaelter); } function deliktZeile(delikt) { return `
${sicher(delikt.kennung)}
${sicher(delikt.bezeichnung)}
${sicher(delikt.paragraph || "ohne Paragraf")} ${delikt.hinweis ? " · " + sicher(delikt.hinweis) : ""}
${delikt.haft || 0} Min ${euro(delikt.geld || 0)} ${delikt.aktiv ? "" : `stillgelegt`}
`; } function verdrahteKatalog(behaelter) { verdrahteReiter(behaelter); const suchfeld = behaelter.querySelector("[data-suche]"); let warten = null; suchfeld.addEventListener("input", () => { clearTimeout(warten); warten = setTimeout(() => { z.katalogSuche = suchfeld.value.trim(); rendern(behaelter); }, 260); }); behaelter.querySelector("[data-kategorie]").addEventListener("change", (e) => { z.katalogKategorie = e.target.value; rendern(behaelter); }); behaelter.querySelector("[data-stillgelegt]").addEventListener("click", () => { z.katalogStillgelegt = !z.katalogStillgelegt; rendern(behaelter); }); behaelter.querySelector("[data-neu]").addEventListener("click", () => deliktFenster(behaelter, null)); behaelter.querySelectorAll("[data-bearbeiten]").forEach((el) => { el.addEventListener("click", () => { const delikt = z.daten.delikte.find((d) => d.id === +el.dataset.bearbeiten); deliktFenster(behaelter, delikt); }); }); behaelter.querySelectorAll("[data-stilllegen]").forEach((el) => { el.addEventListener("click", () => { const delikt = z.daten.delikte.find((d) => d.id === +el.dataset.stilllegen); const freigeben = el.dataset.wert === "1"; bestaetigen({ titel: freigeben ? "Delikt freigeben" : "Delikt stilllegen", gefahr: !freigeben, inhalt: `

${sicher(delikt.kennung)} — ${sicher(delikt.bezeichnung)}

${freigeben ? "Das Delikt steht wieder zur Auswahl." : "Das Delikt lässt sich für neue Anzeigen nicht mehr auswählen."}

Gelöscht wird nichts — bestehende Anzeigen behalten ihren Eintrag und bleiben nachvollziehbar.

`, bestaetigenText: freigeben ? "Freigeben" : "Stilllegen", bestaetigt: async (dialog, zu) => { try { await aufruf("admin", "katalogSchalten", { id: delikt.id, aktiv: freigeben, grund: dialog.querySelector("[name=grund]").value.trim(), }); zu(); meldung(freigeben ? "Delikt freigegeben." : "Delikt stillgelegt.", "gut"); rendern(behaelter); } catch (f) { meldung(f.message, "fehler"); } }, }); }); }); } function deliktFenster(behaelter, delikt) { const neu = !delikt; delikt = delikt || { kennung: "", kategorie: "", bezeichnung: "", paragraph: "", haft: 0, geld: 0, hinweis: "", sortierung: 0 }; fenster({ titel: neu ? "Delikt anlegen" : "Delikt bearbeiten", inhalt: `
${z.daten.kategorien.map((k) => `
ℹ
Diese Werte sind Vorschläge. Das System verhängt sie nie — es rechnet sie nur zusammen und legt sie dem Beamten vor.
`, knoepfe: [ { text: "Abbrechen", art: "leise", klick: (_, zu) => zu() }, { text: neu ? "Anlegen" : "Speichern", klick: async (el, zu) => { const feld = (name) => el.querySelector(`[name=${name}]`).value.trim(); try { await aufruf("admin", "katalogSpeichern", { id: neu ? null : delikt.id, kennung: feld("kennung"), kategorie: feld("kategorie"), bezeichnung: feld("bezeichnung"), paragraph: feld("paragraph"), haft: +feld("haft"), geld: +feld("geld"), hinweis: feld("hinweis"), sortierung: +feld("sortierung"), }); zu(); meldung(neu ? "Delikt angelegt." : "Delikt gespeichert.", "gut"); rendern(behaelter); } catch (f) { meldung(f.message, "fehler"); } } }, ], }); } /* ---- Protokoll ---------------------------------------------------------- */ function protokollAnsicht(behaelter) { const d = z.daten; behaelter.innerHTML = `

Protokoll

Jede Entscheidung im System steht hier mit Beamtem, Zeit und Begründung. Einträge lassen sich weder ändern noch entfernen.

${d.gesamt.toLocaleString("de-DE")} Einträge gesamt
${reiterleiste()}
${d.eintraege.length ? d.eintraege.map(protokollZeile).join("") : `
${d.unbekannteNummer ? "Diese Dienstnummer gibt es nicht." : "Kein Eintrag gefunden."}
`}
`; verdrahteProtokoll(behaelter); } function protokollZeile(e) { // Details als kurze Schlüssel-Wert-Kette const details = e.details && typeof e.details === "object" ? Object.keys(e.details).map((k) => `${sicher(k)}: ${sicher(String(e.details[k]))}`).join(" · ") : ""; return `
${sicher(e.modul)}
${sicher(e.aktion)}
${sicher(e.name)}${e.dienstnummer ? " (" + sicher(e.dienstnummer) + ")" : ""} ${e.ziel_typ ? ` · ${sicher(e.ziel_typ)} ${sicher(e.ziel_id || "")}` : ""} ${details ? ` · ${details}` : ""}
${e.grund ? `
„${sicher(e.grund)}"
` : ""}
${zeit(e.am || e.erstellt_am)}
`; } function verdrahteProtokoll(behaelter) { verdrahteReiter(behaelter); behaelter.querySelector("[data-modul]").addEventListener("change", (e) => { z.protokollModul = e.target.value; rendern(behaelter); }); const nummer = behaelter.querySelector("[data-nummer]"); let warten = null; nummer.addEventListener("input", () => { clearTimeout(warten); warten = setTimeout(() => { z.protokollNummer = nummer.value.trim(); rendern(behaelter); }, 320); }); behaelter.querySelector("[data-zuruecksetzen]").addEventListener("click", () => { z.protokollModul = ""; z.protokollNummer = ""; rendern(behaelter); }); } /* ---- Laden -------------------------------------------------------------- */ async function rendern(behaelter) { try { // Erlaubte Bereiche einmal holen und den Startreiter danach wählen if (!z.bereiche) { z.bereiche = await aufruf("admin", "bereiche", {}); z.reiter = z.bereiche.module ? "module" : z.bereiche.katalog ? "katalog" : z.bereiche.protokoll ? "protokoll" : null; } if (!z.reiter) { behaelter.innerHTML = `

Administration

Für keinen Bereich der Administration hast du eine Berechtigung.

`; return; } if (z.reiter === "katalog") { z.daten = await aufruf("admin", "katalog", { begriff: z.katalogSuche, kategorie: z.katalogKategorie, stillgelegt: z.katalogStillgelegt, }); katalogAnsicht(behaelter); } else if (z.reiter === "protokoll") { z.daten = await aufruf("admin", "protokoll", { modul: z.protokollModul, dienstnummer: z.protokollNummer, limit: 200, }); protokollAnsicht(behaelter); } else { z.daten = await aufruf("admin", "module", {}); moduleAnsicht(behaelter); } } catch (f) { behaelter.innerHTML = `

Administration

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

`; } } App.registriereAnsicht("admin", { rendern }); })();