/* =====================================================================
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 ? `
` : ""}
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 []; }
});
})();