aether-framework/resources/[aether]/aether-medical/html/js/module/behandlung.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

731 lines
31 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* =====================================================================
aether-medical / Ansicht: Behandlungsverwaltung
Ein Dokumentationsbogen, mehr nicht. Kein Feld wird vorausgefüllt,
keine Diagnose vorgeschlagen, kein Symptom ausgewertet. Was hier
steht, hat ein Mediziner geschrieben.
===================================================================== */
"use strict";
(() => {
const { sicher, zeit, minuten, aufruf, meldung, fenster, bestaetigen } = App;
const z = {
ordner: "laufend", // laufend | meine | abgeschlossen
suche: "",
daten: null,
offen: null,
offeneDaten: null,
};
/* ---- Übersicht --------------------------------------------------------- */
function uebersicht(behaelter) {
const d = z.daten;
behaelter.innerHTML = `
<div class="seiten-kopf">
<div>
<h2>Behandlungen</h2>
<p>Das System speichert ausschließlich die Dokumentation. Es behandelt
nicht, es diagnostiziert nicht und es schlägt nichts vor.</p>
</div>
${d.rechte.dokumentieren
? `<button class="knopf" data-neu="1">+ Behandlung dokumentieren</button>` : ""}
</div>
<div class="raster k3">
<div class="kennzahl ${d.zaehler.laufend > 0 ? "warn" : ""}">
<div class="kz-titel">Laufend</div>
<div class="kz-wert">${d.zaehler.laufend}</div>
<div class="kz-zusatz">noch nicht abgeschlossen</div>
</div>
<div class="kennzahl ${d.zaehler.meine > 0 ? "warn" : ""}">
<div class="kz-titel">Davon von dir</div>
<div class="kz-wert">${d.zaehler.meine}</div>
<div class="kz-zusatz">deine offene Dokumentation</div>
</div>
<div class="kennzahl">
<div class="kz-titel">Heute abgeschlossen</div>
<div class="kz-wert">${d.zaehler.heute}</div>
<div class="kz-zusatz">in der ganzen Klinik</div>
</div>
</div>
<div class="reiter">
<button class="${z.ordner === "laufend" ? "aktiv" : ""}" data-reiter="laufend">
Laufend<span class="mdt-zahl">${d.zaehler.laufend}</span></button>
<button class="${z.ordner === "meine" ? "aktiv" : ""}" data-reiter="meine">
Meine</button>
<button class="${z.ordner === "abgeschlossen" ? "aktiv" : ""}" data-reiter="abgeschlossen">
Abgeschlossen</button>
</div>
<div class="filterleiste">
<input data-suche="1" placeholder="Kennung, Patient oder Diagnose…"
value="${sicher(z.suche)}">
</div>
<div class="karte">
<div class="karte-kopf">
<span class="karte-titel">Dokumentationen</span>
<span class="feld-hinweis">${d.liste.length} Einträge</span>
</div>
<div class="liste">
${d.liste.length ? d.liste.map(zeile).join("")
: `<div class="liste-leer">Keine Dokumentation gefunden.</div>`}
</div>
</div>
`;
verdrahteUebersicht(behaelter);
}
function zeile(b) {
const laufend = b.status === "laufend";
return `<div class="liste-zeile" data-oeffne="${b.id}">
<span class="db-nummer">${sicher(b.kennung)}</span>
<div class="lz-haupt">
<div class="lz-titel">${sicher(b.patientName)}
<span class="pat-geburt">geb. ${sicher(b.geburt)}</span></div>
<div class="lz-unter">${sicher(b.diagnose || b.massnahmen
|| "Noch nichts eingetragen")}</div>
</div>
${b.hinweis ? `<span class="marker kritisch" title="${sicher(b.hinweis)}">⚠</span>` : ""}
${b.allergien ? `<span class="marker warn" title="Allergien: ${sicher(b.allergien)}">A</span>` : ""}
${b.gaben > 0 ? `<span class="marker neutral" title="Medikamentengaben">💊 ${b.gaben}</span>` : ""}
${b.einsatzNummer ? `<span class="marker info">${sicher(b.einsatzNummer)}</span>` : ""}
${b.eigene ? `<span class="marker neutral">von dir</span>` : ""}
<span class="marker ${laufend ? "warn" : "gut"}">${sicher(b.status)}</span>
<span class="db-zeit">${laufend
? "offen seit " + minuten(b.offenSeit) : zeit(b.abgeschlossen_am)}</span>
</div>`;
}
function verdrahteUebersicht(behaelter) {
behaelter.querySelectorAll("[data-reiter]").forEach((el) => {
el.addEventListener("click", () => {
z.ordner = el.dataset.reiter;
rendern(behaelter);
});
});
const suchfeld = behaelter.querySelector("[data-suche]");
let warten = null;
suchfeld.addEventListener("input", () => {
clearTimeout(warten);
warten = setTimeout(() => {
z.suche = suchfeld.value.trim();
rendern(behaelter);
}, 280);
});
behaelter.querySelectorAll("[data-oeffne]").forEach((el) => {
el.addEventListener("click", () => oeffne(behaelter, +el.dataset.oeffne));
});
const neu = behaelter.querySelector("[data-neu]");
if (neu) neu.addEventListener("click", () => anlegenFenster(behaelter));
}
/* ---- Dokumentationsbogen ------------------------------------------------- */
function einzelAnsicht(behaelter, d) {
const b = d.behandlung, s = d.stamm;
const laufend = b.status === "laufend";
// Die Diagnose darf nur mit eigenem Recht geschrieben werden
const diagnoseSperre = !d.rechte.diagnose;
behaelter.innerHTML = `
<div class="seiten-kopf">
<div>
<h2>${sicher(b.kennung)} · ${sicher(b.patientName)}</h2>
<p>geb. ${sicher(b.geburt)}
${b.einsatzNummer ? " · Einsatz " + sicher(b.einsatzNummer) : ""}
${b.aufnahmeGrund ? " · Aufnahme: " + sicher(b.aufnahmeGrund) : ""}</p>
</div>
<button class="knopf leise" data-zurueck="1">← Übersicht</button>
</div>
${s.hinweis ? `
<div class="warnung kritisch">
<span class="w-symbol">⚠</span>
<div><b>Warnhinweis</b> — ${sicher(s.hinweis)}</div>
</div>` : ""}
${s.allergien ? `
<div class="warnung warn">
<span class="w-symbol">💊</span>
<div><b>Allergien</b> — ${sicher(s.allergien)}
<br><span class="feld-hinweis">Vor jeder Gabe selbst prüfen. Das System
gleicht Medikamente nicht gegen Allergien ab.</span></div>
</div>` : ""}
<div class="beh-kopf">
<div class="werte-liste">
<div><span>Status</span><b>${sicher(b.status)}</b></div>
<div><span>Angelegt</span><b>${zeit(b.erstelltAm)}</b></div>
<div><span>Von</span><b>${sicher(b.erstelltVon)}</b></div>
<div class="${s.blutgruppe ? "" : "leer"}"><span>Blutgruppe</span>
<b>${sicher(s.blutgruppe || "nicht bestimmt")}</b></div>
<div class="${s.vorerkrankungen ? "" : "leer"}"><span>Vorerkrankungen</span>
<b>${sicher(s.vorerkrankungen || "keine erfasst")}</b></div>
<div class="${s.dauermedikation ? "" : "leer"}"><span>Dauermedikation</span>
<b>${sicher(s.dauermedikation || "keine erfasst")}</b></div>
${b.abgeschlossenAm ? `
<div><span>Abgeschlossen</span><b>${zeit(b.abgeschlossenAm)}</b></div>
<div><span>Durch</span><b>${sicher(b.abgeschlossenVon)}</b></div>` : ""}
</div>
<div class="beh-kopf-aktionen">
<button class="knopf leise klein" data-akte="1">Patientenakte</button>
${laufend && d.rechte.dokumentieren
? `<button class="knopf leise klein" data-baustein="1">🧩 Baustein</button>
<button class="knopf klein" data-speichern="1">Dokumentation speichern</button>` : ""}
${laufend && d.rechte.abschliessen
? `<button class="knopf leise klein" data-abschliessen="1">Abschließen</button>` : ""}
${!laufend && d.rechte.abschliessen
? `<button class="knopf leise klein" data-oeffnen="1">Wieder öffnen</button>` : ""}
</div>
</div>
<div class="beh-bogen">
${feld("symptome", "Symptome / Anamnese", b.symptome, laufend && d.rechte.dokumentieren,
"Was schildert der Patient?")}
${feld("untersuchung", "Untersuchung", b.untersuchung, laufend && d.rechte.dokumentieren,
"Was hast du erhoben? Vitalwerte, Befunde am Patienten…")}
${feld("diagnose", "Diagnose", b.diagnose,
laufend && d.rechte.dokumentieren && !diagnoseSperre,
diagnoseSperre ? "Für die Diagnose fehlt dir die Berechtigung."
: "Deine Einschätzung. Das System stellt keine Diagnose.")}
${feld("massnahmen", "Maßnahmen / Behandlung", b.massnahmen,
laufend && d.rechte.dokumentieren,
"Verbände, Lagerung, Eingriffe, Reanimation…")}
${feld("empfehlung", "Empfehlung", b.empfehlung, laufend && d.rechte.dokumentieren,
"Was empfiehlst du dem Patienten?")}
${feld("nachsorge", "Nachsorge", b.nachsorge, laufend && d.rechte.dokumentieren,
"Kontrolltermin, Wundversorgung, Medikamentenplan…")}
</div>
<div class="karte" style="margin-top:14px">
<div class="karte-kopf">
<span class="karte-titel">Verabreichte Medikamente</span>
${laufend && d.rechte.medikament
? `<button class="knopf klein" data-gabe="1">+ Gabe dokumentieren</button>` : ""}
</div>
<div class="liste">
${d.gaben.length ? d.gaben.map((g) => `
<div class="liste-zeile">
<span class="marker ${g.btm ? "kritisch" : "neutral"}">
${g.btm ? "BTM" : "💊"}</span>
<div class="lz-haupt">
<div class="lz-titel">${sicher(g.name)}
<span class="pat-geburt">${g.menge} ${sicher(g.einheit)}</span></div>
<div class="lz-unter">
${g.dosierung ? sicher(g.dosierung) + " · " : ""}
${sicher(g.autor)} · ${zeit(g.gegeben_am)}
${g.hinweis ? " · " + sicher(g.hinweis) : ""}
</div>
</div>
${laufend && (g.eigene || d.rechte.bestand)
? `<button class="knopf leise klein" data-zuruecknehmen="${g.id}"
title="Fehleintrag zurücknehmen">✕</button>` : ""}
</div>`).join("")
: `<div class="liste-leer">Keine Gabe dokumentiert.</div>`}
</div>
</div>
<div class="db-spalten" style="margin-top:14px">
<div class="karte">
<div class="karte-kopf"><span class="karte-titel">Laborergebnisse des Patienten</span></div>
<div class="liste">
${d.labor.length ? d.labor.map((l) => `
<div class="liste-zeile">
<span class="db-nummer">${sicher(l.kennung)}</span>
<div class="lz-haupt">
<div class="lz-titel">${sicher(l.art)}</div>
<div class="lz-unter">${sicher(l.befund || l.ergebnis
|| "Noch kein Ergebnis")}</div>
</div>
<span class="marker ${l.status === "fertig" ? "gut" : "warn"}">
${sicher(l.status)}</span>
</div>`).join("")
: `<div class="liste-leer">Keine Laboraufträge.</div>`}
</div>
</div>
<div class="karte">
<div class="karte-kopf"><span class="karte-titel">Operationen des Patienten</span></div>
<div class="liste">
${d.operationen.length ? d.operationen.map((o) => `
<div class="liste-zeile">
<span class="db-nummer">${sicher(o.kennung)}</span>
<div class="lz-haupt">
<div class="lz-titel">${sicher(o.art)}</div>
<div class="lz-unter">${o.beginn ? zeit(o.beginn) : "noch nicht begonnen"}</div>
</div>
<span class="marker neutral">${sicher(o.status)}</span>
</div>`).join("")
: `<div class="liste-leer">Keine Operationen.</div>`}
</div>
</div>
</div>
`;
verdrahteEinzel(behaelter, d);
}
/**
* Ein Feld des Dokumentationsbogens.
* @param {string} name Feldname
* @param {string} titel Beschriftung
* @param {string} wert Bisheriger Inhalt
* @param {boolean} schreibbar Darf bearbeitet werden?
* @param {string} hinweis Text unter dem Feld
*/
function feld(name, titel, wert, schreibbar, hinweis) {
if (!schreibbar) {
return `<div class="beh-feld gesperrt">
<label>${sicher(titel)}</label>
<div class="beh-text ${wert ? "" : "leer"}">${wert
? sicher(wert).replace(/\n/g, "<br>")
: "Nicht dokumentiert."}</div>
${hinweis ? `<div class="feld-hinweis">${sicher(hinweis)}</div>` : ""}
</div>`;
}
return `<div class="beh-feld">
<label>${sicher(titel)}</label>
<textarea name="${sicher(name)}" rows="4"
placeholder="${sicher(hinweis || "")}">${sicher(wert || "")}</textarea>
${hinweis ? `<div class="feld-hinweis">${sicher(hinweis)}</div>` : ""}
</div>`;
}
function verdrahteEinzel(behaelter, d) {
const b = d.behandlung;
behaelter.querySelector("[data-zurueck]").addEventListener("click", () => {
z.offen = null;
rendern(behaelter);
});
behaelter.querySelector("[data-akte]").addEventListener("click", () => {
if (!App.zustand.module.some((m) => m.id === "mdt")) {
return meldung("Das MDT steht dir nicht zur Verfügung.", "fehler");
}
App.oeffneModul("mdt", { patient: b.patient });
});
// Baustein einsetzen: füllt nur den Bogen, gespeichert wird danach von Hand
const baustein = behaelter.querySelector("[data-baustein]");
if (baustein) baustein.addEventListener("click", () => {
Bausteine.einsetzen({ modul: "behandlung", behaelter });
});
const speichern = behaelter.querySelector("[data-speichern]");
if (speichern) speichern.addEventListener("click", async () => {
const lies = (name) => {
const el = behaelter.querySelector(`[name=${name}]`);
return el ? el.value.trim() : undefined;
};
try {
await aufruf("behandlung", "speichern", {
id: b.id,
symptome: lies("symptome"),
untersuchung: lies("untersuchung"),
diagnose: lies("diagnose"),
massnahmen: lies("massnahmen"),
empfehlung: lies("empfehlung"),
nachsorge: lies("nachsorge"),
});
meldung("Dokumentation gespeichert.", "gut");
oeffne(behaelter, b.id);
} catch (f) { meldung(f.message, "fehler"); }
});
const abschliessen = behaelter.querySelector("[data-abschliessen]");
if (abschliessen) abschliessen.addEventListener("click", () => {
bestaetigen({
titel: "Behandlung abschließen",
inhalt: `
<p><b>${sicher(b.kennung)}</b> — ${sicher(b.patientName)}</p>
<p>Die Dokumentation wird damit schreibgeschützt. Sie lässt sich später
mit Begründung wieder öffnen.</p>
${!b.diagnose ? `
<div class="warnung warn">
<span class="w-symbol">📄</span>
<div><b>Es ist keine Diagnose eingetragen.</b> Abschließen ist trotzdem
möglich — das System trägt keine für dich ein.</div>
</div>` : ""}
<p class="feld-hinweis">Nicht gespeicherte Änderungen im Bogen gehen dabei
verloren. Erst speichern, dann abschließen.</p>
<div class="feld"><label>Vermerk (freiwillig)</label>
<input name="vermerk" maxlength="255"></div>`,
bestaetigenText: "Abschließen",
bestaetigt: async (el, zu) => {
try {
await aufruf("behandlung", "abschliessen", {
id: b.id, vermerk: el.querySelector("[name=vermerk]").value.trim(),
});
zu();
meldung("Behandlung abgeschlossen.", "gut");
oeffne(behaelter, b.id);
} catch (f) { meldung(f.message, "fehler"); }
},
});
});
const wiederOeffnen = behaelter.querySelector("[data-oeffnen]");
if (wiederOeffnen) wiederOeffnen.addEventListener("click", () => {
bestaetigen({
titel: "Dokumentation wieder öffnen",
inhalt: `<p>Die Behandlung wird wieder bearbeitbar. Der Vorgang steht
dauerhaft im Protokoll.</p>
<div class="feld"><label>Begründung (Pflicht)</label>
<input name="grund" maxlength="255"
placeholder="Was muss nachgetragen werden?"></div>`,
bestaetigenText: "Wieder öffnen",
bestaetigt: async (el, zu) => {
const grund = el.querySelector("[name=grund]").value.trim();
if (grund.length < 3) return meldung("Bitte eine Begründung angeben.", "fehler");
try {
await aufruf("behandlung", "wiedereroeffnen", { id: b.id, grund });
zu();
meldung("Dokumentation wieder geöffnet.", "gut");
oeffne(behaelter, b.id);
} catch (f) { meldung(f.message, "fehler"); }
},
});
});
const gabe = behaelter.querySelector("[data-gabe]");
if (gabe) gabe.addEventListener("click", () => gabeFenster(behaelter, d));
behaelter.querySelectorAll("[data-zuruecknehmen]").forEach((el) => {
el.addEventListener("click", () => {
bestaetigen({
titel: "Eintrag zurücknehmen",
gefahr: true,
inhalt: `<p>Der Eintrag wird aus der Dokumentation entfernt. Wurde damals
Bestand gebucht, wird er als Korrektur zurückgebucht.</p>
<div class="feld"><label>Begründung (Pflicht)</label>
<input name="grund" maxlength="255"
placeholder="z. B. falsches Medikament eingetragen"></div>`,
bestaetigenText: "Zurücknehmen",
bestaetigt: async (dialog, zu) => {
const grund = dialog.querySelector("[name=grund]").value.trim();
if (grund.length < 3) return meldung("Bitte eine Begründung angeben.", "fehler");
try {
await aufruf("behandlung", "medikamentZuruecknehmen", {
gabe: +el.dataset.zuruecknehmen, grund,
});
zu();
meldung("Eintrag zurückgenommen.", "gut");
oeffne(behaelter, b.id);
} catch (f) { meldung(f.message, "fehler"); }
},
});
});
});
}
/* ---- Medikamentengabe ---------------------------------------------------- */
async function gabeFenster(behaelter, d) {
const b = d.behandlung;
let katalog;
try {
katalog = await aufruf("behandlung", "medikamente", {});
} catch (f) { return meldung(f.message, "fehler"); }
let gewaehlt = null;
// Wird gesetzt, wenn der Bestand nicht reicht und trotzdem dokumentiert wird
let ohneBestand = false;
// Medikamente nach Kategorie gruppieren
const gruppen = {};
katalog.medikamente.forEach((m) => {
(gruppen[m.kategorie] = gruppen[m.kategorie] || []).push(m);
});
const kategorieName = {};
katalog.kategorien.forEach((k) => { kategorieName[k.id] = k.name; });
const dialog = bestaetigen({
titel: "Medikamentengabe dokumentieren",
inhalt: `
<p><b>${sicher(b.patientName)}</b> · ${sicher(b.kennung)}</p>
${d.stamm.allergien ? `
<div class="warnung warn">
<span class="w-symbol">⚠</span>
<div><b>Bekannte Allergien:</b> ${sicher(d.stamm.allergien)}<br>
<span class="feld-hinweis">Das System prüft nicht gegen diese Liste.
Der Abgleich ist deine Aufgabe.</span></div>
</div>` : ""}
<div class="feld"><label>Medikament</label>
<input name="suche" placeholder="Filtern…">
<div class="beh-medikamente" data-liste="1">
${Object.keys(gruppen).map((kategorie) => `
<div class="beh-med-gruppe" data-gruppe="${sicher(kategorie)}">
<div class="beh-med-titel">${sicher(kategorieName[kategorie] || kategorie)}</div>
${gruppen[kategorie].map((m) => `
<button class="beh-med ${m.waehlbar ? "" : "gesperrt"}"
${m.waehlbar ? "" : "disabled"}
data-med="${m.id}" data-name="${sicher(m.name.toLowerCase())}"
data-dosierung="${sicher(m.dosierung_standard || "")}"
data-bestand="${m.bestand}" data-einheit="${sicher(m.einheit)}">
<span class="beh-med-name">${sicher(m.name)}
${m.btm ? `<span class="marker kritisch">BTM</span>` : ""}
${m.abgelaufen ? `<span class="marker warn"
title="Verfallsdatum überschritten — nicht gesperrt">abgelaufen</span>` : ""}</span>
<span class="beh-med-unter">${sicher(m.wirkstoff || "")}
${m.dosierung_standard ? " · " + sicher(m.dosierung_standard) : ""}</span>
<span class="beh-med-bestand ${m.bestand <= m.mindestbestand ? "knapp" : ""}">
${m.bestand} ${sicher(m.einheit)}</span>
</button>`).join("")}
</div>`).join("")}
</div>
<div class="feld-hinweis">Die Standarddosierung ist ein <b>Vorschlag</b> aus dem
Katalog. Was du gibst, entscheidest du.</div>
</div>
<div class="feld-reihe">
<div class="feld"><label>Menge</label>
<input name="menge" type="number" min="1" max="999" value="1"></div>
<div class="feld"><label>Dosierung</label>
<input name="dosierung" maxlength="64" placeholder="z. B. 1 g i.v."></div>
</div>
<div class="feld"><label>Hinweis (freiwillig)</label>
<input name="hinweis" maxlength="255"
placeholder="z. B. gute Verträglichkeit, Zugang links"></div>
<div class="beh-bestandswarnung" data-warnung="1" style="display:none"></div>`,
bestaetigenText: "Gabe dokumentieren",
bestaetigt: async (el, zu) => {
if (!gewaehlt) return meldung("Bitte ein Medikament wählen.", "fehler");
try {
await aufruf("behandlung", "medikamentGeben", {
id: b.id,
medikament: gewaehlt,
menge: +el.querySelector("[name=menge]").value,
dosierung: el.querySelector("[name=dosierung]").value.trim(),
hinweis: el.querySelector("[name=hinweis]").value.trim(),
ohneBestand,
});
zu();
meldung("Gabe dokumentiert.", "gut");
oeffne(behaelter, b.id);
} catch (f) {
meldung(f.message, "fehler");
// Bestand reicht nicht: Dokumentation trotzdem anbieten
if (f.message.indexOf("Bestand") !== -1) zeigeBestandsWeg(el);
}
},
nachOeffnen: (hg) => {
const suchfeld = hg.querySelector("[name=suche]");
const dosierung = hg.querySelector("[name=dosierung]");
hg.querySelectorAll("[data-med]").forEach((el) => {
el.addEventListener("click", () => {
gewaehlt = +el.dataset.med;
ohneBestand = false;
hg.querySelector("[data-warnung]").style.display = "none";
hg.querySelectorAll("[data-med]").forEach((k) => k.classList.remove("gewaehlt"));
el.classList.add("gewaehlt");
// Standarddosierung als Vorschlag übernehmen, überschreibbar
if (!dosierung.value) dosierung.value = el.dataset.dosierung;
});
});
suchfeld.addEventListener("input", () => {
const begriff = suchfeld.value.trim().toLowerCase();
hg.querySelectorAll("[data-med]").forEach((el) => {
el.style.display = !begriff || el.dataset.name.indexOf(begriff) !== -1 ? "" : "none";
});
// Leere Gruppen ausblenden
hg.querySelectorAll("[data-gruppe]").forEach((gruppe) => {
const sichtbar = [...gruppe.querySelectorAll("[data-med]")]
.some((el) => el.style.display !== "none");
gruppe.style.display = sichtbar ? "" : "none";
});
});
},
});
/** Bietet an, die Gabe ohne Bestandsbuchung zu dokumentieren */
function zeigeBestandsWeg(el) {
const warnung = el.querySelector("[data-warnung]");
warnung.style.display = "";
warnung.innerHTML = `
<div class="warnung warn">
<span class="w-symbol">📦</span>
<div><b>Der Lagerbestand reicht nicht.</b> Eine Dokumentation darf daran
nicht scheitern — etwa wenn das Mittel aus dem RTW stammt.
<br><button class="knopf leise klein" data-ohne="1"
style="margin-top:8px">Ohne Bestandsbuchung dokumentieren</button></div>
</div>`;
warnung.querySelector("[data-ohne]").addEventListener("click", () => {
ohneBestand = true;
meldung("Bestandsbuchung deaktiviert — jetzt bestätigen.", "gut");
warnung.querySelector("[data-ohne]").disabled = true;
});
}
return dialog;
}
/* ---- Anlegen ------------------------------------------------------------- */
function anlegenFenster(behaelter) {
let gewaehlt = null;
let einsaetze = [];
bestaetigen({
titel: "Behandlung dokumentieren",
inhalt: `
<div class="feld"><label>Patient suchen</label>
<input name="suche" placeholder="Name oder Geburtsdatum…"></div>
<div class="liste pat-trefferliste" data-treffer="1">
<div class="liste-leer">Mindestens zwei Zeichen eingeben.</div>
</div>
<div data-formular="1" style="display:none">
<div class="feld" data-einsatzfeld="1" style="display:none">
<label>Zu einem Einsatz zuordnen (freiwillig)</label>
<select name="einsatz"></select></div>
<div class="feld"><label>Symptome / Anamnese</label>
<textarea name="symptome" rows="3"
placeholder="Was schildert der Patient?"></textarea></div>
<div class="feld"><label>Erste Untersuchung</label>
<textarea name="untersuchung" rows="3"
placeholder="Vitalwerte, erster Eindruck…"></textarea>
<div class="feld-hinweis">Diagnose, Maßnahmen und Nachsorge trägst du
anschließend im Bogen nach.</div></div>
</div>`,
bestaetigenText: "Anlegen",
bestaetigt: async (el, zu) => {
if (!gewaehlt) return meldung("Bitte zuerst einen Patienten wählen.", "fehler");
const einsatzFeld = el.querySelector("[name=einsatz]");
try {
const antwort = await aufruf("behandlung", "anlegen", {
patient: gewaehlt.id,
aufnahme: gewaehlt.aufnahme || null,
einsatz: einsatzFeld && einsatzFeld.value ? +einsatzFeld.value : null,
symptome: el.querySelector("[name=symptome]").value.trim(),
untersuchung: el.querySelector("[name=untersuchung]").value.trim(),
});
zu();
meldung("Behandlung " + antwort.kennung + " angelegt.", "gut");
oeffne(behaelter, antwort.id);
} catch (f) { meldung(f.message, "fehler"); }
},
nachOeffnen: (hg) => {
const suchfeld = hg.querySelector("[name=suche]");
const liste = hg.querySelector("[data-treffer]");
const formular = hg.querySelector("[data-formular]");
const einsatzfeld = hg.querySelector("[data-einsatzfeld]");
let warten = null;
suchfeld.addEventListener("input", () => {
clearTimeout(warten);
warten = setTimeout(async () => {
const begriff = suchfeld.value.trim();
if (begriff.length < 2) {
liste.innerHTML = `<div class="liste-leer">Mindestens zwei Zeichen eingeben.</div>`;
return;
}
try {
const antwort = await aufruf("behandlung", "patientSuche", { begriff });
const treffer = antwort.treffer || [];
einsaetze = antwort.einsaetze || [];
liste.innerHTML = treffer.length ? treffer.map((t) => `
<div class="liste-zeile" data-person="${t.id}">
<div class="lz-haupt">
<div class="lz-titel">${sicher(t.firstname)} ${sicher(t.lastname)}</div>
<div class="lz-unter">geb. ${sicher(t.dob)}
${t.blutgruppe ? " · " + sicher(t.blutgruppe) : ""}</div>
</div>
${t.hinweis ? `<span class="marker kritisch">⚠</span>` : ""}
${t.allergien ? `<span class="marker warn">A</span>` : ""}
${t.aufnahme ? `<span class="marker info">aufgenommen</span>` : ""}
</div>`).join("")
: `<div class="liste-leer">Keine Person gefunden.</div>`;
liste.querySelectorAll("[data-person]").forEach((el) => {
el.addEventListener("click", () => {
gewaehlt = treffer.find((t) => t.id === +el.dataset.person);
liste.querySelectorAll("[data-person]")
.forEach((zeile) => zeile.classList.remove("gewaehlt"));
el.classList.add("gewaehlt");
formular.style.display = "";
// Laufende Einsätze anbieten, an denen man beteiligt ist
if (einsaetze.length) {
einsatzfeld.style.display = "";
hg.querySelector("[name=einsatz]").innerHTML =
`<option value="">— keinem Einsatz zuordnen —</option>` +
einsaetze.map((e) =>
`<option value="${e.id}">${sicher(e.nummer)} · ${sicher(e.titel)}</option>`
).join("");
}
});
});
} catch (f) { meldung(f.message, "fehler"); }
}, 260);
});
},
});
}
/* ---- Laden ---------------------------------------------------------------- */
async function oeffne(behaelter, behandlungId) {
try {
const d = await aufruf("behandlung", "oeffnen", { id: behandlungId });
z.offen = behandlungId;
z.offeneDaten = d;
einzelAnsicht(behaelter, d);
} catch (f) { meldung(f.message, "fehler"); }
}
async function rendern(behaelter) {
try {
z.daten = await aufruf("behandlung", "uebersicht", {
ordner: z.ordner, begriff: z.suche,
});
// Sprungziel aus einem Hinweis, z. B. „Dokumentation fehlt"
const ziel = App.zustand.modulZiel;
if (ziel && ziel.behandlung) {
App.zustand.modulZiel = null;
return oeffne(behaelter, ziel.behandlung);
}
if (z.offen) await oeffne(behaelter, z.offen);
else uebersicht(behaelter);
} catch (f) {
behaelter.innerHTML = `<div class="seiten-kopf"><div><h2>Behandlungen</h2>
<p>Konnte nicht geladen werden: ${sicher(f.message)}</p></div></div>`;
}
}
App.registriereAnsicht("behandlung", { rendern });
App.registriereSuche(async (begriff) => {
try {
const d = await aufruf("behandlung", "uebersicht", {
ordner: "laufend", begriff,
});
return (d.liste || []).slice(0, 5).map((b) => ({
bereich: "Behandlung",
titel: b.kennung + " · " + b.patientName,
unter: b.diagnose || b.massnahmen || "Noch nichts eingetragen",
}));
} catch (e) { return []; }
});
})();