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

604 lines
24 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: 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 = `
<div class="seiten-kopf">
<div>
<h2>Labor</h2>
<p>Das Labor speichert Ergebnisse. Ob ein Wert etwas bedeutet,
entscheidet ein Mediziner — nicht das System.</p>
</div>
${d.rechte.beauftragen
? `<button class="knopf" data-neu="1">+ Laborauftrag stellen</button>` : ""}
</div>
<div class="raster k4">
<div class="kennzahl ${d.zaehler.offen > 0 ? "warn" : ""}">
<div class="kz-titel">Offen</div>
<div class="kz-wert">${d.zaehler.offen}</div>
<div class="kz-zusatz">wartet auf das Labor</div>
</div>
<div class="kennzahl ${d.zaehler.dringend > 0 ? "hoch" : ""}">
<div class="kz-titel">Davon dringend</div>
<div class="kz-wert">${d.zaehler.dringend}</div>
<div class="kz-zusatz">vorrangig bearbeiten</div>
</div>
<div class="kennzahl ${d.zaehler.befund > 0 ? "warn" : ""}">
<div class="kz-titel">Ohne Bewertung</div>
<div class="kz-wert">${d.zaehler.befund}</div>
<div class="kz-zusatz">Ergebnis da, Befund fehlt</div>
</div>
<div class="kennzahl">
<div class="kz-titel">Eigene Aufträge</div>
<div class="kz-wert">${d.zaehler.meine}</div>
<div class="kz-zusatz">von dir gestellt</div>
</div>
</div>
<div class="reiter">
<button class="${z.ordner === "offen" ? "aktiv" : ""}" data-reiter="offen">
Im Labor<span class="mdt-zahl">${d.zaehler.offen}</span></button>
<button class="${z.ordner === "befund" ? "aktiv" : ""}" data-reiter="befund">
Bewertung offen<span class="mdt-zahl">${d.zaehler.befund}</span></button>
<button class="${z.ordner === "meine" ? "aktiv" : ""}" data-reiter="meine">
Meine Aufträge</button>
<button class="${z.ordner === "archiv" ? "aktiv" : ""}" data-reiter="archiv">
Archiv</button>
</div>
<div class="filterleiste">
<input data-suche="1" placeholder="Kennung oder Patient…" value="${sicher(z.suche)}">
<select data-art="1">
<option value="">Alle Probenarten</option>
${d.arten.map((a) =>
`<option value="${sicher(a.id)}" ${a.id === z.art ? "selected" : ""}>
${sicher(a.name)}</option>`).join("")}
</select>
</div>
<div class="karte">
<div class="karte-kopf">
<span class="karte-titel">Aufträge</span>
<span class="feld-hinweis">${d.liste.length} Einträge</span>
</div>
<div class="liste">
${d.liste.length ? d.liste.map(zeile).join("")
: `<div class="liste-leer">Kein Auftrag gefunden.</div>`}
</div>
</div>
`;
verdrahteUebersicht(behaelter);
}
function zeile(l) {
return `<div class="liste-zeile" data-oeffne="${l.id}">
${l.dringend ? `<span class="prio p1" title="Dringend"></span>`
: `<span class="prio p4"></span>`}
<span class="db-nummer">${sicher(l.kennung)}</span>
<div class="lz-haupt">
<div class="lz-titel">${sicher(l.patientName)}
<span class="pat-geburt">${sicher(l.artName)}</span></div>
<div class="lz-unter">${sicher(String(l.fragestellung || "").slice(0, 90))}</div>
</div>
${l.dringend ? `<span class="marker kritisch">dringend</span>` : ""}
${l.hatErgebnis && !l.hatBefund
? `<span class="marker warn">Befund fehlt</span>` : ""}
${l.hatBefund ? `<span class="marker gut">befundet</span>` : ""}
${l.eigene ? `<span class="marker neutral">dein Auftrag</span>` : ""}
<span class="marker ${statusArt[l.status] || "neutral"}">
${sicher(statusName[l.status] || l.status)}</span>
<span class="db-zeit">${l.status === "offen" || l.status === "in_arbeit"
? "seit " + minuten(l.offenSeit) : zeit(l.auftrag_am)}</span>
</div>`;
}
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 = `
<div class="seiten-kopf">
<div>
<h2>${sicher(a.kennung)} · ${sicher(a.artName)}</h2>
<p>${sicher(a.patientName)} · geb. ${sicher(a.geburt)}
${a.dringend ? " · <b>dringend</b>" : ""}</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>` : ""}
<div class="beh-kopf">
<div class="werte-liste">
<div><span>Status</span><b>${sicher(statusName[a.status] || a.status)}</b></div>
<div><span>Beauftragt</span><b>${zeit(a.auftragAm)}</b></div>
<div><span>Von</span><b>${sicher(a.auftragVon)}</b></div>
${a.bearbeitetVon ? `
<div><span>Im Labor</span><b>${sicher(a.bearbeitetVon)}</b></div>
<div><span>Angenommen</span><b>${zeit(a.bearbeitetAm)}</b></div>` : ""}
<div class="${s.blutgruppe ? "" : "leer"}"><span>Blutgruppe (Stammdaten)</span>
<b>${sicher(s.blutgruppe || "nicht bestimmt")}</b></div>
</div>
<div class="beh-kopf-aktionen">
<button class="knopf leise klein" data-akte="1">Patientenakte</button>
${offen && d.rechte.bearbeiten
? `<button class="knopf klein" data-annehmen="1">In Arbeit nehmen</button>` : ""}
${(offen || inArbeit) && d.rechte.bearbeiten
? `<button class="knopf leise klein" data-verwerfen="1">Probe verwerfen</button>` : ""}
</div>
</div>
<div class="feld" style="margin-bottom:16px">
<label>Fragestellung des Auftraggebers</label>
<div class="beh-text">${sicher(a.fragestellung || "—")}</div>
</div>
<div class="lab-spalten">
<!-- Block 1: die gemessenen Werte -->
<div class="karte lab-block">
<div class="karte-kopf">
<span class="karte-titel">Ergebnis — gemessene Werte</span>
${d.rechte.bearbeiten && !verworfen
? `<button class="knopf klein" data-ergebnis="1">
${a.ergebnis ? "Ergebnis ändern" : "Ergebnis eintragen"}</button>` : ""}
</div>
<div class="beh-text ${a.ergebnis ? "" : "leer"}">${a.ergebnis
? sicher(a.ergebnis).replace(/\n/g, "<br>")
: "Das Labor hat noch nichts eingetragen."}</div>
<div class="karte-fuss">
Reine Messwerte. Das System vergleicht sie mit nichts und markiert
nichts als auffällig.
</div>
</div>
<!-- Block 2: die ärztliche Bewertung -->
<div class="karte lab-block ${a.ergebnis && !a.befund ? "offen" : ""}">
<div class="karte-kopf">
<span class="karte-titel">Befund — ärztliche Bewertung</span>
${d.rechte.befunden && a.ergebnis && !verworfen
? `<button class="knopf klein" data-befund="1">
${a.befund ? "Befund ändern" : "Befund eintragen"}</button>` : ""}
</div>
<div class="beh-text ${a.befund ? "" : "leer"}">${a.befund
? sicher(a.befund).replace(/\n/g, "<br>")
: a.ergebnis
? "Das Ergebnis ist noch nicht bewertet."
: "Erst wenn ein Ergebnis vorliegt, lässt sich befunden."}</div>
<div class="karte-fuss">
${a.befundVon
? `Bewertet von <b>${sicher(a.befundVon)}</b> am ${zeit(a.befundAm)}.`
: "Die Bewertung stammt immer von einem Mediziner."}
</div>
</div>
</div>
${a.ergebnis && !verworfen && d.rechte.stammdaten ? `
<div class="karte" style="margin-top:14px">
<div class="karte-kopf">
<span class="karte-titel">Blutgruppe in die Stammdaten übernehmen</span>
</div>
<p class="feld-hinweis" style="margin-top:0">
Falls in diesem Auftrag eine Blutgruppe bestimmt wurde: sie wandert
<b>nicht von selbst</b> in die Patientenakte. Du trägst sie ausdrücklich
ein und stehst dafür gerade.
</p>
<button class="knopf leise klein" data-blutgruppe="1" style="margin-top:10px">
Blutgruppe eintragen${s.blutgruppe ? " (aktuell: " + sicher(s.blutgruppe) + ")" : ""}
</button>
</div>` : ""}
<div class="karte" style="margin-top:14px">
<div class="karte-kopf">
<span class="karte-titel">Frühere Aufträge dieses Patienten</span>
<span class="feld-hinweis">zum Vergleich — nicht verrechnet</span>
</div>
<div class="liste">
${d.frueher.length ? d.frueher.map((f) => `
<div class="liste-zeile klickbar" data-frueher="${f.id}">
<span class="db-nummer">${sicher(f.kennung)}</span>
<div class="lz-haupt">
<div class="lz-titel">${sicher(f.art)}</div>
<div class="lz-unter">${zeit(f.auftrag_am)}</div>
</div>
<span class="marker ${statusArt[f.status] || "neutral"}">
${sicher(statusName[f.status] || f.status)}</span>
</div>`).join("")
: `<div class="liste-leer">Keine früheren Aufträge.</div>`}
</div>
</div>
`;
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: `<p><b>${sicher(a.kennung)}</b> wird als verworfen abgelegt.
Der Auftraggeber wird informiert; ein neuer Auftrag muss von Hand
gestellt werden.</p>
<div class="feld"><label>Begründung (Pflicht)</label>
<textarea name="grund" rows="3"
placeholder="z. B. Probe hämolytisch, Röhrchen unbeschriftet"></textarea></div>`,
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: `
<p class="feld-hinweis" style="margin-top:0">
<b>${sicher(a.patientName)}</b> · ${sicher(a.artName)}<br>
Fragestellung: ${sicher(a.fragestellung || "—")}
</p>
<div class="feld"><label>Gemessene Werte</label>
<textarea name="ergebnis" rows="14"
placeholder="Werte des Labors…">${sicher(a.ergebnis || "")}</textarea>
<div class="feld-hinweis">Nur Messwerte, keine Bewertung. Die Einordnung
nimmt anschließend ein Mediziner im Befund vor.</div></div>
${d.vorlage && !a.ergebnis
? `<button class="knopf leise klein" data-vorlage="1">Vorlage einfügen</button>` : ""}`,
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: `
<div class="feld"><label>Ergebnis des Labors</label>
<div class="beh-text">${sicher(a.ergebnis).replace(/\n/g, "<br>")}</div></div>
<div class="feld"><label>Deine Bewertung</label>
<textarea name="befund" rows="7"
placeholder="Wie ordnest du die Werte ein?">${sicher(a.befund || "")}</textarea>
<div class="feld-hinweis">Der Befund erscheint in der Patientenakte und im
Behandlungsbogen. Er trägt deinen Namen.</div></div>`,
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: `
<div class="feld"><label>Ergebnis des Labors</label>
<div class="beh-text">${sicher(a.ergebnis).replace(/\n/g, "<br>")}</div></div>
<div class="feld"><label>Blutgruppe für ${sicher(a.patientName)}</label>
<select name="blutgruppe">
<option value="">— bitte wählen —</option>
${d.blutgruppen.map((b) =>
`<option value="${sicher(b)}" ${b === d.stamm.blutgruppe ? "selected" : ""}>
${sicher(b)}</option>`).join("")}
</select>
<div class="feld-hinweis">Das System liest die Blutgruppe <b>nicht</b> aus dem
Ergebnistext aus. Du wählst sie, und damit steht dein Name darunter.</div></div>
${d.stamm.blutgruppe ? `
<div class="warnung warn">
<span class="w-symbol">⚠</span>
<div>In den Stammdaten steht bereits <b>${sicher(d.stamm.blutgruppe)}</b>.
Ein Überschreiben wird protokolliert.</div>
</div>` : ""}`,
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: `
<div class="feld"><label>Patient 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>Probenart</label>
<select name="art">
${d.arten.map((a) =>
`<option value="${sicher(a.id)}">${sicher(a.name)}</option>`).join("")}
</select></div>
<div class="feld"><label>Fragestellung</label>
<textarea name="fragestellung" rows="3"
placeholder="Wonach soll gesucht werden?"></textarea>
<div class="feld-hinweis">Je genauer die Frage, desto brauchbarer das
Ergebnis. Das Labor misst — bewerten musst du selbst.</div></div>
<div class="feld">
<label><input type="checkbox" name="dringend" class="haken"> Dringend</label>
<div class="feld-hinweis">Dringende Aufträge stehen im Labor oben und
erzeugen einen Hinweis mit hoher Priorität. Bearbeitet werden sie
trotzdem von einem Menschen.</div>
</div>
</div>`,
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 = `<div class="liste-leer">Mindestens zwei Zeichen eingeben.</div>`;
return;
}
try {
const antwort = await aufruf("labor", "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)
: " · Blutgruppe nicht bestimmt"}</div>
</div>
${t.hinweis ? `<span class="marker kritisch">⚠</span>` : ""}
</div>`).join("")
: `<div class="liste-leer">Keine Person gefunden.</div>`;
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 = `<div class="seiten-kopf"><div><h2>Labor</h2>
<p>Konnte nicht geladen werden: ${sicher(f.message)}</p></div></div>`;
}
}
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 []; }
});
})();