/* =====================================================================
aether-medical / Ansicht: Labor
Zwei Blöcke, streng getrennt: das ERGEBNIS trägt das Labor ein, den
BEFUND ein Mediziner. Dazwischen liegt die Bewertung — und die nimmt
das System niemandem ab. Kein Wert wird markiert, verglichen oder
als auffällig dargestellt.
===================================================================== */
"use strict";
(() => {
const { sicher, zeit, minuten, aufruf, meldung, fenster, bestaetigen } = App;
const z = {
ordner: "offen", // offen | befund | meine | archiv
art: "",
suche: "",
daten: null,
offen: null,
};
const statusArt = {
offen: "warn", in_arbeit: "info", fertig: "gut", verworfen: "neutral",
};
const statusName = {
offen: "offen", in_arbeit: "in Arbeit", fertig: "Ergebnis liegt vor",
verworfen: "verworfen",
};
/* ---- Übersicht ---------------------------------------------------------- */
function uebersicht(behaelter) {
const d = z.daten;
behaelter.innerHTML = `
Labor
Das Labor speichert Ergebnisse. Ob ein Wert etwas bedeutet,
entscheidet ein Mediziner — nicht das System.
${d.rechte.beauftragen
? `
+ Laborauftrag stellen ` : ""}
Offen
${d.zaehler.offen}
wartet auf das Labor
Davon dringend
${d.zaehler.dringend}
vorrangig bearbeiten
Ohne Bewertung
${d.zaehler.befund}
Ergebnis da, Befund fehlt
Eigene Aufträge
${d.zaehler.meine}
von dir gestellt
Im Labor${d.zaehler.offen}
Bewertung offen${d.zaehler.befund}
Meine Aufträge
Archiv
Alle Probenarten
${d.arten.map((a) =>
`
${sicher(a.name)} `).join("")}
Aufträge
${d.liste.length} Einträge
${d.liste.length ? d.liste.map(zeile).join("")
: `
Kein Auftrag gefunden.
`}
`;
verdrahteUebersicht(behaelter);
}
function zeile(l) {
return `
${l.dringend ? `
`
: `
`}
${sicher(l.kennung)}
${sicher(l.patientName)}
${sicher(l.artName)}
${sicher(String(l.fragestellung || "").slice(0, 90))}
${l.dringend ? `
dringend ` : ""}
${l.hatErgebnis && !l.hatBefund
? `
Befund fehlt ` : ""}
${l.hatBefund ? `
befundet ` : ""}
${l.eigene ? `
dein Auftrag ` : ""}
${sicher(statusName[l.status] || l.status)}
${l.status === "offen" || l.status === "in_arbeit"
? "seit " + minuten(l.offenSeit) : zeit(l.auftrag_am)}
`;
}
function verdrahteUebersicht(behaelter) {
behaelter.querySelectorAll("[data-reiter]").forEach((el) => {
el.addEventListener("click", () => {
z.ordner = el.dataset.reiter;
rendern(behaelter);
});
});
behaelter.querySelector("[data-art]").addEventListener("change", (e) => {
z.art = e.target.value;
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", () => auftragFenster(behaelter));
}
/* ---- Einzelansicht -------------------------------------------------------- */
function einzelAnsicht(behaelter, d) {
const a = d.auftrag, s = d.stamm;
const offen = a.status === "offen";
const inArbeit = a.status === "in_arbeit";
const verworfen = a.status === "verworfen";
behaelter.innerHTML = `
${sicher(a.kennung)} · ${sicher(a.artName)}
${sicher(a.patientName)} · geb. ${sicher(a.geburt)}
${a.dringend ? " · dringend " : ""}
← Übersicht
${s.hinweis ? `
⚠
Warnhinweis — ${sicher(s.hinweis)}
` : ""}
Status ${sicher(statusName[a.status] || a.status)}
Beauftragt ${zeit(a.auftragAm)}
Von ${sicher(a.auftragVon)}
${a.bearbeitetVon ? `
Im Labor ${sicher(a.bearbeitetVon)}
Angenommen ${zeit(a.bearbeitetAm)}
` : ""}
Blutgruppe (Stammdaten)
${sicher(s.blutgruppe || "nicht bestimmt")}
Patientenakte
${offen && d.rechte.bearbeiten
? `In Arbeit nehmen ` : ""}
${(offen || inArbeit) && d.rechte.bearbeiten
? `Probe verwerfen ` : ""}
Fragestellung des Auftraggebers
${sicher(a.fragestellung || "—")}
Ergebnis — gemessene Werte
${d.rechte.bearbeiten && !verworfen
? `
${a.ergebnis ? "Ergebnis ändern" : "Ergebnis eintragen"} ` : ""}
${a.ergebnis
? sicher(a.ergebnis).replace(/\n/g, " ")
: "Das Labor hat noch nichts eingetragen."}
Reine Messwerte. Das System vergleicht sie mit nichts und markiert
nichts als auffällig.
Befund — ärztliche Bewertung
${d.rechte.befunden && a.ergebnis && !verworfen
? `
${a.befund ? "Befund ändern" : "Befund eintragen"} ` : ""}
${a.befund
? sicher(a.befund).replace(/\n/g, " ")
: a.ergebnis
? "Das Ergebnis ist noch nicht bewertet."
: "Erst wenn ein Ergebnis vorliegt, lässt sich befunden."}
${a.befundVon
? `Bewertet von ${sicher(a.befundVon)} am ${zeit(a.befundAm)}.`
: "Die Bewertung stammt immer von einem Mediziner."}
${a.ergebnis && !verworfen && d.rechte.stammdaten ? `
Blutgruppe in die Stammdaten übernehmen
Falls in diesem Auftrag eine Blutgruppe bestimmt wurde: sie wandert
nicht von selbst in die Patientenakte. Du trägst sie ausdrücklich
ein und stehst dafür gerade.
Blutgruppe eintragen${s.blutgruppe ? " (aktuell: " + sicher(s.blutgruppe) + ")" : ""}
` : ""}
Frühere Aufträge dieses Patienten
zum Vergleich — nicht verrechnet
${d.frueher.length ? d.frueher.map((f) => `
${sicher(f.kennung)}
${sicher(f.art)}
${zeit(f.auftrag_am)}
${sicher(statusName[f.status] || f.status)}
`).join("")
: `
Keine früheren Aufträge.
`}
`;
verdrahteEinzel(behaelter, d);
}
function verdrahteEinzel(behaelter, d) {
const a = d.auftrag;
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 });
});
behaelter.querySelectorAll("[data-frueher]").forEach((el) => {
el.addEventListener("click", () => oeffne(behaelter, +el.dataset.frueher));
});
const annehmen = behaelter.querySelector("[data-annehmen]");
if (annehmen) annehmen.addEventListener("click", async () => {
try {
await aufruf("labor", "annehmen", { id: a.id });
meldung("Auftrag in Arbeit genommen.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
});
const ergebnis = behaelter.querySelector("[data-ergebnis]");
if (ergebnis) ergebnis.addEventListener("click", () => ergebnisFenster(behaelter, d));
const befund = behaelter.querySelector("[data-befund]");
if (befund) befund.addEventListener("click", () => befundFenster(behaelter, d));
const verwerfen = behaelter.querySelector("[data-verwerfen]");
if (verwerfen) verwerfen.addEventListener("click", () => {
bestaetigen({
titel: "Probe verwerfen",
gefahr: true,
inhalt: `${sicher(a.kennung)} wird als verworfen abgelegt.
Der Auftraggeber wird informiert; ein neuer Auftrag muss von Hand
gestellt werden.
Begründung (Pflicht)
`,
bestaetigenText: "Verwerfen",
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("labor", "verwerfen", { id: a.id, grund });
zu();
meldung("Auftrag verworfen.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
},
});
});
const blutgruppe = behaelter.querySelector("[data-blutgruppe]");
if (blutgruppe) blutgruppe.addEventListener("click", () => blutgruppeFenster(behaelter, d));
}
/* ---- Ergebnis und Befund --------------------------------------------------- */
function ergebnisFenster(behaelter, d) {
const a = d.auftrag;
fenster({
titel: "Ergebnis eintragen · " + a.kennung,
inhalt: `
${sicher(a.patientName)} · ${sicher(a.artName)}
Fragestellung: ${sicher(a.fragestellung || "—")}
Gemessene Werte
Nur Messwerte, keine Bewertung. Die Einordnung
nimmt anschließend ein Mediziner im Befund vor.
${d.vorlage && !a.ergebnis
? `Vorlage einfügen ` : ""}`,
knoepfe: [
{ text: "Abbrechen", art: "leise", klick: (_, zu) => zu() },
{ text: "Ergebnis speichern", klick: async (el, zu) => {
try {
await aufruf("labor", "ergebnis", {
id: a.id, ergebnis: el.querySelector("[name=ergebnis]").value.trim(),
});
zu();
meldung("Ergebnis gespeichert. Der Auftraggeber wurde informiert.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
} },
],
nachOeffnen: (hg) => {
const vorlage = hg.querySelector("[data-vorlage]");
if (vorlage) vorlage.addEventListener("click", () => {
const feld = hg.querySelector("[name=ergebnis]");
feld.value = d.vorlage;
feld.focus();
});
},
});
}
function befundFenster(behaelter, d) {
const a = d.auftrag;
bestaetigen({
titel: "Befund eintragen · " + a.kennung,
inhalt: `
Ergebnis des Labors
${sicher(a.ergebnis).replace(/\n/g, " ")}
Deine Bewertung
Der Befund erscheint in der Patientenakte und im
Behandlungsbogen. Er trägt deinen Namen.
`,
bestaetigenText: "Befund speichern",
bestaetigt: async (el, zu) => {
try {
await aufruf("labor", "befund", {
id: a.id, befund: el.querySelector("[name=befund]").value.trim(),
});
zu();
meldung("Befund gespeichert.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
},
});
}
/* ---- Blutgruppe übernehmen -------------------------------------------------- */
function blutgruppeFenster(behaelter, d) {
const a = d.auftrag;
bestaetigen({
titel: "Blutgruppe in die Stammdaten übernehmen",
inhalt: `
Ergebnis des Labors
${sicher(a.ergebnis).replace(/\n/g, " ")}
Blutgruppe für ${sicher(a.patientName)}
— bitte wählen —
${d.blutgruppen.map((b) =>
`
${sicher(b)} `).join("")}
Das System liest die Blutgruppe nicht aus dem
Ergebnistext aus. Du wählst sie, und damit steht dein Name darunter.
${d.stamm.blutgruppe ? `
⚠
In den Stammdaten steht bereits ${sicher(d.stamm.blutgruppe)} .
Ein Überschreiben wird protokolliert.
` : ""}`,
bestaetigenText: "Übernehmen",
bestaetigt: async (el, zu) => {
const gewaehlt = el.querySelector("[name=blutgruppe]").value;
if (!gewaehlt) return meldung("Bitte eine Blutgruppe wählen.", "fehler");
try {
const antwort = await aufruf("labor", "blutgruppeUebernehmen", {
id: a.id, blutgruppe: gewaehlt,
});
zu();
meldung("Blutgruppe " + antwort.blutgruppe + " eingetragen.", "gut");
oeffne(behaelter, a.id);
} catch (f) { meldung(f.message, "fehler"); }
},
});
}
/* ---- Auftrag stellen --------------------------------------------------------- */
function auftragFenster(behaelter) {
const d = z.daten;
let gewaehlt = null;
bestaetigen({
titel: "Laborauftrag stellen",
inhalt: `
Patient suchen
Mindestens zwei Zeichen eingeben.
Probenart
${d.arten.map((a) =>
`${sicher(a.name)} `).join("")}
Fragestellung
Je genauer die Frage, desto brauchbarer das
Ergebnis. Das Labor misst — bewerten musst du selbst.
`,
bestaetigenText: "Auftrag stellen",
bestaetigt: async (el, zu) => {
if (!gewaehlt) return meldung("Bitte zuerst einen Patienten wählen.", "fehler");
try {
const antwort = await aufruf("labor", "beauftragen", {
patient: gewaehlt.id,
art: el.querySelector("[name=art]").value,
fragestellung: el.querySelector("[name=fragestellung]").value.trim(),
dringend: el.querySelector("[name=dringend]").checked,
});
zu();
meldung("Auftrag " + antwort.kennung + " gestellt.", "gut");
z.ordner = "meine";
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("labor", "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)
: " · Blutgruppe nicht bestimmt"}
${t.hinweis ? `
⚠ ` : ""}
`).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 = "";
});
});
} catch (f) { meldung(f.message, "fehler"); }
}, 260);
});
},
});
}
/* ---- Laden -------------------------------------------------------------------- */
async function oeffne(behaelter, laborId) {
try {
const d = await aufruf("labor", "oeffnen", { id: laborId });
z.offen = laborId;
einzelAnsicht(behaelter, d);
} catch (f) { meldung(f.message, "fehler"); }
}
async function rendern(behaelter) {
try {
z.daten = await aufruf("labor", "uebersicht", {
ordner: z.ordner, art: z.art, begriff: z.suche,
});
// Sprungziel aus einem Hinweis, z. B. „Laborergebnis verfügbar"
const ziel = App.zustand.modulZiel;
if (ziel && ziel.labor) {
App.zustand.modulZiel = null;
return oeffne(behaelter, ziel.labor);
}
if (z.offen) await oeffne(behaelter, z.offen);
else uebersicht(behaelter);
} catch (f) {
behaelter.innerHTML = `Labor
Konnte nicht geladen werden: ${sicher(f.message)}
`;
}
}
App.registriereAnsicht("labor", {
rendern,
aktualisieren() {
if (!z.offen && App.zustand.aktivesModul === "labor") rendern(App.$("inhalt"));
},
});
App.registriereSuche(async (begriff) => {
try {
const d = await aufruf("labor", "uebersicht", { ordner: "archiv", begriff });
return (d.liste || []).slice(0, 5).map((l) => ({
bereich: "Labor",
titel: l.kennung + " · " + l.patientName,
unter: l.artName + " · " + (statusName[l.status] || l.status),
}));
} catch (e) { return []; }
});
})();