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

668 lines
27 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: Patientenverwaltung
Warteliste, Belegung und Entlassung. Die Liste ist nach der vom
Mediziner gesetzten Dringlichkeit sortiert — nicht nach Wartezeit.
Wer als Nächstes drankommt, entscheidet niemand ausser dir.
===================================================================== */
"use strict";
(() => {
const { sicher, zeit, minuten, aufruf, meldung, fenster, bestaetigen } = App;
const z = {
ordner: "warteliste", // warteliste | station | entlassen
station: "",
daten: null,
offen: null, // geöffnete Aufnahme
};
// Farbe der Dringlichkeitsstufen
const prioArt = { 1: "kritisch", 2: "warn", 3: "info", 4: "neutral" };
/* ---- Gerüst ---------------------------------------------------------- */
function reiterleiste(d) {
const eintraege = [
{ id: "warteliste", text: "Warteliste", zahl: d.zaehler.wartend },
{ id: "station", text: "Im Haus", zahl: d.zaehler.aufgenommen },
{ id: "entlassen", text: "Entlassen", zahl: d.zaehler.entlassenHeute },
];
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>`;
}
/* ---- Übersicht -------------------------------------------------------- */
function uebersicht(behaelter) {
const d = z.daten;
// Belegung der Stationen als Kachelreihe
const stationsKacheln = d.stationen.map((s) => `
<div class="kennzahl ${s.frei === 0 ? "hoch" : s.frei <= 1 ? "warn" : ""}">
<div class="kz-titel">${sicher(s.name)}</div>
<div class="kz-wert">${s.frei}</div>
<div class="kz-zusatz">frei von ${s.betten} · ${s.zimmer} Zimmer</div>
</div>`).join("");
behaelter.innerHTML = `
<div class="seiten-kopf">
<div>
<h2>Patientenverwaltung</h2>
<p>Angemeldet wird von Hand, aufgenommen wird von Hand, entlassen wird von Hand.
Das System führt nur die Liste.</p>
</div>
${d.rechte.aufnehmen
? `<button class="knopf" data-anmelden="1">+ Patient anmelden</button>` : ""}
</div>
<div class="raster k5">${stationsKacheln}</div>
${reiterleiste(d)}
<div class="filterleiste">
<select data-station="1">
<option value="">Alle Stationen</option>
${d.stationen.map((s) =>
`<option value="${sicher(s.kennung)}" ${s.kennung === z.station ? "selected" : ""}>
${sicher(s.name)}</option>`).join("")}
</select>
${d.rechte.belegen
? `<button class="knopf leise" data-zimmer="1">Zimmerübersicht</button>` : ""}
</div>
<div class="karte">
<div class="karte-kopf">
<span class="karte-titel">${z.ordner === "warteliste" ? "Wartende Patienten"
: z.ordner === "station" ? "Patienten im Haus" : "Zuletzt entlassen"}</span>
<span class="feld-hinweis">${z.ordner === "warteliste"
? "Sortiert nach eingetragener Dringlichkeit, nicht nach Wartezeit"
: d.liste.length + " Einträge"}</span>
</div>
<div class="liste">
${d.liste.length ? d.liste.map(zeile).join("")
: `<div class="liste-leer">${z.ordner === "warteliste"
? "Niemand wartet." : "Keine Einträge."}</div>`}
</div>
</div>
`;
verdrahteUebersicht(behaelter);
}
function zeile(a) {
const entlassen = a.status === "entlassen";
return `<div class="liste-zeile" data-oeffne="${a.id}">
<span class="prio p${sicher(a.prioritaet)}"
title="Dringlichkeit: ${sicher(a.dringlichkeit)}"></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">${sicher(String(a.grund).slice(0, 90))}</div>
</div>
${a.hinweis ? `<span class="marker kritisch" title="${sicher(a.hinweis)}">⚠</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.aufIntensiv ? `<span class="marker kritisch">Intensiv</span>` : ""}
${a.zimmer ? `<span class="marker info">${sicher(a.zimmer)}</span>`
: a.stationName ? `<span class="marker neutral">${sicher(a.stationName)}</span>` : ""}
<span class="marker ${prioArt[a.prioritaet] || "neutral"}">
${sicher(a.dringlichkeit)}</span>
<span class="db-zeit">${entlassen
? zeit(a.entlassen_am)
: (z.ordner === "warteliste" ? "wartet " : "seit ") + minuten(a.wartet)}</span>
</div>`;
}
function verdrahteUebersicht(behaelter) {
behaelter.querySelectorAll("[data-reiter]").forEach((el) => {
el.addEventListener("click", () => {
z.ordner = el.dataset.reiter;
rendern(behaelter);
});
});
behaelter.querySelector("[data-station]").addEventListener("change", (e) => {
z.station = e.target.value;
rendern(behaelter);
});
behaelter.querySelectorAll("[data-oeffne]").forEach((el) => {
el.addEventListener("click", () => oeffne(behaelter, +el.dataset.oeffne));
});
const anmelden = behaelter.querySelector("[data-anmelden]");
if (anmelden) anmelden.addEventListener("click", () => anmeldeFenster(behaelter));
const zimmer = behaelter.querySelector("[data-zimmer]");
if (zimmer) zimmer.addEventListener("click", () => zimmerUebersicht(behaelter));
}
/* ---- Einzelansicht ------------------------------------------------------ */
function einzelAnsicht(behaelter, d) {
const a = d.aufnahme, s = d.stamm;
behaelter.innerHTML = `
<div class="seiten-kopf">
<div>
<h2>${sicher(a.name)}</h2>
<p>geb. ${sicher(a.geburt)} · ${sicher(a.dringlichkeit)}
${a.stationName ? " · " + sicher(a.stationName) : ""}
${a.zimmer ? " · Zimmer " + sicher(a.zimmer)
+ (a.bett ? " / Bett " + a.bett : "") : ""}</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)}</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>Status</span>
<b>${sicher(statusName(a.status))}</b></div>
<div class="mdt-wert"><span>Dringlichkeit</span>
<b>${sicher(a.dringlichkeit)}</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.vorerkrankungen ? "" : "fehlt"}"><span>Vorerkrankungen</span>
<b>${sicher(s.vorerkrankungen || "keine erfasst")}</b></div>
<div class="mdt-wert ${s.dauermedikation ? "" : "fehlt"}"><span>Dauermedikation</span>
<b>${sicher(s.dauermedikation || "keine erfasst")}</b></div>
<div class="mdt-wert ${s.notfallkontakt ? "" : "fehlt"}"><span>Notfallkontakt</span>
<b>${sicher(s.notfallkontakt || "keiner hinterlegt")}</b></div>
</div>
<div class="mdt-kopf-aktionen">
<button class="knopf leise klein" data-akte="1">Patientenakte öffnen</button>
${a.status === "wartend" && d.rechte.aufnehmen
? `<button class="knopf klein" data-aufnehmen="1">Aufnehmen</button>` : ""}
${a.status === "aufgenommen" && d.rechte.verlegen
? `<button class="knopf leise klein" data-verlegen="1">Verlegen</button>` : ""}
${a.status !== "entlassen" && d.rechte.aufnehmen
? `<button class="knopf leise klein" data-prio="1">Dringlichkeit</button>` : ""}
${a.status !== "entlassen" && d.rechte.entlassen
? `<button class="knopf gefahr klein" data-entlassen="1">Entlassen</button>` : ""}
</div>
</div>
<div class="db-spalten">
<div class="karte">
<div class="karte-kopf"><span class="karte-titel">Vorgang</span></div>
<div class="werte-liste">
<div><span>Grund der Vorstellung</span><b>${sicher(a.grund)}</b></div>
<div><span>Angemeldet</span><b>${zeit(a.aufgenommenAm)}</b></div>
<div><span>Angemeldet von</span><b>${sicher(a.aufgenommenVon)}</b></div>
${a.entlassenAm ? `
<div><span>Entlassen</span><b>${zeit(a.entlassenAm)}</b></div>
<div><span>Entlassen von</span><b>${sicher(a.entlassenVon)}</b></div>` : ""}
</div>
${a.entlassGrund ? `
<div class="karte-fuss"><b>Entlassgrund:</b> ${sicher(a.entlassGrund)}</div>` : ""}
</div>
<div class="karte">
<div class="karte-kopf">
<span class="karte-titel">Behandlungen zu dieser Aufnahme</span>
</div>
<div class="liste">
${d.behandlungen.length ? d.behandlungen.map((b) => `
<div class="liste-zeile">
<span class="db-nummer">${sicher(b.kennung)}</span>
<div class="lz-haupt">
<div class="lz-titel">${sicher(b.diagnose || "Noch keine Diagnose eingetragen")}</div>
<div class="lz-unter">${sicher(b.autor)} · ${zeit(b.erstellt_am)}</div>
</div>
<span class="marker ${b.status === "laufend" ? "warn" : "gut"}">
${sicher(b.status)}</span>
</div>`).join("")
: `<div class="liste-leer">Noch nichts dokumentiert.</div>`}
</div>
</div>
</div>
`;
verdrahteEinzel(behaelter, d);
}
function statusName(status) {
return { wartend: "Wartet auf Aufnahme", aufgenommen: "Aufgenommen",
entlassen: "Entlassen" }[status] || status;
}
function verdrahteEinzel(behaelter, d) {
const a = d.aufnahme;
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 aufnehmen = behaelter.querySelector("[data-aufnehmen]");
if (aufnehmen) aufnehmen.addEventListener("click", () => zuweisungsFenster(behaelter, d, false));
const verlegen = behaelter.querySelector("[data-verlegen]");
if (verlegen) verlegen.addEventListener("click", () => zuweisungsFenster(behaelter, d, true));
const prio = behaelter.querySelector("[data-prio]");
if (prio) prio.addEventListener("click", () => prioFenster(behaelter, d));
const entlassen = behaelter.querySelector("[data-entlassen]");
if (entlassen) entlassen.addEventListener("click", () => entlassungsFenster(behaelter, d));
}
/* ---- Anmeldung ----------------------------------------------------------- */
function anmeldeFenster(behaelter) {
let gewaehlt = null;
bestaetigen({
titel: "Patient anmelden",
inhalt: `
<div class="feld"><label>Person 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"><label>Grund der Vorstellung</label>
<textarea name="grund" rows="3"
placeholder="Was schildert der Patient? Was wurde beobachtet?"></textarea></div>
<div class="feld"><label>Dringlichkeit</label>
<select name="prioritaet">
<option value="1">1 — Sofort (lebensbedrohlich)</option>
<option value="2">2 — Dringend (zeitkritisch)</option>
<option value="3" selected>3 — Regulär</option>
<option value="4">4 — Nachrangig</option>
</select>
<div class="feld-hinweis">Diese Einstufung nimmst du vor. Das System
ändert sie nie — auch nicht, wenn jemand lange wartet.</div></div>
</div>`,
bestaetigenText: "Anmelden",
bestaetigt: async (el, zu) => {
if (!gewaehlt) return meldung("Bitte zuerst eine Person wählen.", "fehler");
try {
await aufruf("patienten", "anmelden", {
patient: gewaehlt.id,
grund: el.querySelector("[name=grund]").value.trim(),
prioritaet: +el.querySelector("[name=prioritaet]").value,
});
zu();
meldung("Patient auf der Warteliste.", "gut");
z.ordner = "warteliste";
rendern(behaelter);
} 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]");
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("patienten", "patientSuche", { begriff });
const treffer = antwort.treffer || [];
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.laufend ? `<span class="marker warn">${sicher(t.laufend)}</span>` : ""}
</div>`).join("")
: `<div class="liste-leer">Keine Person gefunden.</div>`;
liste.querySelectorAll("[data-person]").forEach((el) => {
el.addEventListener("click", () => {
const person = treffer.find((t) => t.id === +el.dataset.person);
if (person && person.laufend) {
return meldung("Für diesen Patienten läuft bereits eine Aufnahme.", "fehler");
}
gewaehlt = person;
liste.querySelectorAll("[data-person]")
.forEach((zeile) => zeile.classList.remove("gewaehlt"));
el.classList.add("gewaehlt");
formular.style.display = "";
});
});
} catch (f) { meldung(f.message, "fehler"); }
}, 260);
});
},
});
}
/* ---- Aufnahme und Verlegung ----------------------------------------------- */
async function zuweisungsFenster(behaelter, d, istVerlegung) {
const a = d.aufnahme;
let zimmerDaten;
try {
zimmerDaten = await aufruf("patienten", "freieZimmer", {});
} catch (f) { return meldung(f.message, "fehler"); }
// Zimmer nach Station gruppieren
const nachStation = {};
zimmerDaten.zimmer.forEach((zi) => {
(nachStation[zi.station] = nachStation[zi.station] || {
name: zi.stationName, zimmer: [],
}).zimmer.push(zi);
});
let gewaehltesZimmer = null;
let gewaehlteStation = null;
bestaetigen({
titel: istVerlegung ? "Patient verlegen" : "Patient aufnehmen",
inhalt: `
<p><b>${sicher(a.name)}</b> — ${sicher(a.grund)}</p>
${istVerlegung
? `<div class="feld"><label>Grund der Verlegung</label>
<input name="grund" maxlength="255"
placeholder="Warum wird verlegt?"></div>`
: `<div class="feld"><label>Vermerk (freiwillig)</label>
<input name="vermerk" maxlength="255"></div>`}
<div class="feld"><label>Station und Zimmer</label>
<div class="pat-zimmer" data-zimmerwahl="1">
${Object.keys(nachStation).map((kennung) => {
const st = nachStation[kennung];
return `<div class="pat-station">
<div class="pat-station-kopf">
<b>${sicher(st.name)}</b>
<button class="knopf leise klein" data-nurstation="${sicher(kennung)}">
ohne Zimmer</button>
</div>
<div class="pat-zimmerreihe">
${st.zimmer.map((zi) => `
<button class="pat-zimmer-knopf ${zi.verfuegbar ? "" : "belegt"}"
${zi.verfuegbar ? "" : "disabled"}
data-zimmer="${zi.id}" data-station="${sicher(kennung)}"
title="${sicher(zi.art)}${zi.hinweis ? " · " + sicher(zi.hinweis) : ""}">
<span class="pz-name">${sicher(zi.kennung)}</span>
<span class="pz-betten">${zi.frei}/${zi.betten} frei</span>
${zi.status !== "frei"
? `<span class="pz-status">${sicher(zi.status)}</span>` : ""}
</button>`).join("")}
</div>
</div>`;
}).join("")}
</div>
<div class="feld-hinweis">Das System zeigt nur, was frei ist. Welches Zimmer
zu diesem Patienten passt, entscheidest du.</div>
</div>`,
bestaetigenText: istVerlegung ? "Verlegen" : "Aufnehmen",
bestaetigt: async (el, zu) => {
if (!gewaehlteStation) {
return meldung("Bitte eine Station wählen.", "fehler");
}
if (istVerlegung) {
const grund = el.querySelector("[name=grund]").value.trim();
if (grund.length < 3) return meldung("Bitte den Grund angeben.", "fehler");
}
try {
const antwort = await aufruf("patienten", istVerlegung ? "verlegen" : "aufnehmen", {
id: a.id,
station: gewaehlteStation,
zimmer: gewaehltesZimmer,
grund: istVerlegung ? el.querySelector("[name=grund]").value.trim() : null,
vermerk: istVerlegung ? null : el.querySelector("[name=vermerk]").value.trim(),
});
zu();
meldung(istVerlegung ? "Patient verlegt."
: "Patient aufgenommen." + (antwort.bett ? " Bett " + antwort.bett + "." : ""), "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
},
nachOeffnen: (hg) => {
const alleKnoepfe = () => hg.querySelectorAll("[data-zimmer], [data-nurstation]");
hg.querySelectorAll("[data-zimmer]").forEach((el) => {
el.addEventListener("click", () => {
gewaehltesZimmer = +el.dataset.zimmer;
gewaehlteStation = el.dataset.station;
alleKnoepfe().forEach((k) => k.classList.remove("gewaehlt"));
el.classList.add("gewaehlt");
});
});
hg.querySelectorAll("[data-nurstation]").forEach((el) => {
el.addEventListener("click", () => {
gewaehltesZimmer = null;
gewaehlteStation = el.dataset.nurstation;
alleKnoepfe().forEach((k) => k.classList.remove("gewaehlt"));
el.classList.add("gewaehlt");
});
});
},
});
}
/* ---- Dringlichkeit --------------------------------------------------------- */
function prioFenster(behaelter, d) {
const a = d.aufnahme;
bestaetigen({
titel: "Dringlichkeit ändern",
inhalt: `
<p>Aktuell: <b>${sicher(a.dringlichkeit)}</b></p>
<div class="feld"><label>Neue Einstufung</label>
<select name="prioritaet">
${[1, 2, 3, 4].map((stufe) => `
<option value="${stufe}" ${stufe === a.prioritaet ? "selected" : ""}>
${stufe} — ${sicher(d.dringlichkeit[stufe].name)}
(${sicher(d.dringlichkeit[stufe].hinweis)})</option>`).join("")}
</select></div>
<div class="feld"><label>Begründung (Pflicht)</label>
<input name="grund" maxlength="255"
placeholder="Was hat sich am Zustand geändert?"></div>`,
bestaetigenText: "Einstufung setzen",
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("patienten", "prioritaet", {
id: a.id,
prioritaet: +el.querySelector("[name=prioritaet]").value,
grund,
});
zu();
meldung("Einstufung geändert.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
},
});
}
/* ---- Entlassung ------------------------------------------------------------ */
function entlassungsFenster(behaelter, d) {
const a = d.aufnahme;
const offeneDoku = d.behandlungen.filter((b) => b.status === "laufend").length;
bestaetigen({
titel: "Patient entlassen",
gefahr: true,
inhalt: `
<p><b>${sicher(a.name)}</b> wird aus der Klinik entlassen.
Ein belegtes Bett wird dabei frei.</p>
${offeneDoku > 0 ? `
<div class="warnung warn">
<span class="w-symbol">📄</span>
<div><b>${offeneDoku} Behandlung(en) noch nicht abgeschlossen.</b>
Sie bleiben offen — das System schließt nichts für dich ab.
Die Entlassung ist trotzdem möglich.</div>
</div>` : ""}
<div class="feld"><label>Begründung / Entlassbrief (Pflicht)</label>
<textarea name="grund" rows="4"
placeholder="Zustand bei Entlassung, Empfehlungen, Nachsorge…"></textarea></div>`,
bestaetigenText: "Entlassen",
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("patienten", "entlassen", { id: a.id, grund });
zu();
meldung("Patient entlassen.", "gut");
z.offen = null;
z.ordner = "entlassen";
rendern(behaelter);
} catch (f) { meldung(f.message, "fehler"); }
},
});
}
/* ---- Zimmerübersicht -------------------------------------------------------- */
async function zimmerUebersicht(behaelter) {
let d;
try {
d = await aufruf("patienten", "freieZimmer", {});
} catch (f) { return meldung(f.message, "fehler"); }
const nachStation = {};
d.zimmer.forEach((zi) => {
(nachStation[zi.station] = nachStation[zi.station] || {
name: zi.stationName, zimmer: [],
}).zimmer.push(zi);
});
const dialog = fenster({
titel: "Zimmerübersicht",
inhalt: `
<p class="feld-hinweis">Ein leer gewordenes Zimmer meldet sich als frei, setzt
seinen Status aber nicht selbst zurück — Reinigung und Freigabe bleiben
eine Entscheidung.</p>
${Object.keys(nachStation).map((kennung) => {
const st = nachStation[kennung];
return `<div class="pat-station">
<div class="pat-station-kopf"><b>${sicher(st.name)}</b></div>
<div class="liste">
${st.zimmer.map((zi) => `
<div class="liste-zeile">
<span class="db-nummer">${sicher(zi.kennung)}</span>
<div class="lz-haupt">
<div class="lz-titel">${zi.frei} von ${zi.betten} Betten frei</div>
<div class="lz-unter">${sicher(zi.art)}
${zi.hinweis ? " · " + sicher(zi.hinweis) : ""}</div>
</div>
<span class="marker ${zi.status === "frei" ? (zi.frei > 0 ? "gut" : "warn")
: zi.status === "reinigung" ? "warn" : "kritisch"}">
${sicher(zi.status)}</span>
<select class="pat-status" data-zimmer="${zi.id}">
<option value="">Status ändern…</option>
<option value="frei">frei</option>
<option value="reinigung">Reinigung</option>
<option value="gesperrt">gesperrt</option>
</select>
</div>`).join("")}
</div>
</div>`;
}).join("")}`,
nachOeffnen: (hg) => {
hg.querySelectorAll("[data-zimmer]").forEach((el) => {
el.addEventListener("change", async () => {
if (!el.value) return;
try {
await aufruf("patienten", "zimmerStatus", {
id: +el.dataset.zimmer, status: el.value,
});
meldung("Zimmerstatus gesetzt.", "gut");
dialog.schliessen();
rendern(behaelter);
} catch (f) {
meldung(f.message, "fehler");
el.value = "";
}
});
});
},
});
}
/* ---- Laden ------------------------------------------------------------------- */
async function oeffne(behaelter, aufnahmeId) {
try {
const d = await aufruf("patienten", "oeffnen", { id: aufnahmeId });
z.offen = aufnahmeId;
einzelAnsicht(behaelter, d);
} catch (f) { meldung(f.message, "fehler"); }
}
async function rendern(behaelter) {
try {
z.daten = await aufruf("patienten", "uebersicht", {
ordner: z.ordner, station: z.station,
});
// Sprungziel aus einem Hinweis, z. B. „Patient wartet"
const ziel = App.zustand.modulZiel;
if (ziel && ziel.aufnahme) {
App.zustand.modulZiel = null;
return oeffne(behaelter, ziel.aufnahme);
}
if (z.offen) await oeffne(behaelter, z.offen);
else uebersicht(behaelter);
} catch (f) {
behaelter.innerHTML = `<div class="seiten-kopf"><div><h2>Patientenverwaltung</h2>
<p>Konnte nicht geladen werden: ${sicher(f.message)}</p></div></div>`;
}
}
App.registriereAnsicht("patienten", {
rendern,
aktualisieren() {
// Nur die Übersicht auffrischen, nicht eine offene Einzelansicht stören
if (!z.offen && App.zustand.aktivesModul === "patienten") {
rendern(App.$("inhalt"));
}
},
});
})();