aether-framework/resources/[aether]/aether-medical/html/js/bausteine.js
Jerrit Fritzsche e4b82dd685 Aether-Framework: Fachmodule, Admin-Panel und Docker-Datenbank
- 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>
2026-07-27 18:55:32 +02:00

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 };
})();