/* =====================================================================
aether-medical / Ansicht: Übersicht
Der Lagebericht der Klinik. Jede Zahl kommt frisch vom Server; keine
ist fest verdrahtet. Die Warnungen betreffen ausschliesslich Bestände,
Fristen und Besetzung — nie den Zustand eines Patienten, den das
System selbst bewertet hätte.
===================================================================== */
"use strict";
(() => {
const { sicher, zeit, minuten, aufruf, meldung } = App;
const z = { daten: null, behaelter: null };
/* ---- Bausteine ------------------------------------------------------ */
/**
* Kennzahlkachel
* @param {string|number} wert Angezeigter Wert
* @param {string} titel Beschriftung
* @param {string} zusatz Zusatzzeile
* @param {string} art "", "gut", "warn", "hoch"
* @param {string} ziel Modul, in das ein Klick springt
*/
function kachel(wert, titel, zusatz, art, ziel) {
return `
${sicher(titel)}
${sicher(wert)}
${zusatz ? `
${sicher(zusatz)}
` : ""}
`;
}
/** Waagerechter Belegungsbalken */
function belegungsBalken(name, belegt, gesamt) {
const anteil = gesamt > 0 ? Math.round((belegt / gesamt) * 100) : 0;
// Je voller, desto dringlicher die Farbe
const farbe = anteil >= 90 ? "var(--kritisch)"
: anteil >= 70 ? "var(--warn)" : "var(--gut)";
return `
${sicher(name)}
${belegt}/${gesamt}
`;
}
/** Name einer Station anhand ihrer Kennung */
function stationsName(id) {
const treffer = (App.zustand.stationen || []).find((s) => s.id === id);
return treffer ? treffer.name : (id || "—");
}
/* ---- Zeichnen -------------------------------------------------------- */
function zeichne(behaelter) {
const d = z.daten;
const dienst = d.dienst, ein = d.einsaetze, pat = d.patienten;
const bel = d.belegung, offen = d.offen;
behaelter.innerHTML = `
Übersicht
Stand der Klinik. Alle Zahlen sind Momentaufnahmen —
sie lösen nichts aus und ersetzen keine Einschätzung am Patienten.
${zeit(new Date().toISOString())}
${d.warnungen.length ? `
${d.warnungen.map((w) => `
${w.art === "kritisch" ? "⚠" : "❗"}
${sicher(w.titel)} — ${sicher(w.text)}
`).join("")}
` : ""}
${kachel(dienst.imDienst, "Im Dienst",
dienst.leitstelle ? "Leitstelle: " + dienst.leitstelle : "Leitstelle unbesetzt",
dienst.imDienst === 0 ? "hoch" : "gut")}
${kachel(dienst.notaerzte, "Notärzte",
dienst.aufRtw + " auf RTW · " + dienst.imOp + " im OP")}
${kachel(ein.offen, "Offene Notrufe",
ein.laufend + " laufend · " + ein.dringend + " dringend",
ein.offen > 0 ? "warn" : "", "leitstelle")}
${kachel(pat.aufgenommen, "Patienten im Haus",
pat.wartend + " warten auf Aufnahme", "", "patienten")}
${kachel(pat.intensiv, "Intensivpatienten",
pat.kritisch + " als kritisch eingetragen",
pat.kritisch > 0 ? "hoch" : "", "intensiv")}
${kachel(bel.opLaufend + "/" + bel.opSaeleGesamt, "OP-Säle belegt",
bel.opGeplant + " geplant", "", "operation")}
${kachel(bel.bettenFrei, "Freie Betten",
"von " + bel.bettenGesamt + " insgesamt",
bel.bettenFrei === 0 ? "hoch" : bel.bettenFrei <= 2 ? "warn" : "gut", "klinik")}
${kachel(bel.behandlungFrei, "Freie Behandlungsräume",
"von " + bel.behandlungGesamt + " insgesamt",
bel.behandlungFrei === 0 ? "warn" : "", "klinik")}
${kachel(offen.dokumentationen, "Offene Dokumentationen",
offen.eigeneDoku + " davon von dir",
offen.eigeneDoku > 0 ? "warn" : "", "behandlung")}
${kachel(offen.laborOffen, "Laboraufträge offen",
offen.laborUnbefundet + " warten auf Bewertung", "", "labor")}
${kachel(offen.berichteEntwurf, "Eigene Berichtsentwürfe",
"noch nicht eingereicht",
offen.berichteEntwurf > 0 ? "warn" : "", "bericht")}
${kachel(pat.laengsteWartezeit > 0 ? minuten(pat.laengsteWartezeit) : "—",
"Längste Wartezeit",
pat.wartend + " Patienten warten",
pat.laengsteWartezeit >= 30 ? "hoch"
: pat.laengsteWartezeit >= 10 ? "warn" : "", "patienten")}
Wartende Patienten
Aufnahme entscheidet ein Mediziner
${pat.warteliste.length ? pat.warteliste.map((p) => `
${sicher(p.firstname)} ${sicher(p.lastname)}
${sicher(String(p.grund).slice(0, 70))}
wartet ${minuten(p.wartet)}
`).join("")
: `
Niemand wartet auf Aufnahme.
`}
Offene und laufende Einsätze
Zuweisung durch die Leitstelle
${ein.liste.length ? ein.liste.map((e) => `
${sicher(e.nummer)}
${sicher(e.titel)}
${sicher(e.ort || "Ort offen")}
${e.status === "offen" ? "offen" : "laufend"}
`).join("")
: `
Keine offenen Einsätze.
`}
Belegung
${belegungsBalken("Betten", bel.bettenBelegt, bel.bettenGesamt)}
${belegungsBalken("Behandlungsräume",
bel.behandlungGesamt - bel.behandlungFrei, bel.behandlungGesamt)}
${belegungsBalken("OP-Säle", bel.opLaufend, bel.opSaeleGesamt)}
Kollegen im Dienst
${dienst.imDienst} anwesend
${dienst.liste.length ? dienst.liste.slice(0, 8).map((m) => `
${sicher(m.personalnummer)}
${sicher(m.name)}
${sicher(m.rolleName)}
· ${sicher(stationsName(m.station))}
${m.leitstelle ? `
Leitstelle` : ""}
${sicher(funktionsName(m.funktion))}
`).join("")
: `
Niemand im Dienst.
`}
Das System unterstützt medizinische Entscheidungen — es trifft niemals selbst
welche. Es nimmt niemanden auf, entlässt niemanden, stellt keine Diagnose,
gibt kein Medikament aus und stellt keinen Tod fest. Wartezeiten und Fristen
laufen ab, ohne dass etwas passiert, bis ein Mediziner entscheidet.
`;
verdrahte(behaelter);
}
/** Farbe zum Dienststatus aus der Konfiguration */
function farbeZuStatus(status) {
const treffer = (App.zustand.status || []).find((s) => s.id === status);
return treffer ? treffer.farbe : "var(--flaeche-3)";
}
/** Klartextname einer Dienstfunktion */
function funktionsName(funktion) {
const treffer = (App.zustand.funktionen || []).find((f) => f.id === funktion);
return treffer ? treffer.name : (funktion || "—");
}
function verdrahte(behaelter) {
// Kacheln und Warnungen springen in das zuständige Modul
behaelter.querySelectorAll("[data-springe]").forEach((el) => {
el.addEventListener("click", () => {
const ziel = el.dataset.springe;
if (!App.zustand.module.some((m) => m.id === ziel)) {
meldung("Dieses Modul steht dir nicht zur Verfügung.", "fehler");
return;
}
App.oeffneModul(ziel);
});
});
}
/* ---- Laden ------------------------------------------------------------ */
async function rendern(behaelter) {
z.behaelter = behaelter;
try {
z.daten = await aufruf("dashboard", "laden", {});
// Hinweise aus dem ersten Laden in den Kern übernehmen
App.zustand.hinweise = z.daten.hinweise || [];
zeichne(behaelter);
} catch (f) {
behaelter.innerHTML = `Übersicht
Konnte nicht geladen werden: ${sicher(f.message)}
`;
}
}
App.registriereAnsicht("dashboard", {
rendern,
// Wird gerufen, wenn sich Dienstliste oder Einsätze ändern
aktualisieren() {
if (z.behaelter && App.zustand.aktivesModul === "dashboard") rendern(z.behaelter);
},
});
})();