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

748 lines
30 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: Intensivstation
Diese Ansicht ist ein Monitor für Dokumentation, nicht für Patienten.
Kein Wert hier wird gemessen: der Zustand steht da, weil ein Arzt ihn
eingetragen hat, und die Geräteliste ist eine Liste von Namen.
Ein Aufenthalt endet erst, wenn jemand ihn beendet.
===================================================================== */
"use strict";
(() => {
const { sicher, zeit, minuten, aufruf, meldung, fenster, bestaetigen } = App;
const z = {
ordner: "laufend", // laufend | beendet
offen: null, // geöffneter Aufenthalt
daten: null,
};
// Marker-Art je Zustand — nur Farbe, keine Wertung
const zustandArt = { kritisch: "kritisch", stabil: "info", besserung: "gut" };
/* ---- Kleine Helfer ---------------------------------------------------- */
/** Farbe eines Zustands aus der Konfiguration */
function zustandFarbe(kennung) {
const liste = (z.daten && z.daten.zustaende) || [];
const treffer = liste.find((s) => s.id === kennung);
return treffer ? treffer.farbe : "#8697a4";
}
/** Zerlegt die Geräteliste in einzelne Namen */
function geraeteTeile(text) {
return String(text || "").split(",").map((t) => t.trim()).filter(Boolean);
}
/* ---- Gerüst ------------------------------------------------------------ */
function reiterleiste(d) {
const eintraege = [
{ id: "laufend", text: "In Betreuung", zahl: d.zaehler.laufend },
{ id: "beendet", text: "Beendet", zahl: d.zaehler.beendetHeute },
];
return `<div class="reiter">
${eintraege.map((e) => `
<button class="${z.ordner === e.id ? "aktiv" : ""}" data-reiter="${e.id}">
${sicher(e.text)}<span class="mdt-zahl">${e.zahl}</span>
</button>`).join("")}
</div>`;
}
function kennzahlen(d) {
const zk = d.zaehler;
return `<div class="raster k4">
<div class="kennzahl">
<div class="kz-titel">In Betreuung</div>
<div class="kz-wert">${zk.laufend}</div>
<div class="kz-zusatz">laufende Aufenthalte</div>
</div>
<div class="kennzahl ${zk.kritisch > 0 ? "hoch" : ""}">
<div class="kz-titel">Als kritisch eingetragen</div>
<div class="kz-wert">${zk.kritisch}</div>
<div class="kz-zusatz">von Hand eingestuft</div>
</div>
<div class="kennzahl ${zk.ohneArzt > 0 ? "hoch" : "gut"}">
<div class="kz-titel">Ohne zuständigen Arzt</div>
<div class="kz-wert">${zk.ohneArzt}</div>
<div class="kz-zusatz">niemand eingetragen</div>
</div>
<div class="kennzahl ${zk.langeStill > 0 ? "warn" : ""}">
<div class="kz-titel">Ohne frischen Eintrag</div>
<div class="kz-wert">${zk.langeStill}</div>
<div class="kz-zusatz">seit über ${d.stilleNach} Minuten</div>
</div>
</div>`;
}
/* ---- Übersicht ---------------------------------------------------------- */
function uebersicht(behaelter) {
const d = z.daten;
behaelter.innerHTML = `
<div class="seiten-kopf">
<div>
<h2>Intensivstation</h2>
<p>Das System misst nichts und schlägt keinen Alarm. Was hier steht, hat
jemand eingetragen — der Zustand ändert sich nie von selbst.</p>
</div>
${d.rechte.betreuen
? `<button class="knopf" data-aufnehmen="1">+ Intensivaufnahme</button>` : ""}
</div>
${kennzahlen(d)}
${reiterleiste(d)}
<div class="karte">
<div class="karte-kopf">
<span class="karte-titel">${z.ordner === "laufend"
? "Patienten in Intensivbetreuung" : "Beendete Aufenthalte"}</span>
<span class="feld-hinweis">${z.ordner === "laufend"
? "Oben steht, was als am schwersten eingetragen wurde"
: d.liste.length + " Einträge"}</span>
</div>
<div class="liste">
${d.liste.length ? d.liste.map(zeile).join("")
: `<div class="liste-leer">${z.ordner === "laufend"
? "Zurzeit wird niemand intensiv betreut."
: "Noch keine beendeten Aufenthalte."}</div>`}
</div>
</div>
`;
verdrahteUebersicht(behaelter);
}
function zeile(a) {
const laufend = !a.beendet_am;
return `<div class="liste-zeile" data-oeffne="${a.id}">
<span class="its-punkt" style="background:${sicher(zustandFarbe(a.zustand))}"
title="${sicher(a.zustandName)}"></span>
<div class="lz-haupt">
<div class="lz-titel">${sicher(a.name)}
<span class="pat-geburt">geb. ${sicher(a.geburt)}</span></div>
<div class="lz-unter">
${a.zimmerKennung ? "Zimmer " + sicher(a.zimmerKennung) : "ohne festen Platz"}
${a.geraete ? " · " + sicher(String(a.geraete).slice(0, 60)) : ""}
${laufend ? " · zuletzt dokumentiert vor " + sicher(minuten(a.stillSeit)) : ""}
</div>
</div>
${a.warnhinweis ? `<span class="marker kritisch" title="${sicher(a.warnhinweis)}">⚠</span>` : ""}
${a.allergien ? `<span class="marker warn" title="Allergien: ${sicher(a.allergien)}">A</span>` : ""}
${a.blutgruppe ? `<span class="marker neutral">${sicher(a.blutgruppe)}</span>` : ""}
${a.langeStill ? `<span class="marker warn">Verlauf offen</span>` : ""}
${laufend && !a.verantwortlichName
? `<span class="marker kritisch">kein Arzt</span>`
: a.verantwortlichName
? `<span class="marker neutral">${sicher(a.verantwortlichName)}</span>` : ""}
<span class="marker ${zustandArt[a.zustand] || "neutral"}">
${sicher(a.zustandName)}</span>
<span class="db-zeit">${laufend
? "seit " + sicher(minuten(a.liegtSeit))
: zeit(a.beendet_am)}</span>
</div>`;
}
function verdrahteUebersicht(behaelter) {
behaelter.querySelectorAll("[data-reiter]").forEach((el) => {
el.addEventListener("click", () => {
z.ordner = el.dataset.reiter;
rendern(behaelter);
});
});
behaelter.querySelectorAll("[data-oeffne]").forEach((el) => {
el.addEventListener("click", () => oeffne(behaelter, +el.dataset.oeffne));
});
const aufnehmen = behaelter.querySelector("[data-aufnehmen]");
if (aufnehmen) aufnehmen.addEventListener("click", () => aufnahmeFenster(behaelter));
}
/* ---- Einzelansicht -------------------------------------------------------- */
function einzelAnsicht(behaelter, d) {
const a = d.aufenthalt, s = d.stamm;
const geraete = geraeteTeile(a.geraete);
behaelter.innerHTML = `
<div class="seiten-kopf">
<div>
<h2>${sicher(a.name)}</h2>
<p>geb. ${sicher(a.geburt)} · ${sicher(a.zustandName)}
${a.zimmerKennung ? " · Zimmer " + sicher(a.zimmerKennung) : ""}
${a.stationName ? " · " + sicher(a.stationName) : ""}</p>
</div>
<button class="knopf leise" data-zurueck="1">← Übersicht</button>
</div>
${a.laufend ? "" : `
<div class="warnung">
<span class="w-symbol">✔</span>
<div><b>Intensivbetreuung beendet</b> — ${zeit(a.beendetAm)} von
${sicher(a.beendetVon || "unbekannt")}. Der Aufenthalt ist nur noch
zum Nachlesen da.</div>
</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)}</div>
</div>` : ""}
${a.laufend && !a.verantwortlichName ? `
<div class="warnung kritisch">
<span class="w-symbol">🧑‍⚕️</span>
<div><b>Kein zuständiger Arzt eingetragen.</b> Das System wählt keinen aus —
jemand muss die Verantwortung übernehmen.</div>
</div>` : ""}
<div class="mdt-kopf">
<div class="mdt-bild">${s.lichtbild
? `<img src="${sicher(s.lichtbild)}" alt="Lichtbild">`
: `<span>Kein Lichtbild</span>`}</div>
<div class="mdt-kopf-werte">
<div class="mdt-wert"><span>Zustand</span>
<b style="color:${sicher(zustandFarbe(a.zustand))}">
${sicher(a.zustandName)}</b></div>
<div class="mdt-wert"><span>In Betreuung seit</span>
<b>${sicher(minuten(a.liegtSeit))}</b></div>
<div class="mdt-wert ${a.verantwortlichName ? "" : "fehlt"}"><span>Zuständig</span>
<b>${sicher(a.verantwortlichName || "niemand eingetragen")}</b></div>
<div class="mdt-wert ${a.zimmerKennung ? "" : "fehlt"}"><span>Platz</span>
<b>${sicher(a.zimmerKennung || "kein fester Platz")}</b></div>
<div class="mdt-wert ${s.blutgruppe ? "" : "fehlt"}"><span>Blutgruppe</span>
<b>${sicher(s.blutgruppe || "nicht bestimmt")}</b></div>
<div class="mdt-wert ${s.dauermedikation ? "" : "fehlt"}"><span>Dauermedikation</span>
<b>${sicher(s.dauermedikation || "keine erfasst")}</b></div>
</div>
<div class="mdt-kopf-aktionen">
<button class="knopf leise klein" data-akte="1">Patientenakte öffnen</button>
${a.aufnahme
? `<button class="knopf leise klein" data-vorgang="1">Aufnahme öffnen</button>` : ""}
${a.laufend && d.rechte.betreuen ? `
<button class="knopf klein" data-zustand="1">Zustand eintragen</button>
<button class="knopf leise klein" data-geraete="1">Geräte</button>
<button class="knopf leise klein" data-zustaendig="1">Zuständigkeit</button>
<button class="knopf gefahr klein" data-beenden="1">Betreuung beenden</button>` : ""}
</div>
</div>
<div class="db-spalten">
<div class="karte">
<div class="karte-kopf"><span class="karte-titel">Aufenthalt</span></div>
<div class="werte-liste">
<div><span>Aufgenommen</span><b>${zeit(a.aufgenommenAm)}</b></div>
<div><span>Aufgenommen von</span><b>${sicher(a.aufgenommenVon)}</b></div>
${a.aufnahmeGrund
? `<div><span>Grund der Aufnahme</span><b>${sicher(a.aufnahmeGrund)}</b></div>` : ""}
${a.beendetAm ? `
<div><span>Beendet</span><b>${zeit(a.beendetAm)}</b></div>
<div><span>Beendet von</span><b>${sicher(a.beendetVon)}</b></div>` : ""}
</div>
<div class="karte-kopf" style="margin-top:14px">
<span class="karte-titel">Angeschlossene Geräte</span>
<span class="feld-hinweis">nur notiert, nicht gesteuert</span>
</div>
${geraete.length
? `<div class="its-geraete">${geraete.map((g) =>
`<span class="its-geraet">${sicher(g)}</span>`).join("")}</div>`
: `<div class="beh-text leer">Nichts eingetragen.</div>`}
<div class="karte-kopf" style="margin-top:14px">
<span class="karte-titel">Grund der Intensivbetreuung</span>
</div>
<div class="beh-text ${a.notiz ? "" : "leer"}">
${sicher(a.notiz || "Nichts eingetragen.")}</div>
</div>
<div class="karte">
<div class="karte-kopf">
<span class="karte-titel">Verlauf</span>
<span class="feld-hinweis">${d.verlauf.length} Einträge</span>
</div>
${a.laufend && d.rechte.betreuen ? `
<div class="feld">
<textarea data-neuerverlauf="1" rows="3" maxlength="1000"
placeholder="Was wurde beobachtet, was wurde getan?"></textarea>
<div class="feld-reihe" style="margin-top:8px">
<button class="knopf klein" data-verlaufspeichern="1">Eintrag schreiben</button>
<span class="feld-hinweis">Der Wortlaut bleibt genau so stehen.</span>
</div>
</div>` : ""}
${d.verlauf.length ? `
<div class="mdt-verlauf">
${d.verlauf.map((v) => `
<div class="mdt-vl-schritt">
<div class="mdt-vl-punkt info">✎</div>
<div class="mdt-vl-inhalt">
<div class="mdt-vl-kopf">
<b>${sicher(v.autor)}</b>
<span class="db-zeit">${zeit(v.am)}</span>
</div>
<div class="mdt-vl-text">${sicher(v.text)}</div>
</div>
</div>`).join("")}
</div>`
: `<div class="liste-leer">Noch nichts dokumentiert.</div>`}
</div>
</div>
`;
verdrahteEinzel(behaelter, d);
}
function verdrahteEinzel(behaelter, d) {
const a = d.aufenthalt;
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: a.patient });
});
const vorgang = behaelter.querySelector("[data-vorgang]");
if (vorgang) {
vorgang.addEventListener("click", () => {
if (!App.zustand.module.some((m) => m.id === "patienten")) {
return meldung("Die Patientenverwaltung steht dir nicht zur Verfügung.", "fehler");
}
App.oeffneModul("patienten", { aufnahme: a.aufnahme });
});
}
const zustand = behaelter.querySelector("[data-zustand]");
if (zustand) zustand.addEventListener("click", () => zustandFenster(behaelter, d));
const geraete = behaelter.querySelector("[data-geraete]");
if (geraete) geraete.addEventListener("click", () => geraeteFenster(behaelter, d));
const zustaendig = behaelter.querySelector("[data-zustaendig]");
if (zustaendig) zustaendig.addEventListener("click", () => zustaendigFenster(behaelter, d));
const beenden = behaelter.querySelector("[data-beenden]");
if (beenden) beenden.addEventListener("click", () => beendenFenster(behaelter, d));
const speichern = behaelter.querySelector("[data-verlaufspeichern]");
if (speichern) {
speichern.addEventListener("click", async () => {
const feld = behaelter.querySelector("[data-neuerverlauf]");
const text = feld.value.trim();
if (text.length < 3) return meldung("Bitte einen Text eingeben.", "fehler");
try {
await aufruf("intensiv", "verlauf", { id: a.id, text });
meldung("Eintrag geschrieben.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
});
}
}
/* ---- Intensivaufnahme ------------------------------------------------------ */
async function aufnahmeFenster(behaelter) {
let k;
try {
k = await aufruf("intensiv", "kandidaten", {});
} catch (f) { return meldung(f.message, "fehler"); }
let gewaehlt = null; // Patient/Aufnahme
let gewaehltesZimmer = null; // Zimmer der Betreuung
bestaetigen({
titel: "Patient auf die Intensivstation aufnehmen",
inhalt: `
<div class="feld"><label>Patient (aufgenommen, ohne laufende Intensivbetreuung)</label>
<div class="liste pat-trefferliste" data-patienten="1">
${k.patienten.length ? k.patienten.map((p) => `
<div class="liste-zeile" data-patient="${p.aufnahme}">
<span class="prio p${sicher(p.prioritaet)}"></span>
<div class="lz-haupt">
<div class="lz-titel">${sicher(p.name)}
<span class="pat-geburt">geb. ${sicher(p.geburt)}</span></div>
<div class="lz-unter">${sicher(String(p.grund || "").slice(0, 70))}</div>
</div>
${p.warnhinweis ? `<span class="marker kritisch">⚠</span>` : ""}
${p.allergien ? `<span class="marker warn">A</span>` : ""}
${p.zimmerKennung ? `<span class="marker info">${sicher(p.zimmerKennung)}</span>` : ""}
</div>`).join("")
: `<div class="liste-leer">Kein aufgenommener Patient verfügbar.</div>`}
</div>
<div class="feld-hinweis">Auf die Intensivstation kommt nur, wer bereits
aufgenommen ist. Die Reihenfolge ist die eingetragene Dringlichkeit.</div>
</div>
<div class="feld"><label>Zustand bei Übernahme</label>
<select name="zustand">
${(z.daten.zustaende || []).map((s) =>
`<option value="${sicher(s.id)}" ${s.id === "stabil" ? "selected" : ""}>
${sicher(s.name)}</option>`).join("")}
</select>
<div class="feld-hinweis">Deine Einschätzung. Das System leitet sie aus nichts ab.</div>
</div>
<div class="feld"><label>Platz auf der Intensivstation (freiwillig)</label>
<div class="pat-zimmerreihe" data-zimmerwahl="1">
${k.zimmer.length ? k.zimmer.map((zi) => `
<button class="pat-zimmer-knopf ${zi.status === "frei" ? "" : "belegt"}"
data-zimmer="${zi.id}"
title="${sicher(zi.stationName)}${
zi.hinweis ? " · " + sicher(zi.hinweis) : ""}">
<span class="pz-name">${sicher(zi.kennung)}</span>
<span class="pz-betten">${zi.belegt}/${zi.betten} belegt</span>
${zi.status !== "frei"
? `<span class="pz-status">${sicher(zi.status)}</span>` : ""}
</button>`).join("")
: `<div class="liste-leer">Kein Intensivplatz angelegt.</div>`}
</div>
<div class="feld-hinweis">Hier wird nur festgehalten, wo betreut wird.
Das Bett selbst belegt die Stationsverwaltung.</div>
</div>
<div class="feld"><label>Angeschlossene Geräte (freiwillig)</label>
<input name="geraete" maxlength="512"
placeholder="z. B. Beatmung, Monitoring, Perfusor"></div>
<div class="feld"><label>Grund der Intensivbetreuung (Pflicht)</label>
<textarea name="notiz" rows="3" maxlength="1000"
placeholder="Warum wird dieser Patient intensiv betreut?"></textarea></div>
<div class="feld"><label>
<input type="checkbox" class="haken" name="selbst" checked>
Ich übernehme die Zuständigkeit für diesen Patienten</label>
<div class="feld-hinweis">Ohne Häkchen bleibt der Aufenthalt ohne zuständigen
Arzt — das wird allen im Dienst gemeldet.</div></div>`,
bestaetigenText: "Aufnehmen",
bestaetigt: async (el, zu) => {
if (!gewaehlt) return meldung("Bitte zuerst einen Patienten wählen.", "fehler");
const notiz = el.querySelector("[name=notiz]").value.trim();
if (notiz.length < 5) return meldung("Bitte den Grund angeben.", "fehler");
try {
const antwort = await aufruf("intensiv", "aufnehmen", {
aufnahme: gewaehlt,
zimmer: gewaehltesZimmer,
zustand: el.querySelector("[name=zustand]").value,
geraete: el.querySelector("[name=geraete]").value.trim(),
notiz,
selbst: el.querySelector("[name=selbst]").checked,
});
zu();
meldung("Intensivaufenthalt eröffnet.", "gut");
z.ordner = "laufend";
oeffne(behaelter, antwort.id);
} catch (f) { meldung(f.message, "fehler"); }
},
nachOeffnen: (hg) => {
hg.querySelectorAll("[data-patient]").forEach((el) => {
el.addEventListener("click", () => {
gewaehlt = +el.dataset.patient;
hg.querySelectorAll("[data-patient]")
.forEach((zeile) => zeile.classList.remove("gewaehlt"));
el.classList.add("gewaehlt");
});
});
hg.querySelectorAll("[data-zimmer]").forEach((el) => {
el.addEventListener("click", () => {
// Ein zweiter Klick nimmt die Wahl wieder zurück
const schonGewaehlt = gewaehltesZimmer === +el.dataset.zimmer;
hg.querySelectorAll("[data-zimmer]").forEach((k) => k.classList.remove("gewaehlt"));
gewaehltesZimmer = schonGewaehlt ? null : +el.dataset.zimmer;
if (!schonGewaehlt) el.classList.add("gewaehlt");
});
});
},
});
}
/* ---- Zustand eintragen ------------------------------------------------------- */
function zustandFenster(behaelter, d) {
const a = d.aufenthalt;
bestaetigen({
titel: "Zustand eintragen",
inhalt: `
<p>Aktuell eingetragen: <b>${sicher(a.zustandName)}</b>
— von ${sicher(a.verantwortlichName || "niemandem")} verantwortet.</p>
<div class="feld"><label>Neue Einschätzung</label>
<select name="zustand">
${(d.zustaende || []).map((s) =>
`<option value="${sicher(s.id)}" ${s.id === a.zustand ? "selected" : ""}>
${sicher(s.name)}</option>`).join("")}
</select></div>
<div class="feld"><label>Begründung (Pflicht)</label>
<textarea name="grund" rows="3" maxlength="1000"
placeholder="Worauf stützt sich die Einschätzung?"></textarea>
<div class="feld-hinweis">Dieser Text wandert unverändert in den Verlauf.</div></div>`,
bestaetigenText: "Eintragen",
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("intensiv", "zustand", {
id: a.id,
zustand: el.querySelector("[name=zustand]").value,
grund,
});
zu();
meldung("Zustand eingetragen.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
},
});
}
/* ---- Geräte ------------------------------------------------------------------- */
function geraeteFenster(behaelter, d) {
const a = d.aufenthalt;
const vorschlaege = d.geraeteliste || [];
const vorhanden = geraeteTeile(a.geraete);
// Alles, was nicht in der Vorschlagsliste steht, bleibt als Freitext erhalten
const eigene = vorhanden.filter((g) => !vorschlaege.includes(g));
bestaetigen({
titel: "Angeschlossene Geräte",
inhalt: `
<div class="warnung">
<span class="w-symbol">ℹ</span>
<div>Das System steuert kein Gerät, liest keinen Wert aus und schlägt keinen
Alarm. Es hält fest, was jemand angeschlossen hat.</div>
</div>
<div class="feld"><label>Aus der Liste</label>
<div class="its-geraetewahl">
${vorschlaege.map((g) => `
<button class="its-geraet-knopf ${vorhanden.includes(g) ? "gewaehlt" : ""}"
data-geraet="${sicher(g)}">${sicher(g)}</button>`).join("")}
</div></div>
<div class="feld"><label>Weitere (mit Komma trennen)</label>
<input name="eigene" maxlength="512" value="${sicher(eigene.join(", "))}"></div>`,
bestaetigenText: "Speichern",
bestaetigt: async (el, zu) => {
const gewaehlt = Array.from(el.querySelectorAll(".its-geraet-knopf.gewaehlt"))
.map((k) => k.dataset.geraet);
const eigeneTexte = geraeteTeile(el.querySelector("[name=eigene]").value);
try {
await aufruf("intensiv", "geraete", {
id: a.id,
geraete: gewaehlt.concat(eigeneTexte).join(", "),
});
zu();
meldung("Geräte eingetragen.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
},
nachOeffnen: (hg) => {
hg.querySelectorAll("[data-geraet]").forEach((el) => {
el.addEventListener("click", () => el.classList.toggle("gewaehlt"));
});
},
});
}
/* ---- Zuständigkeit -------------------------------------------------------------- */
async function zustaendigFenster(behaelter, d) {
const a = d.aufenthalt;
let b;
try {
b = await aufruf("intensiv", "betreuer", {});
} catch (f) { return meldung(f.message, "fehler"); }
let gewaehlt = a.verantwortlich || null;
let niederlegen = false;
bestaetigen({
titel: "Zuständigkeit",
inhalt: `
<p>Zuständig ist zurzeit:
<b>${sicher(a.verantwortlichName || "niemand")}</b></p>
<div class="feld"><label>Im Dienst und berechtigt</label>
<div class="liste pat-trefferliste" data-betreuer="1">
${b.liste.length ? b.liste.map((p) => `
<div class="liste-zeile ${p.charakterId === a.verantwortlich ? "gewaehlt" : ""}"
data-person="${p.charakterId}">
<div class="lz-haupt">
<div class="lz-titel">${sicher(p.name)}
${p.selbst ? `<span class="marker info">du</span>` : ""}</div>
<div class="lz-unter">${sicher(p.rolleName)} ·
${sicher(p.personalnummer || "")}</div>
</div>
<span class="marker neutral">${sicher(p.funktion || "")}</span>
</div>`).join("")
: `<div class="liste-leer">Niemand mit diesem Recht ist im Dienst.</div>`}
</div>
<div class="feld-hinweis">Das System verteilt keine Zuständigkeit —
sie wird übernommen oder übertragen.</div></div>
<div class="feld"><label>
<input type="checkbox" class="haken" name="niederlegen">
Zuständigkeit niederlegen (niemand ist danach eingetragen)</label></div>
<div class="feld"><label>Vermerk${a.verantwortlich ? " / Grund" : " (freiwillig)"}</label>
<input name="grund" maxlength="255"
placeholder="Übergabe, Dienstende, Fachwechsel…"></div>`,
bestaetigenText: "Übernehmen",
bestaetigt: async (el, zu) => {
const grund = el.querySelector("[name=grund]").value.trim();
if (niederlegen && !grund) {
return meldung("Bitte angeben, warum niemand mehr zuständig ist.", "fehler");
}
if (!niederlegen && !gewaehlt) {
return meldung("Bitte eine Person wählen.", "fehler");
}
try {
await aufruf("intensiv", "verantwortlich", {
id: a.id,
charakter: niederlegen ? null : gewaehlt,
grund,
});
zu();
meldung(niederlegen ? "Zuständigkeit niedergelegt." : "Zuständigkeit eingetragen.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
},
nachOeffnen: (hg) => {
hg.querySelectorAll("[data-person]").forEach((el) => {
el.addEventListener("click", () => {
gewaehlt = +el.dataset.person;
hg.querySelectorAll("[data-person]")
.forEach((zeile) => zeile.classList.remove("gewaehlt"));
el.classList.add("gewaehlt");
});
});
const haken = hg.querySelector("[name=niederlegen]");
haken.addEventListener("change", () => {
niederlegen = haken.checked;
// Die Personenliste wird unbrauchbar, sobald niedergelegt wird
hg.querySelector("[data-betreuer]").style.opacity = niederlegen ? ".45" : "";
});
},
});
}
/* ---- Betreuung beenden ------------------------------------------------------------ */
function beendenFenster(behaelter, d) {
const a = d.aufenthalt;
bestaetigen({
titel: "Intensivbetreuung beenden",
gefahr: true,
inhalt: `
<p><b>${sicher(a.name)}</b> wird ab sofort nicht mehr intensiv betreut.</p>
<div class="warnung warn">
<span class="w-symbol">ℹ</span>
<div><b>Das ist keine Entlassung.</b> Der Patient bleibt aufgenommen, sein Bett
bleibt belegt und keine Behandlung wird dadurch abgeschlossen.</div>
</div>
${a.zustand === "kritisch" ? `
<div class="warnung kritisch">
<span class="w-symbol">⚠</span>
<div>Der Zustand steht weiterhin auf <b>kritisch</b>. Das Beenden ist
trotzdem möglich — es wird so festgehalten.</div>
</div>` : ""}
<div class="feld"><label>Begründung (Pflicht)</label>
<textarea name="grund" rows="4" maxlength="1000"
placeholder="Zustand bei Verlegung auf Normalstation, weiteres Vorgehen…"></textarea>
<div class="feld-hinweis">Dieser Text schließt den Verlauf ab.</div></div>`,
bestaetigenText: "Betreuung beenden",
bestaetigt: async (el, zu) => {
const grund = el.querySelector("[name=grund]").value.trim();
if (grund.length < 5) return meldung("Bitte eine Begründung angeben.", "fehler");
try {
await aufruf("intensiv", "beenden", { id: a.id, grund });
zu();
meldung("Intensivbetreuung beendet.", "gut");
z.offen = null;
z.ordner = "beendet";
rendern(behaelter);
} catch (f) { meldung(f.message, "fehler"); }
},
});
}
/* ---- Laden ----------------------------------------------------------------------- */
async function oeffne(behaelter, intensivId) {
try {
const d = await aufruf("intensiv", "oeffnen", { id: intensivId });
z.offen = intensivId;
einzelAnsicht(behaelter, d);
} catch (f) { meldung(f.message, "fehler"); }
}
async function rendern(behaelter) {
try {
z.daten = await aufruf("intensiv", "uebersicht", { ordner: z.ordner });
// Sprungziel aus einem Hinweis, z. B. „als kritisch eingetragen"
const ziel = App.zustand.modulZiel;
if (ziel && ziel.intensiv) {
App.zustand.modulZiel = null;
return oeffne(behaelter, ziel.intensiv);
}
if (z.offen) await oeffne(behaelter, z.offen);
else uebersicht(behaelter);
} catch (f) {
behaelter.innerHTML = `<div class="seiten-kopf"><div><h2>Intensivstation</h2>
<p>Konnte nicht geladen werden: ${sicher(f.message)}</p></div></div>`;
}
}
App.registriereAnsicht("intensiv", {
rendern,
aktualisieren() {
// Eine offene Einzelansicht oder ein offenes Fenster nicht stören
if (App.zustand.aktivesModul === "intensiv" && !z.offen
&& !document.querySelector(".fenster-hg")) {
rendern(App.$("inhalt"));
}
},
});
})();