- 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>
554 lines
24 KiB
JavaScript
554 lines
24 KiB
JavaScript
/* =====================================================================
|
||
aether-police / Ansicht: Strafenkatalog & Anzeigen
|
||
|
||
Der Katalog liefert einen VORSCHLAG. Die Felder darunter gehören dem
|
||
Beamten — er kann sie jederzeit ändern. Weicht er ab, wird das
|
||
sichtbar gemacht und eine Begründung verlangt.
|
||
===================================================================== */
|
||
"use strict";
|
||
|
||
(() => {
|
||
const { sicher, zeit, euro, aufruf, meldung, fenster, bestaetigen, $ } = App;
|
||
|
||
const z = {
|
||
reiter: "erfassen", // erfassen | vorgaenge | katalog
|
||
katalog: [],
|
||
rechte: {},
|
||
auswahl: new Set(), // gewählte Delikt-Kennungen
|
||
vorschlag: null, // { delikte, haft, geld, hinweise }
|
||
beschuldigter: null, // { id, name }
|
||
anzeigen: [],
|
||
filterStatus: "",
|
||
};
|
||
|
||
/* ---- Bausteine --------------------------------------------------- */
|
||
|
||
function reiterLeiste() {
|
||
const reiter = [
|
||
{ id: "erfassen", name: "Anzeige erfassen", symbol: "✎" },
|
||
{ id: "vorgaenge", name: "Vorgänge", symbol: "📋" },
|
||
];
|
||
if (z.rechte.bearbeiten) reiter.push({ id: "katalog", name: "Katalog pflegen", symbol: "⚙" });
|
||
|
||
return `<div class="mdt-reiter">${reiter.map((r) =>
|
||
`<button data-reiter="${r.id}" class="${r.id === z.reiter ? "aktiv" : ""}">
|
||
${r.symbol} ${r.name}</button>`).join("")}</div>`;
|
||
}
|
||
|
||
/** Zeigt Minuten als „1 Std 30 Min" */
|
||
function haftText(minuten) {
|
||
const m = Math.max(0, Math.floor(minuten || 0));
|
||
if (m === 0) return "keine Haft";
|
||
if (m < 60) return m + " Min";
|
||
return Math.floor(m / 60) + " Std " + (m % 60 ? (m % 60) + " Min" : "");
|
||
}
|
||
|
||
/* =====================================================================
|
||
ANZEIGE ERFASSEN
|
||
===================================================================== */
|
||
|
||
function katalogAuswahl() {
|
||
if (!z.katalog.length) return `<div class="liste-leer">Der Katalog ist leer.</div>`;
|
||
|
||
return z.katalog.map((gruppe) => `
|
||
<div class="katalog-gruppe">
|
||
<div class="katalog-titel">${sicher(gruppe.kategorie)}</div>
|
||
${gruppe.delikte.map((d) => `
|
||
<label class="delikt ${z.auswahl.has(d.kennung) ? "gewaehlt" : ""}">
|
||
<input type="checkbox" data-delikt="${sicher(d.kennung)}"
|
||
${z.auswahl.has(d.kennung) ? "checked" : ""}>
|
||
<div class="delikt-text">
|
||
<div class="delikt-name">${sicher(d.bezeichnung)}</div>
|
||
<div class="delikt-unter">
|
||
${d.paragraph ? sicher(d.paragraph) + " · " : ""}
|
||
${haftText(d.haft)} · ${euro(d.geld)}
|
||
</div>
|
||
</div>
|
||
</label>`).join("")}
|
||
</div>`).join("");
|
||
}
|
||
|
||
function erfassenAnsicht(behaelter) {
|
||
const v = z.vorschlag;
|
||
|
||
behaelter.innerHTML = `
|
||
<div class="seiten-kopf">
|
||
<div>
|
||
<h2>Anzeige erfassen</h2>
|
||
<p>Der Katalog schlägt vor — entschieden wird von dir.</p>
|
||
</div>
|
||
</div>
|
||
|
||
${reiterLeiste()}
|
||
|
||
<div class="anzeige-raster">
|
||
<div class="karte">
|
||
<div class="karte-kopf">
|
||
<span class="karte-titel">Strafenkatalog</span>
|
||
<input class="katalog-suche" id="an-katalogsuche" type="search" placeholder="Delikt suchen …">
|
||
</div>
|
||
<div class="katalog-liste" id="an-katalog">${katalogAuswahl()}</div>
|
||
</div>
|
||
|
||
<div class="karte">
|
||
<div class="karte-kopf"><span class="karte-titel">Vorgang</span></div>
|
||
|
||
<div class="feld">
|
||
<label>Beschuldigte Person</label>
|
||
<div class="person-waehler">
|
||
<input id="an-person" placeholder="Name eingeben und suchen …"
|
||
value="${z.beschuldigter ? sicher(z.beschuldigter.name) : ""}">
|
||
<button class="knopf leise klein" id="an-personsuche">Suchen</button>
|
||
</div>
|
||
${z.beschuldigter
|
||
? `<div class="feld-hinweis">Ausgewählt: <b>${sicher(z.beschuldigter.name)}</b></div>`
|
||
: `<div class="feld-hinweis">Ohne Beschuldigten kann keine Anzeige erfasst werden.</div>`}
|
||
</div>
|
||
|
||
${v ? `
|
||
<div class="vorschlag-block">
|
||
<div class="vorschlag-kopf">
|
||
<span class="karte-titel">Vorschlag des Katalogs</span>
|
||
<span class="marker neutral">${v.delikte.length} Delikt${v.delikte.length === 1 ? "" : "e"}</span>
|
||
</div>
|
||
<div class="vorschlag-werte">
|
||
<div><span>Haft</span><b>${haftText(v.haft)}</b></div>
|
||
<div><span>Geldstrafe</span><b>${euro(v.geld)}</b></div>
|
||
</div>
|
||
${v.hinweise.length ? `
|
||
<div class="vorschlag-hinweise">
|
||
${v.hinweise.map((h) => `<div>ℹ ${sicher(h)}</div>`).join("")}
|
||
</div>` : ""}
|
||
</div>
|
||
|
||
<div class="entscheidung-block">
|
||
<div class="karte-titel" style="margin-bottom:10px">Deine Entscheidung</div>
|
||
<div class="raster k2" style="gap:10px">
|
||
<div class="feld" style="margin:0">
|
||
<label>Haftdauer (Minuten)</label>
|
||
<input id="an-haft" type="number" min="0" max="1440" value="${v.haft}">
|
||
</div>
|
||
<div class="feld" style="margin:0">
|
||
<label>Geldstrafe (€)</label>
|
||
<input id="an-geld" type="number" min="0" value="${v.geld}">
|
||
</div>
|
||
</div>
|
||
|
||
<div id="an-abweichung" class="abweichung" hidden>
|
||
<div class="abweichung-kopf">⚠ Du weichst vom Vorschlag ab</div>
|
||
<div class="feld" style="margin:10px 0 0">
|
||
<label>Begründung (erforderlich)</label>
|
||
<textarea id="an-grund" placeholder="Warum weichst du ab?"></textarea>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="grundsatz">
|
||
<span>ℹ</span>
|
||
<div>Der Katalog ist ein <b>Nachschlagewerk</b>. Beide Werte lassen sich frei
|
||
anpassen — gespeichert werden Vorschlag und Entscheidung getrennt, damit
|
||
nachvollziehbar bleibt, wer was festgelegt hat.</div>
|
||
</div>
|
||
|
||
<button class="knopf gross" id="an-speichern">Als Entwurf erfassen</button>
|
||
<div class="feld-hinweis" style="text-align:center;margin-top:8px">
|
||
Es wird noch nichts eingezogen und niemand kommt in Haft.
|
||
</div>
|
||
</div>`
|
||
: `<div class="liste-leer">Wähle links mindestens ein Delikt aus.</div>`}
|
||
</div>
|
||
</div>`;
|
||
|
||
verdrahteErfassen(behaelter);
|
||
}
|
||
|
||
function verdrahteErfassen(behaelter) {
|
||
// Delikt an-/abwählen
|
||
behaelter.querySelectorAll("[data-delikt]").forEach((k) =>
|
||
k.addEventListener("change", async () => {
|
||
const kennung = k.dataset.delikt;
|
||
if (k.checked) z.auswahl.add(kennung); else z.auswahl.delete(kennung);
|
||
await berechneVorschlag();
|
||
erfassenAnsicht(behaelter);
|
||
}));
|
||
|
||
// Katalogsuche (nur Anzeige filtern)
|
||
const suche = $("an-katalogsuche");
|
||
if (suche) suche.addEventListener("input", () => {
|
||
const begriff = suche.value.trim().toLowerCase();
|
||
behaelter.querySelectorAll(".delikt").forEach((el) => {
|
||
const text = el.textContent.toLowerCase();
|
||
el.style.display = !begriff || text.includes(begriff) ? "" : "none";
|
||
});
|
||
behaelter.querySelectorAll(".katalog-gruppe").forEach((g) => {
|
||
const sichtbar = [...g.querySelectorAll(".delikt")].some((e) => e.style.display !== "none");
|
||
g.style.display = sichtbar ? "" : "none";
|
||
});
|
||
});
|
||
|
||
// Person suchen
|
||
const personSuche = () => {
|
||
const feld = $("an-person");
|
||
const begriff = feld.value.trim();
|
||
if (begriff.length < 2) { meldung("Bitte mindestens zwei Zeichen.", "fehler"); return; }
|
||
personWaehlen(behaelter, begriff);
|
||
};
|
||
const personKnopf = $("an-personsuche");
|
||
if (personKnopf) personKnopf.addEventListener("click", personSuche);
|
||
const personFeld = $("an-person");
|
||
if (personFeld) personFeld.addEventListener("keydown", (e) => { if (e.key === "Enter") personSuche(); });
|
||
|
||
// Abweichung sichtbar machen
|
||
const pruefeAbweichung = () => {
|
||
if (!z.vorschlag) return;
|
||
const haft = +($("an-haft") || {}).value || 0;
|
||
const geld = +($("an-geld") || {}).value || 0;
|
||
const abweichung = haft !== z.vorschlag.haft || geld !== z.vorschlag.geld;
|
||
const block = $("an-abweichung");
|
||
if (block) block.hidden = !abweichung;
|
||
};
|
||
["an-haft", "an-geld"].forEach((id) => {
|
||
const el = $(id);
|
||
if (el) el.addEventListener("input", pruefeAbweichung);
|
||
});
|
||
|
||
// Erfassen
|
||
const speichern = $("an-speichern");
|
||
if (speichern) speichern.addEventListener("click", async () => {
|
||
if (!z.beschuldigter) { meldung("Bitte eine Person auswählen.", "fehler"); return; }
|
||
|
||
const haft = +($("an-haft") || {}).value || 0;
|
||
const geld = +($("an-geld") || {}).value || 0;
|
||
const abweichung = haft !== z.vorschlag.haft || geld !== z.vorschlag.geld;
|
||
const grund = ($("an-grund") || {}).value || "";
|
||
|
||
if (abweichung && !z.rechte.abweichen) {
|
||
meldung("Dir fehlt die Berechtigung, vom Vorschlag abzuweichen.", "fehler");
|
||
return;
|
||
}
|
||
if (abweichung && grund.trim().length < 5) {
|
||
meldung("Bitte begründe die Abweichung.", "fehler");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
await aufruf("anzeige", "erstellen", {
|
||
beschuldigter: z.beschuldigter.id,
|
||
delikte: [...z.auswahl],
|
||
haft, geld, begruendung: grund,
|
||
});
|
||
meldung("Als Entwurf erfasst — Bestätigung steht noch aus.", "gut");
|
||
z.auswahl.clear(); z.vorschlag = null; z.beschuldigter = null;
|
||
z.reiter = "vorgaenge";
|
||
rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
});
|
||
}
|
||
|
||
/** Holt den Vorschlag zur aktuellen Auswahl vom Server */
|
||
async function berechneVorschlag() {
|
||
if (!z.auswahl.size) { z.vorschlag = null; return; }
|
||
try {
|
||
z.vorschlag = await aufruf("anzeige", "vorschlag", { delikte: [...z.auswahl] });
|
||
} catch (f) { meldung(f.message, "fehler"); z.vorschlag = null; }
|
||
}
|
||
|
||
/** Personensuche über das MDT-Modul */
|
||
async function personWaehlen(behaelter, begriff) {
|
||
try {
|
||
const d = await aufruf("mdt", "personSuchen", { begriff });
|
||
const treffer = d.treffer || [];
|
||
|
||
fenster({
|
||
titel: "Person auswählen",
|
||
inhalt: treffer.length
|
||
? `<div class="liste">${treffer.map((p) =>
|
||
`<div class="liste-zeile treffer" data-wahl="${p.id}" data-name="${sicher(p.firstname + " " + p.lastname)}">
|
||
<div class="lz-haupt">
|
||
<div class="lz-titel">${sicher(p.firstname + " " + p.lastname)}</div>
|
||
<div class="lz-unter">geb. ${sicher(String(p.dob || "").slice(0, 10))}</div>
|
||
</div>
|
||
</div>`).join("")}</div>`
|
||
: `<div class="liste-leer">Keine Treffer.</div>`,
|
||
nachOeffnen: (el) => {
|
||
el.querySelectorAll("[data-wahl]").forEach((zeile) =>
|
||
zeile.addEventListener("click", () => {
|
||
z.beschuldigter = { id: +zeile.dataset.wahl, name: zeile.dataset.name };
|
||
el.remove();
|
||
erfassenAnsicht(behaelter);
|
||
}));
|
||
},
|
||
});
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
}
|
||
|
||
/* =====================================================================
|
||
VORGÄNGE
|
||
===================================================================== */
|
||
|
||
function vorgaengeAnsicht(behaelter) {
|
||
const statusArt = { entwurf: "warn", bestaetigt: "info", verworfen: "neutral" };
|
||
const statusText = { entwurf: "Entwurf", bestaetigt: "Bestätigt", verworfen: "Verworfen" };
|
||
|
||
behaelter.innerHTML = `
|
||
<div class="seiten-kopf">
|
||
<div><h2>Vorgänge</h2><p>Erfasste Anzeigen und ihr Bearbeitungsstand</p></div>
|
||
<select id="an-filter" class="katalog-suche" style="max-width:190px">
|
||
<option value="">Alle</option>
|
||
<option value="entwurf" ${z.filterStatus === "entwurf" ? "selected" : ""}>Nur Entwürfe</option>
|
||
<option value="bestaetigt" ${z.filterStatus === "bestaetigt" ? "selected" : ""}>Nur bestätigte</option>
|
||
<option value="verworfen" ${z.filterStatus === "verworfen" ? "selected" : ""}>Nur verworfene</option>
|
||
</select>
|
||
</div>
|
||
|
||
${reiterLeiste()}
|
||
|
||
<div class="karte">
|
||
${z.anzeigen.length ? `<div class="liste">${z.anzeigen.map((a) => `
|
||
<div class="liste-zeile anzeige-zeile">
|
||
<div class="lz-haupt">
|
||
<div class="lz-titel">
|
||
${sicher((a.b_vorname || "") + " " + (a.b_nachname || ""))}
|
||
${a.abweichung ? '<span class="marker warn">abweichend</span>' : ""}
|
||
</div>
|
||
<div class="lz-unter">
|
||
${sicher((a.delikte || []).map((d) => d.name).join(", ") || "—")}
|
||
</div>
|
||
<div class="lz-unter" style="margin-top:3px">
|
||
${haftText(a.haft_final)} · ${euro(a.geld_final)}
|
||
${a.abweichung ? ` <span style="opacity:.7">(Vorschlag: ${haftText(a.haft_empfohlen)} · ${euro(a.geld_empfohlen)})</span>` : ""}
|
||
· erfasst von ${sicher((a.e_vorname || "") + " " + (a.e_nachname || ""))} · ${zeit(a.erstellt_am)}
|
||
</div>
|
||
${a.begruendung ? `<div class="lz-unter" style="margin-top:3px"><i>„${sicher(a.begruendung)}"</i></div>` : ""}
|
||
</div>
|
||
<div style="display:flex;flex-direction:column;gap:6px;align-items:flex-end">
|
||
<span class="marker ${statusArt[a.status]}">${sicher(statusText[a.status] || a.status)}</span>
|
||
${a.status === "entwurf" && z.rechte.bestaetigen
|
||
? `<button class="knopf klein" data-bestaetigen="${a.id}">Bestätigen</button>` : ""}
|
||
${a.status === "entwurf"
|
||
? `<button class="knopf leise klein" data-verwerfen="${a.id}">Verwerfen</button>` : ""}
|
||
</div>
|
||
</div>`).join("")}</div>`
|
||
: `<div class="liste-leer">Keine Vorgänge.</div>`}
|
||
</div>`;
|
||
|
||
$("an-filter").addEventListener("change", (e) => {
|
||
z.filterStatus = e.target.value;
|
||
rendern(behaelter);
|
||
});
|
||
|
||
behaelter.querySelectorAll("[data-bestaetigen]").forEach((b) =>
|
||
b.addEventListener("click", () => bestaetigenDialog(behaelter, +b.dataset.bestaetigen)));
|
||
|
||
behaelter.querySelectorAll("[data-verwerfen]").forEach((b) =>
|
||
b.addEventListener("click", () => {
|
||
bestaetigen({
|
||
titel: "Entwurf verwerfen",
|
||
gefahr: true,
|
||
inhalt: `<div class="feld"><label>Grund</label>
|
||
<input id="av-grund" placeholder="Warum wird der Entwurf verworfen?"></div>`,
|
||
bestaetigenText: "Verwerfen",
|
||
bestaetigt: async (el, zu) => {
|
||
try {
|
||
await aufruf("anzeige", "verwerfen", {
|
||
id: +b.dataset.verwerfen,
|
||
grund: el.querySelector("#av-grund").value.trim(),
|
||
});
|
||
zu(); meldung("Entwurf verworfen."); rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
},
|
||
});
|
||
}));
|
||
}
|
||
|
||
/** Bestätigungsdialog — benennt genau, was ausgeführt wird */
|
||
function bestaetigenDialog(behaelter, anzeigeId) {
|
||
const a = z.anzeigen.find((x) => x.id === anzeigeId);
|
||
if (!a) return;
|
||
|
||
bestaetigen({
|
||
titel: "Anzeige bestätigen",
|
||
inhalt: `
|
||
<p style="margin:0 0 14px;font-size:13px;line-height:1.6">
|
||
<b>${sicher((a.b_vorname || "") + " " + (a.b_nachname || ""))}</b><br>
|
||
${sicher((a.delikte || []).map((d) => d.name).join(", "))}
|
||
</p>
|
||
|
||
<div class="folgen">
|
||
<div class="folgen-titel">Mit deiner Bestätigung geschieht Folgendes:</div>
|
||
<div class="folge">
|
||
<span class="folge-symbol">💶</span>
|
||
<div>Die Geldstrafe von <b>${euro(a.geld_final)}</b> wird eingezogen und
|
||
der Staatskasse zugeführt.</div>
|
||
</div>
|
||
<div class="folge">
|
||
<span class="folge-symbol">🔒</span>
|
||
<div>${a.haft_final > 0
|
||
? `Die festgelegte Haftdauer von <b>${haftText(a.haft_final)}</b> wird
|
||
<b>vermerkt</b> — eine Einweisung in Gewahrsam erfolgt dadurch <b>nicht</b>.
|
||
Das ist ein eigener Schritt.`
|
||
: "Es ist keine Haft vorgesehen."}</div>
|
||
</div>
|
||
<div class="folge">
|
||
<span class="folge-symbol">📄</span>
|
||
<div>Der Vorgang wird mit deinem Namen als bestätigend festgehalten.</div>
|
||
</div>
|
||
</div>
|
||
|
||
${a.abweichung ? `
|
||
<div class="abweichung" style="margin-top:12px">
|
||
<div class="abweichung-kopf">⚠ Abweichung vom Katalogvorschlag</div>
|
||
<div style="font-size:12px;margin-top:6px;line-height:1.5">
|
||
Vorschlag war ${haftText(a.haft_empfohlen)} · ${euro(a.geld_empfohlen)}.
|
||
${a.begruendung ? `Begründung: „${sicher(a.begruendung)}"` : "Keine Begründung hinterlegt."}
|
||
</div>
|
||
</div>` : ""}`,
|
||
bestaetigenText: "Bestätigen",
|
||
bestaetigt: async (_, zu) => {
|
||
try {
|
||
const ergebnis = await aufruf("anzeige", "bestaetigen", { id: anzeigeId });
|
||
zu();
|
||
meldung(ergebnis.eingezogen > 0
|
||
? "Bestätigt · " + euro(ergebnis.eingezogen) + " eingezogen."
|
||
: "Bestätigt.", "gut");
|
||
rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
},
|
||
});
|
||
}
|
||
|
||
/* =====================================================================
|
||
KATALOGPFLEGE
|
||
===================================================================== */
|
||
|
||
function katalogAnsicht(behaelter) {
|
||
behaelter.innerHTML = `
|
||
<div class="seiten-kopf">
|
||
<div><h2>Strafenkatalog pflegen</h2>
|
||
<p>Die Werte hier sind <b>Empfehlungen</b> — sie binden niemanden.</p></div>
|
||
<button class="knopf klein" id="kat-neu">+ Eintrag</button>
|
||
</div>
|
||
|
||
${reiterLeiste()}
|
||
|
||
<div class="karte">
|
||
${z.katalog.map((gruppe) => `
|
||
<div class="katalog-gruppe">
|
||
<div class="katalog-titel">${sicher(gruppe.kategorie)}</div>
|
||
<div class="liste">${gruppe.delikte.map((d) => `
|
||
<div class="liste-zeile">
|
||
<span class="marker neutral">${sicher(d.kennung)}</span>
|
||
<div class="lz-haupt">
|
||
<div class="lz-titel">${sicher(d.bezeichnung)}</div>
|
||
<div class="lz-unter">${d.paragraph ? sicher(d.paragraph) + " · " : ""}
|
||
${haftText(d.haft)} · ${euro(d.geld)}
|
||
${d.hinweis ? " · " + sicher(d.hinweis) : ""}</div>
|
||
</div>
|
||
<button class="knopf leise klein" data-kat-bearbeiten="${sicher(d.kennung)}">Ändern</button>
|
||
<button class="knopf leise klein" data-kat-weg="${sicher(d.kennung)}">Deaktivieren</button>
|
||
</div>`).join("")}</div>
|
||
</div>`).join("")}
|
||
</div>`;
|
||
|
||
$("kat-neu").addEventListener("click", () => katalogDialog(behaelter, null));
|
||
|
||
behaelter.querySelectorAll("[data-kat-bearbeiten]").forEach((b) =>
|
||
b.addEventListener("click", () => {
|
||
const kennung = b.dataset.katBearbeiten;
|
||
let eintrag = null;
|
||
z.katalog.forEach((g) => g.delikte.forEach((d) => { if (d.kennung === kennung) eintrag = d; }));
|
||
katalogDialog(behaelter, eintrag);
|
||
}));
|
||
|
||
behaelter.querySelectorAll("[data-kat-weg]").forEach((b) =>
|
||
b.addEventListener("click", () => {
|
||
bestaetigen({
|
||
titel: "Eintrag deaktivieren",
|
||
inhalt: `<p style="margin:0;font-size:13px;line-height:1.6">
|
||
Der Eintrag verschwindet aus der Auswahl, bleibt aber in bestehenden Anzeigen
|
||
nachvollziehbar. Gelöscht wird nichts.</p>`,
|
||
bestaetigenText: "Deaktivieren",
|
||
bestaetigt: async (_, zu) => {
|
||
try {
|
||
await aufruf("anzeige", "katalogDeaktivieren", { kennung: b.dataset.katWeg });
|
||
zu(); meldung("Eintrag deaktiviert."); rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
},
|
||
});
|
||
}));
|
||
}
|
||
|
||
function katalogDialog(behaelter, eintrag) {
|
||
const e = eintrag || {};
|
||
fenster({
|
||
titel: eintrag ? "Eintrag ändern" : "Neuer Katalogeintrag",
|
||
inhalt: `
|
||
<div class="raster k2" style="gap:10px">
|
||
<div class="feld" style="margin:0"><label>Kennung</label>
|
||
<input id="kd-kennung" value="${sicher(e.kennung || "")}"
|
||
${eintrag ? "readonly" : ""} placeholder="z. B. v6"></div>
|
||
<div class="feld" style="margin:0"><label>Kategorie</label>
|
||
<input id="kd-kategorie" value="${sicher(e.kategorie || "")}" placeholder="z. B. Verkehr"></div>
|
||
</div>
|
||
<div class="feld"><label>Bezeichnung</label>
|
||
<input id="kd-bezeichnung" value="${sicher(e.bezeichnung || "")}"></div>
|
||
<div class="feld"><label>Gesetzliche Grundlage</label>
|
||
<input id="kd-paragraph" value="${sicher(e.paragraph || "")}" placeholder="z. B. § 242 StGB"></div>
|
||
<div class="raster k2" style="gap:10px">
|
||
<div class="feld" style="margin:0"><label>Empfohlene Haft (Minuten)</label>
|
||
<input id="kd-haft" type="number" min="0" max="1440" value="${e.haft || 0}"></div>
|
||
<div class="feld" style="margin:0"><label>Empfohlene Geldstrafe (€)</label>
|
||
<input id="kd-geld" type="number" min="0" value="${e.geld || 0}"></div>
|
||
</div>
|
||
<div class="feld"><label>Hinweis für Beamte</label>
|
||
<textarea id="kd-hinweis" placeholder="Worauf ist zu achten?">${sicher(e.hinweis || "")}</textarea></div>`,
|
||
knoepfe: [
|
||
{ text: "Abbrechen", klick: (_, zu) => zu() },
|
||
{ text: "Speichern", klick: async (el, zu) => {
|
||
try {
|
||
await aufruf("anzeige", "katalogSpeichern", {
|
||
kennung: el.querySelector("#kd-kennung").value.trim(),
|
||
kategorie: el.querySelector("#kd-kategorie").value.trim(),
|
||
bezeichnung: el.querySelector("#kd-bezeichnung").value.trim(),
|
||
paragraph: el.querySelector("#kd-paragraph").value.trim(),
|
||
haft: +el.querySelector("#kd-haft").value,
|
||
geld: +el.querySelector("#kd-geld").value,
|
||
hinweis: el.querySelector("#kd-hinweis").value.trim(),
|
||
});
|
||
zu(); meldung("Gespeichert.", "gut"); rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
}},
|
||
],
|
||
});
|
||
}
|
||
|
||
/* ---- Laden -------------------------------------------------------- */
|
||
|
||
async function rendern(behaelter) {
|
||
try {
|
||
const k = await aufruf("anzeige", "katalog", {});
|
||
z.katalog = k.katalog;
|
||
z.rechte = k.rechte;
|
||
|
||
if (z.reiter === "vorgaenge") {
|
||
const v = await aufruf("anzeige", "liste", { status: z.filterStatus });
|
||
z.anzeigen = v.anzeigen;
|
||
z.rechte = Object.assign({}, z.rechte, v.rechte);
|
||
vorgaengeAnsicht(behaelter);
|
||
} else if (z.reiter === "katalog") {
|
||
katalogAnsicht(behaelter);
|
||
} else {
|
||
erfassenAnsicht(behaelter);
|
||
}
|
||
|
||
behaelter.querySelectorAll("[data-reiter]").forEach((b) =>
|
||
b.addEventListener("click", () => { z.reiter = b.dataset.reiter; rendern(behaelter); }));
|
||
|
||
} catch (f) {
|
||
behaelter.innerHTML = `<div class="seiten-kopf"><div><h2>Anzeigen</h2>
|
||
<p>Konnte nicht geladen werden: ${sicher(f.message)}</p></div></div>`;
|
||
}
|
||
}
|
||
|
||
App.registriereAnsicht("anzeige", { rendern });
|
||
})();
|