- 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>
569 lines
22 KiB
JavaScript
569 lines
22 KiB
JavaScript
/* =====================================================================
|
||
aether-police / Ansicht: Interne Kommunikation
|
||
|
||
Posteingang und Aushänge. Der Posteingang merkt sich, was du gelesen
|
||
hast — das ist Anzeige. Eine Kenntnisnahme ist dagegen eine Erklärung
|
||
und wird nur durch deinen ausdrücklichen Klick gesetzt.
|
||
===================================================================== */
|
||
"use strict";
|
||
|
||
(() => {
|
||
const { sicher, zeit, aufruf, meldung, fenster, bestaetigen } = App;
|
||
|
||
const z = {
|
||
reiter: "eingang", // eingang | gesendet | aushang | archiv
|
||
daten: null,
|
||
aushangDaten: null,
|
||
offeneNachricht: null,
|
||
offenerAushang: null,
|
||
};
|
||
|
||
// Farbgebung der Aushangkategorien
|
||
const kategorieArt = {
|
||
anweisung: "info", information: "neutral", warnung: "kritisch",
|
||
};
|
||
const kategorieSymbol = {
|
||
anweisung: "📋", information: "ℹ", warnung: "⚠",
|
||
};
|
||
|
||
/* ---- Gerüst -------------------------------------------------------- */
|
||
|
||
function reiterleiste(d) {
|
||
const eintraege = [
|
||
{ id: "eingang", text: "Posteingang", zaehler: d.ungelesen },
|
||
{ id: "gesendet", text: "Gesendet" },
|
||
{ id: "aushang", text: "Aushänge", zaehler: d.offeneKenntnis },
|
||
{ id: "archiv", text: "Archiv" },
|
||
];
|
||
|
||
return `<div class="mdt-reiter">
|
||
${eintraege.map((e) => `
|
||
<button class="${z.reiter === e.id ? "aktiv" : ""}" data-reiter="${e.id}">
|
||
${sicher(e.text)}
|
||
${e.zaehler ? `<span class="komm-zaehler">${e.zaehler}</span>` : ""}
|
||
</button>`).join("")}
|
||
</div>`;
|
||
}
|
||
|
||
/* ---- Posteingang --------------------------------------------------- */
|
||
|
||
function postliste(behaelter) {
|
||
const d = z.daten;
|
||
const gesendet = z.reiter === "gesendet";
|
||
|
||
behaelter.innerHTML = `
|
||
<div class="seiten-kopf">
|
||
<div>
|
||
<h2>Kommunikation</h2>
|
||
<p>Nachrichten und Aushänge der Behörde. Zugestellt wird automatisch —
|
||
gelesen, beantwortet und zur Kenntnis genommen wird von Hand.</p>
|
||
</div>
|
||
${d.rechte.senden
|
||
? `<button class="knopf" data-neu="1">✉ Nachricht verfassen</button>` : ""}
|
||
</div>
|
||
|
||
${reiterleiste(d)}
|
||
|
||
<div class="liste">
|
||
${(d.nachrichten || []).length
|
||
? d.nachrichten.map((n) => postzeile(n, gesendet)).join("")
|
||
: `<div class="liste-leer">${gesendet
|
||
? "Du hast noch nichts gesendet."
|
||
: "Keine Nachrichten."}</div>`}
|
||
</div>
|
||
`;
|
||
|
||
verdrahtePost(behaelter);
|
||
}
|
||
|
||
function postzeile(n, gesendet) {
|
||
const typMarker = n.empf_typ === "rundmeldung"
|
||
? `<span class="marker kritisch">Rundmeldung</span>`
|
||
: n.empf_typ === "abteilung"
|
||
? `<span class="marker info">Abteilung</span>` : "";
|
||
|
||
return `<div class="liste-zeile komm-zeile ${n.gelesen ? "" : "ungelesen"}"
|
||
data-nachricht="${n.id}">
|
||
<span class="komm-punkt ${n.gelesen ? "" : "neu"}"></span>
|
||
<div class="lz-haupt">
|
||
<div class="lz-titel">${sicher(n.betreff || "(kein Betreff)")}</div>
|
||
<div class="lz-unter">
|
||
${gesendet ? "an " + sicher(n.empfaengerName)
|
||
: "von " + sicher(n.absenderName)
|
||
+ (n.dienstnummer ? " · " + sicher(n.dienstnummer) : "")}
|
||
— ${sicher(n.vorschau)}
|
||
</div>
|
||
</div>
|
||
${typMarker}
|
||
<span class="komm-zeit">${zeit(n.gesendet)}</span>
|
||
</div>`;
|
||
}
|
||
|
||
function verdrahtePost(behaelter) {
|
||
verdrahteReiter(behaelter);
|
||
|
||
behaelter.querySelectorAll("[data-nachricht]").forEach((el) => {
|
||
el.addEventListener("click", () => nachrichtOeffnen(behaelter, +el.dataset.nachricht));
|
||
});
|
||
|
||
const neu = behaelter.querySelector("[data-neu]");
|
||
if (neu) neu.addEventListener("click", () => verfassen(behaelter));
|
||
}
|
||
|
||
function verdrahteReiter(behaelter) {
|
||
behaelter.querySelectorAll("[data-reiter]").forEach((el) => {
|
||
el.addEventListener("click", () => {
|
||
z.reiter = el.dataset.reiter;
|
||
rendern(behaelter);
|
||
});
|
||
});
|
||
}
|
||
|
||
/* ---- Nachricht lesen ------------------------------------------------ */
|
||
|
||
async function nachrichtOeffnen(behaelter, id) {
|
||
try {
|
||
const n = await aufruf("kommunikation", "nachrichtOeffnen", { id });
|
||
|
||
fenster({
|
||
titel: n.betreff || "(kein Betreff)",
|
||
inhalt: `
|
||
<div class="komm-kopf">
|
||
<div>
|
||
<b>${sicher(n.absenderName)}</b>
|
||
<span class="komm-kopf-unter">
|
||
${sicher(n.dienstnummer || "")} ${n.rolleName ? "· " + sicher(n.rolleName) : ""}
|
||
</span>
|
||
</div>
|
||
<span class="komm-zeit">${zeit(n.gesendet)}</span>
|
||
</div>
|
||
${n.empfaengerName
|
||
? `<p class="feld-hinweis">Gerichtet an: ${sicher(n.empfaengerName)}</p>` : ""}
|
||
<div class="komm-text">${sicher(n.text).replace(/\n/g, "<br>")}</div>`,
|
||
knoepfe: n.antwortAn
|
||
? [{ text: "Antworten", klick: (_, zu) => {
|
||
zu();
|
||
verfassen(behaelter, {
|
||
empfTyp: "person", empfaenger: n.antwortAn,
|
||
empfaengerName: n.absenderName,
|
||
betreff: n.betreff && n.betreff.startsWith("Re:")
|
||
? n.betreff : "Re: " + (n.betreff || ""),
|
||
});
|
||
} },
|
||
{ text: "Schließen", art: "leise", klick: (_, zu) => zu() }]
|
||
: [{ text: "Schließen", art: "leise", klick: (_, zu) => zu() }],
|
||
});
|
||
|
||
// Der Lesestatus hat sich geändert — Liste auffrischen
|
||
rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
}
|
||
|
||
/* ---- Nachricht verfassen -------------------------------------------- */
|
||
|
||
async function verfassen(behaelter, vorgabe) {
|
||
vorgabe = vorgabe || {};
|
||
|
||
let empfaengerDaten;
|
||
try {
|
||
empfaengerDaten = await aufruf("kommunikation", "empfaenger", {});
|
||
} catch (f) { return meldung(f.message, "fehler"); }
|
||
|
||
let gewaehlt = vorgabe.empfaenger
|
||
? { typ: vorgabe.empfTyp, id: vorgabe.empfaenger, name: vorgabe.empfaengerName }
|
||
: null;
|
||
|
||
fenster({
|
||
titel: "Nachricht verfassen",
|
||
inhalt: `
|
||
<div class="feld"><label>Empfänger</label>
|
||
<div class="komm-empf-arten">
|
||
<button class="pv-schalter gewaehlt" data-art="person">Beamter</button>
|
||
<button class="pv-schalter" data-art="abteilung">Abteilung</button>
|
||
${empfaengerDaten.rundmeldung
|
||
? `<button class="pv-schalter" data-art="rundmeldung">Alle Einheiten</button>` : ""}
|
||
</div>
|
||
<div data-empfbereich="1"></div>
|
||
</div>
|
||
<div class="feld"><label>Betreff</label>
|
||
<input name="betreff" maxlength="160"
|
||
value="${sicher(vorgabe.betreff || "")}" placeholder="Worum geht es?"></div>
|
||
<div class="feld"><label>Nachricht</label>
|
||
<textarea name="text" rows="7" placeholder="Text der Nachricht…"></textarea></div>`,
|
||
knoepfe: [
|
||
{ text: "Abbrechen", art: "leise", klick: (_, zu) => zu() },
|
||
{ text: "Senden", klick: async (el, zu) => {
|
||
if (!gewaehlt) return meldung("Bitte einen Empfänger wählen.", "fehler");
|
||
try {
|
||
await aufruf("kommunikation", "senden", {
|
||
empfTyp: gewaehlt.typ,
|
||
empfaenger: gewaehlt.id,
|
||
betreff: el.querySelector("[name=betreff]").value.trim(),
|
||
text: el.querySelector("[name=text]").value.trim(),
|
||
});
|
||
zu();
|
||
meldung("Nachricht gesendet.", "gut");
|
||
rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
} },
|
||
],
|
||
nachOeffnen: (hg) => {
|
||
const bereich = hg.querySelector("[data-empfbereich]");
|
||
|
||
/** Zeichnet den Empfängerbereich passend zur gewählten Art */
|
||
function zeichne(art) {
|
||
hg.querySelectorAll("[data-art]").forEach((b) =>
|
||
b.classList.toggle("gewaehlt", b.dataset.art === art));
|
||
|
||
if (art === "rundmeldung") {
|
||
gewaehlt = { typ: "rundmeldung", id: "alle", name: "Alle Einheiten" };
|
||
bereich.innerHTML = `<div class="warnung">
|
||
<span>📣</span>
|
||
<div>Diese Nachricht erreicht <b>jeden Beamten</b> der Behörde.</div>
|
||
</div>`;
|
||
return;
|
||
}
|
||
|
||
if (art === "abteilung") {
|
||
gewaehlt = null;
|
||
bereich.innerHTML = `<select name="abt">
|
||
<option value="">— Abteilung wählen —</option>
|
||
${empfaengerDaten.abteilungen.map((a) =>
|
||
`<option value="${sicher(a.id)}">${sicher(a.name)}</option>`).join("")}
|
||
</select>`;
|
||
bereich.querySelector("[name=abt]").addEventListener("change", (e) => {
|
||
gewaehlt = e.target.value
|
||
? { typ: "abteilung", id: e.target.value, name: e.target.value } : null;
|
||
});
|
||
return;
|
||
}
|
||
|
||
// Einzelner Beamter: Liste mit Suchfeld
|
||
bereich.innerHTML = `
|
||
<input name="suche" placeholder="Beamten suchen…">
|
||
<div class="liste komm-empfliste" data-liste="1"></div>`;
|
||
|
||
const liste = bereich.querySelector("[data-liste]");
|
||
const suchfeld = bereich.querySelector("[name=suche]");
|
||
|
||
function zeichneListe(begriff) {
|
||
const gefiltert = empfaengerDaten.beamte.filter((b) => {
|
||
if (!begriff) return true;
|
||
const text = (b.firstname + " " + b.lastname + " " + b.dienstnummer).toLowerCase();
|
||
return text.includes(begriff.toLowerCase());
|
||
}).slice(0, 40);
|
||
|
||
liste.innerHTML = gefiltert.length
|
||
? gefiltert.map((b) => `
|
||
<div class="liste-zeile ${gewaehlt && gewaehlt.id === b.character ? "gewaehlt" : ""}"
|
||
data-beamter="${b.character}">
|
||
<span class="pv-punkt ${b.imDienst ? "an" : ""}"
|
||
title="${b.imDienst ? "Im Dienst" : "Außer Dienst"}"></span>
|
||
<span class="pv-nummer">${sicher(b.dienstnummer)}</span>
|
||
<div class="lz-haupt">
|
||
<div class="lz-titel">${sicher(b.firstname)} ${sicher(b.lastname)}</div>
|
||
<div class="lz-unter">${sicher(b.rolleName)}</div>
|
||
</div>
|
||
</div>`).join("")
|
||
: `<div class="liste-leer">Kein Beamter gefunden.</div>`;
|
||
|
||
liste.querySelectorAll("[data-beamter]").forEach((el) => {
|
||
el.addEventListener("click", () => {
|
||
const b = empfaengerDaten.beamte.find((x) => x.character === +el.dataset.beamter);
|
||
gewaehlt = { typ: "person", id: b.character,
|
||
name: b.firstname + " " + b.lastname };
|
||
liste.querySelectorAll("[data-beamter]")
|
||
.forEach((zeile) => zeile.classList.remove("gewaehlt"));
|
||
el.classList.add("gewaehlt");
|
||
});
|
||
});
|
||
}
|
||
|
||
zeichneListe(gewaehlt && gewaehlt.name ? gewaehlt.name : "");
|
||
suchfeld.addEventListener("input", () => zeichneListe(suchfeld.value.trim()));
|
||
}
|
||
|
||
hg.querySelectorAll("[data-art]").forEach((b) =>
|
||
b.addEventListener("click", () => zeichne(b.dataset.art)));
|
||
|
||
zeichne(gewaehlt ? gewaehlt.typ : "person");
|
||
},
|
||
});
|
||
}
|
||
|
||
/* ---- Aushänge -------------------------------------------------------- */
|
||
|
||
function aushangListe(behaelter) {
|
||
const d = z.aushangDaten;
|
||
|
||
behaelter.innerHTML = `
|
||
<div class="seiten-kopf">
|
||
<div>
|
||
<h2>${d.archiv ? "Archivierte Aushänge" : "Aushänge"}</h2>
|
||
<p>Dienstanweisungen und Mitteilungen der Leitung. Eine Kenntnisnahme
|
||
bestätigst ausschließlich du selbst.</p>
|
||
</div>
|
||
${d.rechte.schreiben && !d.archiv
|
||
? `<button class="knopf" data-neu-aushang="1">+ Aushang verfassen</button>` : ""}
|
||
</div>
|
||
|
||
${reiterleiste(z.daten || { ungelesen: 0, offeneKenntnis: 0 })}
|
||
|
||
<div class="komm-aushaenge">
|
||
${(d.aushaenge || []).length
|
||
? d.aushaenge.map((a) => aushangKarte(a, d)).join("")
|
||
: `<div class="liste-leer">${d.archiv
|
||
? "Das Archiv ist leer." : "Zurzeit hängt nichts aus."}</div>`}
|
||
</div>
|
||
`;
|
||
|
||
verdrahteAushaenge(behaelter);
|
||
}
|
||
|
||
function aushangKarte(a, d) {
|
||
const art = kategorieArt[a.kategorie] || "neutral";
|
||
const offen = a.betroffen - a.kenntnisse;
|
||
|
||
return `<div class="karte komm-aushang ${a.angeheftet ? "angeheftet" : ""}">
|
||
<div class="karte-kopf">
|
||
<span class="karte-titel">
|
||
${kategorieSymbol[a.kategorie] || ""} ${sicher(a.titel)}
|
||
</span>
|
||
<div class="komm-aushang-marker">
|
||
${a.angeheftet ? `<span class="marker warn">angeheftet</span>` : ""}
|
||
<span class="marker ${art}">${sicher(a.kategorieName)}</span>
|
||
${a.abteilungName
|
||
? `<span class="marker neutral">${sicher(a.abteilungName)}</span>` : ""}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="komm-text">${sicher(a.text).replace(/\n/g, "<br>")}</div>
|
||
|
||
<div class="komm-aushang-fuss">
|
||
<span class="feld-hinweis">
|
||
${sicher(a.autor)}${a.dienstnummer ? " (" + sicher(a.dienstnummer) + ")" : ""}
|
||
· ${zeit(a.erstellt_am)}
|
||
${a.frist ? ` · Kenntnisnahme bis ${zeit(a.frist)}` : ""}
|
||
</span>
|
||
<div class="komm-aushang-aktionen">
|
||
${a.kenntnispflicht ? (a.bestaetigt
|
||
? `<span class="marker gut">✓ zur Kenntnis genommen</span>`
|
||
: (a.aktiv === 1
|
||
? `<button class="knopf klein" data-kenntnis="${a.id}">
|
||
Kenntnis bestätigen</button>` : "")) : ""}
|
||
${a.kenntnispflicht && d.rechte.schreiben
|
||
? `<button class="knopf leise klein" data-stand="${a.id}">
|
||
${a.kenntnisse}/${a.betroffen} bestätigt${offen > 0 ? " · " + offen + " offen" : ""}
|
||
</button>` : ""}
|
||
${d.rechte.schreiben && a.aktiv === 1
|
||
? `<button class="knopf leise klein" data-anheften="${a.id}"
|
||
data-wert="${a.angeheftet ? "0" : "1"}">
|
||
${a.angeheftet ? "Loslösen" : "Anheften"}</button>` : ""}
|
||
${d.rechte.archivieren && a.aktiv === 1
|
||
? `<button class="knopf leise klein" data-archiv="${a.id}">Archivieren</button>` : ""}
|
||
</div>
|
||
</div>
|
||
</div>`;
|
||
}
|
||
|
||
function verdrahteAushaenge(behaelter) {
|
||
verdrahteReiter(behaelter);
|
||
|
||
const neu = behaelter.querySelector("[data-neu-aushang]");
|
||
if (neu) neu.addEventListener("click", () => aushangVerfassen(behaelter));
|
||
|
||
behaelter.querySelectorAll("[data-kenntnis]").forEach((el) => {
|
||
el.addEventListener("click", () => kenntnisBestaetigen(behaelter, +el.dataset.kenntnis));
|
||
});
|
||
|
||
behaelter.querySelectorAll("[data-stand]").forEach((el) => {
|
||
el.addEventListener("click", () => kenntnisStand(+el.dataset.stand));
|
||
});
|
||
|
||
behaelter.querySelectorAll("[data-anheften]").forEach((el) => {
|
||
el.addEventListener("click", async () => {
|
||
try {
|
||
await aufruf("kommunikation", "anheften", {
|
||
id: +el.dataset.anheften, angeheftet: el.dataset.wert === "1",
|
||
});
|
||
rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
});
|
||
});
|
||
|
||
behaelter.querySelectorAll("[data-archiv]").forEach((el) => {
|
||
el.addEventListener("click", () => aushangArchivieren(behaelter, +el.dataset.archiv));
|
||
});
|
||
}
|
||
|
||
/* ---- Kenntnisnahme ---------------------------------------------------- */
|
||
|
||
function kenntnisBestaetigen(behaelter, id) {
|
||
// Bewusst über den Bestätigungsdialog: das ist eine Erklärung, kein Klick
|
||
bestaetigen({
|
||
titel: "Kenntnisnahme bestätigen",
|
||
inhalt: `<p>Du erklärst damit, den Aushang gelesen und verstanden zu haben.
|
||
Die Bestätigung wird mit deinem Namen und der Uhrzeit vermerkt.</p>`,
|
||
bestaetigenText: "Zur Kenntnis genommen",
|
||
bestaetigt: async (_, zu) => {
|
||
try {
|
||
await aufruf("kommunikation", "kenntnisnahme", { id });
|
||
zu();
|
||
meldung("Kenntnisnahme vermerkt.", "gut");
|
||
rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
},
|
||
});
|
||
}
|
||
|
||
async function kenntnisStand(id) {
|
||
try {
|
||
const d = await aufruf("kommunikation", "kenntnisStand", { id });
|
||
|
||
fenster({
|
||
titel: "Stand der Kenntnisnahmen",
|
||
inhalt: `
|
||
<p><b>${sicher(d.titel)}</b></p>
|
||
<p class="feld-hinweis">${d.gesamt - d.offen} von ${d.gesamt} Beamten haben bestätigt.
|
||
${d.frist ? "Frist: " + zeit(d.frist) + "." : ""}
|
||
Das System erinnert nur — es bestätigt für niemanden.</p>
|
||
<div class="liste komm-empfliste">
|
||
${d.beamte.map((b) => `
|
||
<div class="liste-zeile">
|
||
<span class="pv-nummer">${sicher(b.dienstnummer)}</span>
|
||
<div class="lz-haupt"><div class="lz-titel">${sicher(b.name)}</div></div>
|
||
${b.bestaetigt_am
|
||
? `<span class="marker gut">${zeit(b.bestaetigt_am)}</span>`
|
||
: `<span class="marker warn">offen</span>`}
|
||
</div>`).join("")}
|
||
</div>`,
|
||
});
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
}
|
||
|
||
/* ---- Aushang verfassen und archivieren -------------------------------- */
|
||
|
||
function aushangVerfassen(behaelter) {
|
||
const d = z.aushangDaten;
|
||
|
||
bestaetigen({
|
||
titel: "Aushang verfassen",
|
||
inhalt: `
|
||
<div class="feld"><label>Titel</label>
|
||
<input name="titel" maxlength="160" placeholder="Überschrift des Aushangs"></div>
|
||
<div class="feld"><label>Art</label>
|
||
<select name="kategorie">
|
||
${Object.keys(d.kategorien).map((k) =>
|
||
`<option value="${sicher(k)}">${sicher(d.kategorien[k])}</option>`).join("")}
|
||
</select></div>
|
||
<div class="feld"><label>Gilt für</label>
|
||
<select name="abteilung">
|
||
<option value="">Gesamte Behörde</option>
|
||
${d.abteilungen.map((a) =>
|
||
`<option value="${sicher(a.id)}">${sicher(a.name)}</option>`).join("")}
|
||
</select></div>
|
||
<div class="feld"><label>Inhalt</label>
|
||
<textarea name="text" rows="8" placeholder="Text des Aushangs…"></textarea></div>
|
||
<div class="feld">
|
||
<label><input type="checkbox" name="angeheftet" class="komm-haken"> Oben anheften</label>
|
||
</div>
|
||
<div class="feld">
|
||
<label><input type="checkbox" name="kenntnispflicht" class="komm-haken">
|
||
Kenntnisnahme verlangen</label>
|
||
<div class="feld-hinweis">Beamte bestätigen selbst. Das System erinnert nur
|
||
an offene Bestätigungen und setzt nie eine.</div>
|
||
</div>
|
||
<div class="feld" data-fristfeld="1" style="display:none">
|
||
<label>Frist für die Kenntnisnahme (Minuten)</label>
|
||
<input name="frist" type="number" min="5" max="20160" value="1440">
|
||
</div>`,
|
||
bestaetigenText: "Veröffentlichen",
|
||
bestaetigt: async (el, zu) => {
|
||
const kenntnispflicht = el.querySelector("[name=kenntnispflicht]").checked;
|
||
const frist = +el.querySelector("[name=frist]").value;
|
||
try {
|
||
await aufruf("kommunikation", "aushangErstellen", {
|
||
titel: el.querySelector("[name=titel]").value.trim(),
|
||
text: el.querySelector("[name=text]").value.trim(),
|
||
kategorie: el.querySelector("[name=kategorie]").value,
|
||
abteilung: el.querySelector("[name=abteilung]").value,
|
||
angeheftet: el.querySelector("[name=angeheftet]").checked,
|
||
kenntnispflicht,
|
||
fristMinuten: kenntnispflicht && frist > 0 ? frist : null,
|
||
});
|
||
zu();
|
||
meldung("Aushang veröffentlicht.", "gut");
|
||
rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
},
|
||
nachOeffnen: (hg) => {
|
||
// Das Fristfeld ist nur sinnvoll, wenn eine Kenntnisnahme verlangt wird
|
||
const haken = hg.querySelector("[name=kenntnispflicht]");
|
||
const fristfeld = hg.querySelector("[data-fristfeld]");
|
||
haken.addEventListener("change", () => {
|
||
fristfeld.style.display = haken.checked ? "" : "none";
|
||
});
|
||
},
|
||
});
|
||
}
|
||
|
||
function aushangArchivieren(behaelter, id) {
|
||
bestaetigen({
|
||
titel: "Aushang archivieren",
|
||
gefahr: true,
|
||
inhalt: `<p>Der Aushang verschwindet vom Brett und wandert ins Archiv.
|
||
Bereits bestätigte Kenntnisnahmen bleiben erhalten.</p>
|
||
<div class="feld"><label>Grund (freiwillig)</label>
|
||
<input name="grund" placeholder="Warum wird der Aushang abgenommen?"></div>`,
|
||
bestaetigenText: "Archivieren",
|
||
bestaetigt: async (el, zu) => {
|
||
try {
|
||
await aufruf("kommunikation", "aushangArchivieren", {
|
||
id, grund: el.querySelector("[name=grund]").value.trim(),
|
||
});
|
||
zu();
|
||
meldung("Aushang archiviert.", "gut");
|
||
rendern(behaelter);
|
||
} catch (f) { meldung(f.message, "fehler"); }
|
||
},
|
||
});
|
||
}
|
||
|
||
/* ---- Laden ------------------------------------------------------------- */
|
||
|
||
async function rendern(behaelter) {
|
||
try {
|
||
// Die Zähler der Reiterleiste kommen immer aus der Übersicht
|
||
z.daten = await aufruf("kommunikation", "uebersicht", {
|
||
ordner: z.reiter === "gesendet" ? "gesendet" : "eingang",
|
||
});
|
||
|
||
// Sprungziel aus einem Hinweis, z. B. „Neuer Aushang"
|
||
const ziel = App.zustand.modulZiel;
|
||
if (ziel && ziel.aushang) {
|
||
App.zustand.modulZiel = null;
|
||
z.reiter = "aushang";
|
||
} else if (ziel && ziel.nachricht) {
|
||
App.zustand.modulZiel = null;
|
||
z.reiter = "eingang";
|
||
postliste(behaelter);
|
||
return nachrichtOeffnen(behaelter, ziel.nachricht);
|
||
}
|
||
|
||
if (z.reiter === "aushang" || z.reiter === "archiv") {
|
||
z.aushangDaten = await aufruf("kommunikation", "aushaenge", {
|
||
archiv: z.reiter === "archiv",
|
||
});
|
||
aushangListe(behaelter);
|
||
} else {
|
||
postliste(behaelter);
|
||
}
|
||
} catch (f) {
|
||
behaelter.innerHTML = `<div class="seiten-kopf"><div><h2>Kommunikation</h2>
|
||
<p>Konnte nicht geladen werden: ${sicher(f.message)}</p></div></div>`;
|
||
}
|
||
}
|
||
|
||
App.registriereAnsicht("kommunikation", { rendern });
|
||
})();
|