/* ===================================================================== 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 `
${eintraege.map((e) => ` `).join("")}
`; } /* ---- Übersicht -------------------------------------------------------- */ function uebersicht(behaelter) { const d = z.daten; // Belegung der Stationen als Kachelreihe const stationsKacheln = d.stationen.map((s) => `
${sicher(s.name)}
${s.frei}
frei von ${s.betten} · ${s.zimmer} Zimmer
`).join(""); behaelter.innerHTML = `

Patientenverwaltung

Angemeldet wird von Hand, aufgenommen wird von Hand, entlassen wird von Hand. Das System führt nur die Liste.

${d.rechte.aufnehmen ? `` : ""}
${stationsKacheln}
${reiterleiste(d)}
${d.rechte.belegen ? `` : ""}
${z.ordner === "warteliste" ? "Wartende Patienten" : z.ordner === "station" ? "Patienten im Haus" : "Zuletzt entlassen"} ${z.ordner === "warteliste" ? "Sortiert nach eingetragener Dringlichkeit, nicht nach Wartezeit" : d.liste.length + " Einträge"}
${d.liste.length ? d.liste.map(zeile).join("") : `
${z.ordner === "warteliste" ? "Niemand wartet." : "Keine Einträge."}
`}
`; verdrahteUebersicht(behaelter); } function zeile(a) { const entlassen = a.status === "entlassen"; return `
${sicher(a.name)} geb. ${sicher(a.geburt)}
${sicher(String(a.grund).slice(0, 90))}
${a.hinweis ? `⚠` : ""} ${a.allergien ? `A` : ""} ${a.blutgruppe ? `${sicher(a.blutgruppe)}` : ""} ${a.aufIntensiv ? `Intensiv` : ""} ${a.zimmer ? `${sicher(a.zimmer)}` : a.stationName ? `${sicher(a.stationName)}` : ""} ${sicher(a.dringlichkeit)} ${entlassen ? zeit(a.entlassen_am) : (z.ordner === "warteliste" ? "wartet " : "seit ") + minuten(a.wartet)}
`; } 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 = `

${sicher(a.name)}

geb. ${sicher(a.geburt)} · ${sicher(a.dringlichkeit)} ${a.stationName ? " · " + sicher(a.stationName) : ""} ${a.zimmer ? " · Zimmer " + sicher(a.zimmer) + (a.bett ? " / Bett " + a.bett : "") : ""}

${s.hinweis ? `
⚠
Warnhinweis — ${sicher(s.hinweis)}
` : ""} ${s.allergien ? `
💊
Allergien — ${sicher(s.allergien)}
` : ""}
${s.lichtbild ? `Lichtbild` : `Kein Lichtbild`}
Status ${sicher(statusName(a.status))}
Dringlichkeit ${sicher(a.dringlichkeit)}
Blutgruppe ${sicher(s.blutgruppe || "nicht bestimmt")}
Vorerkrankungen ${sicher(s.vorerkrankungen || "keine erfasst")}
Dauermedikation ${sicher(s.dauermedikation || "keine erfasst")}
Notfallkontakt ${sicher(s.notfallkontakt || "keiner hinterlegt")}
${a.status === "wartend" && d.rechte.aufnehmen ? `` : ""} ${a.status === "aufgenommen" && d.rechte.verlegen ? `` : ""} ${a.status !== "entlassen" && d.rechte.aufnehmen ? `` : ""} ${a.status !== "entlassen" && d.rechte.entlassen ? `` : ""}
Vorgang
Grund der Vorstellung${sicher(a.grund)}
Angemeldet${zeit(a.aufgenommenAm)}
Angemeldet von${sicher(a.aufgenommenVon)}
${a.entlassenAm ? `
Entlassen${zeit(a.entlassenAm)}
Entlassen von${sicher(a.entlassenVon)}
` : ""}
${a.entlassGrund ? `
Entlassgrund: ${sicher(a.entlassGrund)}
` : ""}
Behandlungen zu dieser Aufnahme
${d.behandlungen.length ? d.behandlungen.map((b) => `
${sicher(b.kennung)}
${sicher(b.diagnose || "Noch keine Diagnose eingetragen")}
${sicher(b.autor)} · ${zeit(b.erstellt_am)}
${sicher(b.status)}
`).join("") : `
Noch nichts dokumentiert.
`}
`; 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: `
Mindestens zwei Zeichen eingeben.
Diese Einstufung nimmst du vor. Das System ändert sie nie — auch nicht, wenn jemand lange wartet.
`, 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 = `
Mindestens zwei Zeichen eingeben.
`; return; } try { const antwort = await aufruf("patienten", "patientSuche", { begriff }); const treffer = antwort.treffer || []; liste.innerHTML = treffer.length ? treffer.map((t) => `
${sicher(t.firstname)} ${sicher(t.lastname)}
geb. ${sicher(t.dob)} ${t.blutgruppe ? " · " + sicher(t.blutgruppe) : ""}
${t.hinweis ? `⚠` : ""} ${t.laufend ? `${sicher(t.laufend)}` : ""}
`).join("") : `
Keine Person gefunden.
`; 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: `

${sicher(a.name)} — ${sicher(a.grund)}

${istVerlegung ? `
` : `
`}
${Object.keys(nachStation).map((kennung) => { const st = nachStation[kennung]; return `
${sicher(st.name)}
${st.zimmer.map((zi) => ` `).join("")}
`; }).join("")}
Das System zeigt nur, was frei ist. Welches Zimmer zu diesem Patienten passt, entscheidest du.
`, 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: `

Aktuell: ${sicher(a.dringlichkeit)}

`, 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: `

${sicher(a.name)} wird aus der Klinik entlassen. Ein belegtes Bett wird dabei frei.

${offeneDoku > 0 ? `
📄
${offeneDoku} Behandlung(en) noch nicht abgeschlossen. Sie bleiben offen — das System schließt nichts für dich ab. Die Entlassung ist trotzdem möglich.
` : ""}
`, 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: `

Ein leer gewordenes Zimmer meldet sich als frei, setzt seinen Status aber nicht selbst zurück — Reinigung und Freigabe bleiben eine Entscheidung.

${Object.keys(nachStation).map((kennung) => { const st = nachStation[kennung]; return `
${sicher(st.name)}
${st.zimmer.map((zi) => `
${sicher(zi.kennung)}
${zi.frei} von ${zi.betten} Betten frei
${sicher(zi.art)} ${zi.hinweis ? " · " + sicher(zi.hinweis) : ""}
0 ? "gut" : "warn") : zi.status === "reinigung" ? "warn" : "kritisch"}"> ${sicher(zi.status)}
`).join("")}
`; }).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 = `

Patientenverwaltung

Konnte nicht geladen werden: ${sicher(f.message)}

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