/* =====================================================================
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 `
${eintraege.map((e) => `
${sicher(e.text)}${e.zahl}
`).join("")}
`;
}
function kennzahlen(d) {
const zk = d.zaehler;
return `
In Betreuung
${zk.laufend}
laufende Aufenthalte
Als kritisch eingetragen
${zk.kritisch}
von Hand eingestuft
Ohne zuständigen Arzt
${zk.ohneArzt}
niemand eingetragen
Ohne frischen Eintrag
${zk.langeStill}
seit über ${d.stilleNach} Minuten
`;
}
/* ---- Übersicht ---------------------------------------------------------- */
function uebersicht(behaelter) {
const d = z.daten;
behaelter.innerHTML = `
Intensivstation
Das System misst nichts und schlägt keinen Alarm. Was hier steht, hat
jemand eingetragen — der Zustand ändert sich nie von selbst.
${d.rechte.betreuen
? `
+ Intensivaufnahme ` : ""}
${kennzahlen(d)}
${reiterleiste(d)}
${z.ordner === "laufend"
? "Patienten in Intensivbetreuung" : "Beendete Aufenthalte"}
${z.ordner === "laufend"
? "Oben steht, was als am schwersten eingetragen wurde"
: d.liste.length + " Einträge"}
${d.liste.length ? d.liste.map(zeile).join("")
: `
${z.ordner === "laufend"
? "Zurzeit wird niemand intensiv betreut."
: "Noch keine beendeten Aufenthalte."}
`}
`;
verdrahteUebersicht(behaelter);
}
function zeile(a) {
const laufend = !a.beendet_am;
return `
${sicher(a.name)}
geb. ${sicher(a.geburt)}
${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)) : ""}
${a.warnhinweis ? `
⚠ ` : ""}
${a.allergien ? `
A ` : ""}
${a.blutgruppe ? `
${sicher(a.blutgruppe)} ` : ""}
${a.langeStill ? `
Verlauf offen ` : ""}
${laufend && !a.verantwortlichName
? `
kein Arzt `
: a.verantwortlichName
? `
${sicher(a.verantwortlichName)} ` : ""}
${sicher(a.zustandName)}
${laufend
? "seit " + sicher(minuten(a.liegtSeit))
: zeit(a.beendet_am)}
`;
}
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 = `
${sicher(a.name)}
geb. ${sicher(a.geburt)} · ${sicher(a.zustandName)}
${a.zimmerKennung ? " · Zimmer " + sicher(a.zimmerKennung) : ""}
${a.stationName ? " · " + sicher(a.stationName) : ""}
← Übersicht
${a.laufend ? "" : `
✔
Intensivbetreuung beendet — ${zeit(a.beendetAm)} von
${sicher(a.beendetVon || "unbekannt")}. Der Aufenthalt ist nur noch
zum Nachlesen da.
`}
${s.hinweis ? `
⚠
Warnhinweis — ${sicher(s.hinweis)}
` : ""}
${s.allergien ? `
💊
Allergien — ${sicher(s.allergien)}
` : ""}
${a.laufend && !a.verantwortlichName ? `
🧑⚕️
Kein zuständiger Arzt eingetragen. Das System wählt keinen aus —
jemand muss die Verantwortung übernehmen.
` : ""}
${s.lichtbild
? `
`
: `
Kein Lichtbild `}
Zustand
${sicher(a.zustandName)}
In Betreuung seit
${sicher(minuten(a.liegtSeit))}
Zuständig
${sicher(a.verantwortlichName || "niemand eingetragen")}
Platz
${sicher(a.zimmerKennung || "kein fester Platz")}
Blutgruppe
${sicher(s.blutgruppe || "nicht bestimmt")}
Dauermedikation
${sicher(s.dauermedikation || "keine erfasst")}
Patientenakte öffnen
${a.aufnahme
? `Aufnahme öffnen ` : ""}
${a.laufend && d.rechte.betreuen ? `
Zustand eintragen
Geräte
Zuständigkeit
Betreuung beenden ` : ""}
Aufenthalt
Aufgenommen ${zeit(a.aufgenommenAm)}
Aufgenommen von ${sicher(a.aufgenommenVon)}
${a.aufnahmeGrund
? `
Grund der Aufnahme ${sicher(a.aufnahmeGrund)}
` : ""}
${a.beendetAm ? `
Beendet ${zeit(a.beendetAm)}
Beendet von ${sicher(a.beendetVon)}
` : ""}
Angeschlossene Geräte
nur notiert, nicht gesteuert
${geraete.length
? `
${geraete.map((g) =>
`${sicher(g)} `).join("")}
`
: `
Nichts eingetragen.
`}
Grund der Intensivbetreuung
${sicher(a.notiz || "Nichts eingetragen.")}
Verlauf
${d.verlauf.length} Einträge
${a.laufend && d.rechte.betreuen ? `
Eintrag schreiben
Der Wortlaut bleibt genau so stehen.
` : ""}
${d.verlauf.length ? `
${d.verlauf.map((v) => `
✎
${sicher(v.autor)}
${zeit(v.am)}
${sicher(v.text)}
`).join("")}
`
: `
Noch nichts dokumentiert.
`}
`;
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: `
Patient (aufgenommen, ohne laufende Intensivbetreuung)
${k.patienten.length ? k.patienten.map((p) => `
${sicher(p.name)}
geb. ${sicher(p.geburt)}
${sicher(String(p.grund || "").slice(0, 70))}
${p.warnhinweis ? `
⚠ ` : ""}
${p.allergien ? `
A ` : ""}
${p.zimmerKennung ? `
${sicher(p.zimmerKennung)} ` : ""}
`).join("")
: `
Kein aufgenommener Patient verfügbar.
`}
Auf die Intensivstation kommt nur, wer bereits
aufgenommen ist. Die Reihenfolge ist die eingetragene Dringlichkeit.
Zustand bei Übernahme
${(z.daten.zustaende || []).map((s) =>
`
${sicher(s.name)} `).join("")}
Deine Einschätzung. Das System leitet sie aus nichts ab.
Platz auf der Intensivstation (freiwillig)
${k.zimmer.length ? k.zimmer.map((zi) => `
${sicher(zi.kennung)}
${zi.belegt}/${zi.betten} belegt
${zi.status !== "frei"
? `${sicher(zi.status)} ` : ""}
`).join("")
: `
Kein Intensivplatz angelegt.
`}
Hier wird nur festgehalten, wo betreut wird.
Das Bett selbst belegt die Stationsverwaltung.
Angeschlossene Geräte (freiwillig)
Grund der Intensivbetreuung (Pflicht)
`,
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: `
Aktuell eingetragen: ${sicher(a.zustandName)}
— von ${sicher(a.verantwortlichName || "niemandem")} verantwortet.
Neue Einschätzung
${(d.zustaende || []).map((s) =>
`
${sicher(s.name)} `).join("")}
Begründung (Pflicht)
Dieser Text wandert unverändert in den Verlauf.
`,
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: `
ℹ
Das System steuert kein Gerät, liest keinen Wert aus und schlägt keinen
Alarm. Es hält fest, was jemand angeschlossen hat.
Aus der Liste
${vorschlaege.map((g) => `
${sicher(g)} `).join("")}
Weitere (mit Komma trennen)
`,
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: `
Zuständig ist zurzeit:
${sicher(a.verantwortlichName || "niemand")}
Im Dienst und berechtigt
${b.liste.length ? b.liste.map((p) => `
${sicher(p.name)}
${p.selbst ? `du ` : ""}
${sicher(p.rolleName)} ·
${sicher(p.personalnummer || "")}
${sicher(p.funktion || "")}
`).join("")
: `
Niemand mit diesem Recht ist im Dienst.
`}
Das System verteilt keine Zuständigkeit —
sie wird übernommen oder übertragen.
Zuständigkeit niederlegen (niemand ist danach eingetragen)
Vermerk${a.verantwortlich ? " / Grund" : " (freiwillig)"}
`,
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: `
${sicher(a.name)} wird ab sofort nicht mehr intensiv betreut.
ℹ
Das ist keine Entlassung. Der Patient bleibt aufgenommen, sein Bett
bleibt belegt und keine Behandlung wird dadurch abgeschlossen.
${a.zustand === "kritisch" ? `
⚠
Der Zustand steht weiterhin auf kritisch . Das Beenden ist
trotzdem möglich — es wird so festgehalten.
` : ""}
Begründung (Pflicht)
Dieser Text schließt den Verlauf ab.
`,
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 = `Intensivstation
Konnte nicht geladen werden: ${sicher(f.message)}
`;
}
}
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"));
}
},
});
})();