/* ===================================================================== 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 = `

Behandlungen

Das System speichert ausschließlich die Dokumentation. Es behandelt nicht, es diagnostiziert nicht und es schlägt nichts vor.

${d.rechte.dokumentieren ? `` : ""}
Laufend
${d.zaehler.laufend}
noch nicht abgeschlossen
Davon von dir
${d.zaehler.meine}
deine offene Dokumentation
Heute abgeschlossen
${d.zaehler.heute}
in der ganzen Klinik
Dokumentationen ${d.liste.length} Einträge
${d.liste.length ? d.liste.map(zeile).join("") : `
Keine Dokumentation gefunden.
`}
`; verdrahteUebersicht(behaelter); } function zeile(b) { const laufend = b.status === "laufend"; return `
${sicher(b.kennung)}
${sicher(b.patientName)} geb. ${sicher(b.geburt)}
${sicher(b.diagnose || b.massnahmen || "Noch nichts eingetragen")}
${b.hinweis ? `⚠` : ""} ${b.allergien ? `A` : ""} ${b.gaben > 0 ? `💊 ${b.gaben}` : ""} ${b.einsatzNummer ? `${sicher(b.einsatzNummer)}` : ""} ${b.eigene ? `von dir` : ""} ${sicher(b.status)} ${laufend ? "offen seit " + minuten(b.offenSeit) : zeit(b.abgeschlossen_am)}
`; } 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 = `

${sicher(b.kennung)} · ${sicher(b.patientName)}

geb. ${sicher(b.geburt)} ${b.einsatzNummer ? " · Einsatz " + sicher(b.einsatzNummer) : ""} ${b.aufnahmeGrund ? " · Aufnahme: " + sicher(b.aufnahmeGrund) : ""}

${s.hinweis ? `
⚠
Warnhinweis — ${sicher(s.hinweis)}
` : ""} ${s.allergien ? `
💊
Allergien — ${sicher(s.allergien)}
Vor jeder Gabe selbst prüfen. Das System gleicht Medikamente nicht gegen Allergien ab.
` : ""}
Status${sicher(b.status)}
Angelegt${zeit(b.erstelltAm)}
Von${sicher(b.erstelltVon)}
Blutgruppe ${sicher(s.blutgruppe || "nicht bestimmt")}
Vorerkrankungen ${sicher(s.vorerkrankungen || "keine erfasst")}
Dauermedikation ${sicher(s.dauermedikation || "keine erfasst")}
${b.abgeschlossenAm ? `
Abgeschlossen${zeit(b.abgeschlossenAm)}
Durch${sicher(b.abgeschlossenVon)}
` : ""}
${laufend && d.rechte.dokumentieren ? ` ` : ""} ${laufend && d.rechte.abschliessen ? `` : ""} ${!laufend && d.rechte.abschliessen ? `` : ""}
${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…")}
Verabreichte Medikamente ${laufend && d.rechte.medikament ? `` : ""}
${d.gaben.length ? d.gaben.map((g) => `
${g.btm ? "BTM" : "💊"}
${sicher(g.name)} ${g.menge} ${sicher(g.einheit)}
${g.dosierung ? sicher(g.dosierung) + " · " : ""} ${sicher(g.autor)} · ${zeit(g.gegeben_am)} ${g.hinweis ? " · " + sicher(g.hinweis) : ""}
${laufend && (g.eigene || d.rechte.bestand) ? `` : ""}
`).join("") : `
Keine Gabe dokumentiert.
`}
Laborergebnisse des Patienten
${d.labor.length ? d.labor.map((l) => `
${sicher(l.kennung)}
${sicher(l.art)}
${sicher(l.befund || l.ergebnis || "Noch kein Ergebnis")}
${sicher(l.status)}
`).join("") : `
Keine Laboraufträge.
`}
Operationen des Patienten
${d.operationen.length ? d.operationen.map((o) => `
${sicher(o.kennung)}
${sicher(o.art)}
${o.beginn ? zeit(o.beginn) : "noch nicht begonnen"}
${sicher(o.status)}
`).join("") : `
Keine Operationen.
`}
`; 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 `
${wert ? sicher(wert).replace(/\n/g, "
") : "Nicht dokumentiert."}
${hinweis ? `
${sicher(hinweis)}
` : ""}
`; } return `
${hinweis ? `
${sicher(hinweis)}
` : ""}
`; } 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: `

${sicher(b.kennung)} — ${sicher(b.patientName)}

Die Dokumentation wird damit schreibgeschützt. Sie lässt sich später mit Begründung wieder öffnen.

${!b.diagnose ? `
📄
Es ist keine Diagnose eingetragen. Abschließen ist trotzdem möglich — das System trägt keine für dich ein.
` : ""}

Nicht gespeicherte Änderungen im Bogen gehen dabei verloren. Erst speichern, dann abschließen.

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

Die Behandlung wird wieder bearbeitbar. Der Vorgang steht dauerhaft im Protokoll.

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

Der Eintrag wird aus der Dokumentation entfernt. Wurde damals Bestand gebucht, wird er als Korrektur zurückgebucht.

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

${sicher(b.patientName)} · ${sicher(b.kennung)}

${d.stamm.allergien ? `
⚠
Bekannte Allergien: ${sicher(d.stamm.allergien)}
Das System prüft nicht gegen diese Liste. Der Abgleich ist deine Aufgabe.
` : ""}
${Object.keys(gruppen).map((kategorie) => `
${sicher(kategorieName[kategorie] || kategorie)}
${gruppen[kategorie].map((m) => ` `).join("")}
`).join("")}
Die Standarddosierung ist ein Vorschlag aus dem Katalog. Was du gibst, entscheidest du.
`, 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 = `
📦
Der Lagerbestand reicht nicht. Eine Dokumentation darf daran nicht scheitern — etwa wenn das Mittel aus dem RTW stammt.
`; 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: `
Mindestens zwei Zeichen eingeben.
Diagnose, Maßnahmen und Nachsorge trägst du anschließend im Bogen nach.
`, 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 = `
Mindestens zwei Zeichen eingeben.
`; return; } try { const antwort = await aufruf("behandlung", "patientSuche", { begriff }); const treffer = antwort.treffer || []; einsaetze = antwort.einsaetze || []; 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.allergien ? `A` : ""} ${t.aufnahme ? `aufgenommen` : ""}
`).join("") : `
Keine Person gefunden.
`; 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 = `` + einsaetze.map((e) => `` ).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 = `

Behandlungen

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

`; } } 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 []; } }); })();