- 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>
157 lines
5.7 KiB
JavaScript
157 lines
5.7 KiB
JavaScript
/* =====================================================================
|
|
aether-medical — Bausteine für die Bögen
|
|
|
|
Setzt vorformulierten Text in ein Formular ein. Mehr tut diese Datei
|
|
nicht, und mehr soll sie nicht tun:
|
|
|
|
* Sie schreibt nichts zum Server. Der Text landet im Textfeld,
|
|
gespeichert wird danach von Hand.
|
|
* Sie überschreibt nichts. Steht schon etwas im Feld, wird der
|
|
Baustein darunter gehängt.
|
|
* Sie wählt nichts aus. Welcher Baustein passt, entscheidet der,
|
|
der ihn einsetzt.
|
|
===================================================================== */
|
|
"use strict";
|
|
|
|
const Bausteine = (() => {
|
|
const { sicher, aufruf, meldung, bestaetigen } = App;
|
|
|
|
/**
|
|
* Hängt Text an ein Textfeld an, ohne Vorhandenes zu verlieren.
|
|
* @param {HTMLTextAreaElement} feld Zielfeld
|
|
* @param {string} text Einzusetzender Text
|
|
*/
|
|
function anhaengen(feld, text) {
|
|
const vorhanden = feld.value.trim();
|
|
feld.value = vorhanden ? vorhanden + "\n\n" + text : text;
|
|
}
|
|
|
|
/**
|
|
* Öffnet die Bausteinauswahl für einen Bogen.
|
|
* @param {object} o {
|
|
* modul Modulkennung, unter der die Aktion liegt
|
|
* behaelter Element, in dem die Textfelder stehen
|
|
* nachEinsetzen Rückruf nach dem Einsetzen (freiwillig)
|
|
* }
|
|
*/
|
|
async function einsetzen(o) {
|
|
let d;
|
|
try {
|
|
d = await aufruf(o.modul, "bausteine", {});
|
|
} catch (fehler) { return meldung(fehler.message, "fehler"); }
|
|
|
|
if (!d.bausteine.length) {
|
|
return meldung("Für diesen Bogen ist kein Baustein hinterlegt.", "fehler");
|
|
}
|
|
|
|
let gewaehlt = null; // aktuell gewählter Baustein
|
|
const abgewaehlt = {}; // Felder, die der Nutzer ausgenommen hat
|
|
|
|
bestaetigen({
|
|
titel: "Baustein einsetzen",
|
|
inhalt: `
|
|
<div class="warnung">
|
|
<span class="w-symbol">🧩</span>
|
|
<div>Der Baustein füllt nur das Formular. <b>Gespeichert wird nichts</b> —
|
|
das machst du danach selbst. Text, der schon im Feld steht, bleibt
|
|
erhalten; der Baustein kommt darunter.</div>
|
|
</div>
|
|
|
|
<div class="feld"><label>Baustein für den ${sicher(d.bereichName)}</label>
|
|
<div class="liste bs-liste" data-bausteine="1">
|
|
${d.bausteine.map((b) => `
|
|
<div class="liste-zeile" data-baustein="${b.id}">
|
|
<span class="db-nummer">${sicher(b.kennung)}</span>
|
|
<div class="lz-haupt">
|
|
<div class="lz-titel">${sicher(b.name)}</div>
|
|
<div class="lz-unter">${d.felder
|
|
.filter((f) => b.inhalt[f.id])
|
|
.map((f) => sicher(f.name)).join(" · ") || "leer"}</div>
|
|
</div>
|
|
</div>`).join("")}
|
|
</div></div>
|
|
|
|
<div data-vorschau="1">
|
|
<div class="liste-leer">Wähle einen Baustein, um seinen Text zu sehen.</div>
|
|
</div>`,
|
|
bestaetigenText: "In den Bogen einsetzen",
|
|
bestaetigt: (el, zu) => {
|
|
if (!gewaehlt) return meldung("Bitte einen Baustein wählen.", "fehler");
|
|
|
|
const gefuellt = [];
|
|
const uebersprungen = [];
|
|
|
|
d.felder.forEach((feld) => {
|
|
const text = gewaehlt.inhalt[feld.id];
|
|
if (!text || abgewaehlt[feld.id]) return;
|
|
|
|
// Gesperrte Felder gibt es im Bogen gar nicht — sie bleiben aussen vor
|
|
const ziel = o.behaelter.querySelector(`textarea[name=${feld.id}]`);
|
|
if (!ziel) {
|
|
uebersprungen.push(feld.name);
|
|
return;
|
|
}
|
|
|
|
anhaengen(ziel, text);
|
|
gefuellt.push(feld.name);
|
|
});
|
|
|
|
zu();
|
|
|
|
if (!gefuellt.length) {
|
|
return meldung("Kein Feld des Bogens ließ sich füllen.", "fehler");
|
|
}
|
|
|
|
meldung(uebersprungen.length
|
|
? `Eingesetzt in ${gefuellt.length} Feld(er). Übersprungen: ${uebersprungen.join(", ")}.`
|
|
: `Eingesetzt in ${gefuellt.length} Feld(er). Noch nicht gespeichert.`, "gut");
|
|
|
|
if (o.nachEinsetzen) o.nachEinsetzen(gefuellt);
|
|
},
|
|
nachOeffnen: (hg) => {
|
|
const vorschau = hg.querySelector("[data-vorschau]");
|
|
|
|
/** Zeigt, was der Baustein in welches Feld schreiben würde */
|
|
function zeigeVorschau() {
|
|
vorschau.innerHTML = `
|
|
<div class="karte-kopf" style="margin-top:12px">
|
|
<span class="karte-titel">Wird eingesetzt</span>
|
|
<span class="feld-hinweis">Häkchen entfernen, was nicht mit soll</span>
|
|
</div>
|
|
${d.felder.filter((f) => gewaehlt.inhalt[f.id]).map((f) => `
|
|
<div class="bs-feld">
|
|
<label>
|
|
<input type="checkbox" class="haken" data-feld="${sicher(f.id)}"
|
|
${abgewaehlt[f.id] ? "" : "checked"}>
|
|
${sicher(f.name)}</label>
|
|
<div class="beh-text">${sicher(gewaehlt.inhalt[f.id])
|
|
.replace(/\n/g, "<br>")}</div>
|
|
</div>`).join("")}`;
|
|
|
|
vorschau.querySelectorAll("[data-feld]").forEach((el) => {
|
|
el.addEventListener("change", () => {
|
|
abgewaehlt[el.dataset.feld] = !el.checked;
|
|
});
|
|
});
|
|
}
|
|
|
|
hg.querySelectorAll("[data-baustein]").forEach((el) => {
|
|
el.addEventListener("click", () => {
|
|
gewaehlt = d.bausteine.find((b) => b.id === +el.dataset.baustein);
|
|
// Bei einem Wechsel gelten wieder alle Felder
|
|
Object.keys(abgewaehlt).forEach((k) => delete abgewaehlt[k]);
|
|
|
|
hg.querySelectorAll("[data-baustein]")
|
|
.forEach((zeile) => zeile.classList.remove("gewaehlt"));
|
|
el.classList.add("gewaehlt");
|
|
|
|
zeigeVorschau();
|
|
});
|
|
});
|
|
},
|
|
});
|
|
}
|
|
|
|
return { einsetzen };
|
|
})();
|