/* ===================================================================== aether-police / Ansicht: Beweisverwaltung Es gibt keine automatische Spurenerkennung — jeder Beweis wird hier von einem Beamten erfasst und beschrieben. ===================================================================== */ "use strict"; (() => { const { sicher, zeit, aufruf, meldung, fenster, bestaetigen, $ } = App; const z = { daten: null, offen: null, // geöffneter Beweis filterArt: "", suche: "", }; const artSymbol = { foto: "📷", video: "🎬", fingerabdruck: "🖐", dna: "🧬", huelse: "🔩", dokument: "📄", aussage: "🗣", gegenstand: "📦", }; function artName(id) { const treffer = (z.daten.arten || []).find((a) => a.id === id); return treffer ? treffer.name : id; } /* ---- Übersicht ---------------------------------------------------- */ function uebersicht(behaelter) { const d = z.daten; behaelter.innerHTML = `

Beweismittel

Jeder Beweis wird von einem Beamten erfasst — es gibt keine automatische Spurenerkennung.

${d.rechte.erfassen ? `` : ""}
${d.beweise.length ? `
${d.beweise.map((b) => `
${artSymbol[b.art] || "•"}
${sicher(b.bezeichnung)}
${sicher(b.kennung)} · ${sicher(artName(b.art))} ${b.fundort ? " · Fundort: " + sicher(b.fundort) : ""} ${b.aktenzeichen ? " · " + sicher(b.aktenzeichen) : ""}
erfasst von ${sicher(b.dienstnummer || "")} ${sicher((b.firstname || "") + " " + (b.lastname || ""))} · ${zeit(b.erfasst_am)}
${b.datei ? 'Anlage' : ""}
`).join("")}
` : `
Keine Beweismittel gefunden.
`}
`; const neu = $("bw-neu"); if (neu) neu.addEventListener("click", () => erfassenDialog(behaelter)); const suchfeld = $("bw-suche"); suchfeld.addEventListener("keydown", (e) => { if (e.key === "Enter") { z.suche = suchfeld.value.trim(); rendern(behaelter); } }); $("bw-art").addEventListener("change", (e) => { z.filterArt = e.target.value; rendern(behaelter); }); behaelter.querySelectorAll("[data-beweis]").forEach((el) => el.addEventListener("click", () => oeffne(behaelter, +el.dataset.beweis))); } /* ---- Einzelansicht ------------------------------------------------ */ function einzelAnsicht(behaelter, d) { const b = d.beweis; behaelter.innerHTML = `

${artSymbol[b.art] || "•"} ${sicher(b.bezeichnung)}

${sicher(b.kennung)} · ${sicher(artName(b.art))}

${d.rechte.loeschen ? `` : ""}
Beweismittel
Kennung${sicher(b.kennung)} Art${sicher(artName(b.art))} Fundort${sicher(b.fundort || "—")} Aufgefunden${zeit(b.gefunden_am)} Erfasst von${sicher(b.dienstnummer || "")} ${sicher((b.firstname || "") + " " + (b.lastname || ""))} Erfasst am${zeit(b.erfasst_am)}
${b.beschreibung ? `
${sicher(b.beschreibung).replace(/\n/g, "
")}
` : ""} ${b.datei ? `
📎 Anlage öffnen
` : ""}
Verknüpfte Akten
${d.akten.length ? `
${d.akten.map((a) => `
${sicher(a.typ)}
${sicher(a.titel)}
${sicher(a.aktenzeichen)}
${sicher(a.status)}
`).join("")}
` : `
Noch keiner Akte zugeordnet.
`}
Zugehörige Asservate
${d.asservate.length ? `
${d.asservate.map((a) => `
${sicher(a.kennung)}
${sicher(a.bezeichnung)}
${sicher(a.lagerort)}
${sicher(a.status)}
`).join("")}
` : `
Keine Asservate hinterlegt.
`}
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.
`}
`; $("bw-zurueck").addEventListener("click", () => { z.offen = null; rendern(behaelter); }); $("bw-bearbeiten").addEventListener("click", () => { fenster({ titel: "Beweis ändern", inhalt: `
`, knoepfe: [ { text: "Abbrechen", klick: (_, zu) => zu() }, { text: "Speichern", klick: async (el, zu) => { try { await aufruf("beweis", "bearbeiten", { id: b.id, bezeichnung: el.querySelector("#be-bez").value.trim(), fundort: el.querySelector("#be-ort").value.trim(), beschreibung: el.querySelector("#be-text").value, datei: el.querySelector("#be-datei").value.trim(), grund: el.querySelector("#be-grund").value.trim(), }); zu(); meldung("Gespeichert.", "gut"); oeffne(behaelter, b.id); } catch (f) { meldung(f.message, "fehler"); } }}, ], }); }); const entfernen = $("bw-entfernen"); if (entfernen) entfernen.addEventListener("click", () => { bestaetigen({ titel: "Beweismittel entfernen", gefahr: true, inhalt: `
⚠
Der Beweis ${sicher(b.kennung)} wird aus dem Bestand entfernt. Der Vorgang bleibt im Protokoll sichtbar und alle Beamten werden benachrichtigt.
`, bestaetigenText: "Entfernen", bestaetigt: async (el, zu) => { try { await aufruf("beweis", "entfernen", { id: b.id, grund: el.querySelector("#bl-grund").value.trim(), }); zu(); meldung("Beweis entfernt."); z.offen = null; rendern(behaelter); } catch (f) { meldung(f.message, "fehler"); } }, }); }); $("bw-akte").addEventListener("click", () => akteVerknuepfenDialog(behaelter, b.id)); } /* ---- Dialoge ------------------------------------------------------ */ function erfassenDialog(behaelter) { const arten = (z.daten.arten || []); fenster({ titel: "Beweis erfassen", inhalt: `
ℹ
Der Fundort ist Pflicht — er gehört zur Beweiskette. Das System erkennt keine Spuren; alles hier ist deine Dokumentation.
`, knoepfe: [ { text: "Abbrechen", klick: (_, zu) => zu() }, { text: "Erfassen", klick: async (el, zu) => { try { const ergebnis = await aufruf("beweis", "erfassen", { art: el.querySelector("#bn-art").value, bezeichnung: el.querySelector("#bn-bez").value.trim(), beschreibung: el.querySelector("#bn-text").value, fundort: el.querySelector("#bn-ort").value.trim(), datei: el.querySelector("#bn-datei").value.trim(), }); zu(); meldung("Beweis " + ergebnis.kennung + " erfasst.", "gut"); oeffne(behaelter, ergebnis.id); } catch (f) { meldung(f.message, "fehler"); } }}, ], }); } function akteVerknuepfenDialog(behaelter, beweisId) { fenster({ titel: "Mit Akte verknüpfen", inhalt: `
`, nachOeffnen: (el) => { const suchen = async () => { const begriff = el.querySelector("#bv-suche").value.trim(); if (begriff.length < 2) { meldung("Bitte mindestens zwei Zeichen.", "fehler"); return; } try { const d = await aufruf("beweis", "aktenSuche", { begriff }); const ziel = el.querySelector("#bv-treffer"); ziel.innerHTML = (d.akten || []).length ? `
${d.akten.map((a) => `
${sicher(a.typ)}
${sicher(a.titel)}
${sicher(a.aktenzeichen)}
`).join("")}
` : `
Keine Treffer.
`; ziel.querySelectorAll("[data-wahl]").forEach((zeile) => zeile.addEventListener("click", async () => { try { await aufruf("beweis", "akteVerknuepfen", { beweisId, akteId: +zeile.dataset.wahl, }); el.remove(); meldung("Verknüpft.", "gut"); oeffne(behaelter, beweisId); } catch (f) { meldung(f.message, "fehler"); } })); } catch (f) { meldung(f.message, "fehler"); } }; el.querySelector("#bv-suchen").addEventListener("click", suchen); el.querySelector("#bv-suche").addEventListener("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); suchen(); } }); }, }); } /* ---- Laden -------------------------------------------------------- */ async function oeffne(behaelter, beweisId) { try { const d = await aufruf("beweis", "oeffnen", { id: beweisId }); z.offen = beweisId; einzelAnsicht(behaelter, d); } catch (f) { meldung(f.message, "fehler"); } } async function rendern(behaelter) { try { z.daten = await aufruf("beweis", "liste", { art: z.filterArt, begriff: z.suche }); if (z.offen) await oeffne(behaelter, z.offen); else uebersicht(behaelter); } catch (f) { behaelter.innerHTML = `

Beweise

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

`; } } App.registriereAnsicht("beweis", { rendern }); /* Globale Suche bedienen */ App.registriereSuche(async (begriff) => { try { const d = await aufruf("beweis", "liste", { begriff }); return (d.beweise || []).slice(0, 5).map((b) => ({ bereich: "Beweis", titel: b.bezeichnung, unter: b.kennung + " · " + (b.fundort || ""), })); } catch (e) { return []; } }); })();