import type { GesundheitsArt } from '@prisma/client' /** * Strukturierte Zusatzangaben der Schnellerfassung. * * Das Schema kennt am Gesundheitsereignis nur Titel, Datum, bisDatum, * Behandler, Einrichtung, Abwesenheitstage und eine verschluesselte * Beschreibung. Die Details, die bei einer COPD wirklich interessieren - * Schweregrad einer Exazerbation, Sauerstofffluss, ob stationaer behandelt * wurde - haben kein eigenes Feld (siehe docs/schema-wuensche/substanz.md). * * Sie werden deshalb als beschriftete Zeilen in die Beschreibung * geschrieben, die ohnehin verschluesselt ist. Gesundheitsdaten sind * besondere Kategorien nach Art. 9 DSGVO; genau deshalb landen sie dort * und nicht in einem unverschluesselten Zusatzfeld. * * Das Format steht nur hier, damit es sich spaeter in echte Spalten * migrieren laesst. */ export type Detailfeld = { name: string beschriftung: string erklaerung?: string typ: 'text' | 'zahl' | 'auswahl' | 'mehrzeilig' | 'jaNein' optionen?: readonly string[] einheit?: string pflicht?: boolean } /** * Welche Zusatzfelder gehoeren zu welcher Ereignisart? Bewusst wenige - * eine Schnellerfassung, die zwanzig Felder zeigt, wird nicht benutzt, * und dann steht am Jahresende gar nichts in der Akte. */ export const DETAILFELDER: Partial> = { EXAZERBATION: [ { name: 'schweregrad', beschriftung: 'Schweregrad', typ: 'auswahl', optionen: ['leicht', 'mittel', 'schwer'], erklaerung: 'leicht = nur Bedarfsmedikation gesteigert, mittel = Antibiotikum oder Kortison, ' + 'schwer = Krankenhausbehandlung nötig.', pflicht: true, }, { name: 'ausloeser', beschriftung: 'Auslöser', typ: 'text', erklaerung: 'Zum Beispiel Atemwegsinfekt, Lungenentzündung, Kälte — oder „unklar“.', }, { name: 'behandlung', beschriftung: 'Behandlung', typ: 'mehrzeilig' }, { name: 'stationaer', beschriftung: 'Stationär behandelt', typ: 'jaNein', erklaerung: 'Wenn ja, leg zusätzlich einen Krankenhausaufenthalt an — nur dort werden ' + 'die Abwesenheitstage gezählt.', }, ], SAUERSTOFFVERSORGUNG: [ { name: 'fluss', beschriftung: 'Fluss', typ: 'zahl', einheit: 'l/min' }, { name: 'stundenProTag', beschriftung: 'Anwendung', typ: 'zahl', einheit: 'Stunden/Tag' }, { name: 'geraet', beschriftung: 'Gerät', typ: 'text', erklaerung: 'Konzentrator, Flüssigsauerstoff, mobile Flasche — mit Hersteller und Modell.', }, { name: 'versorger', beschriftung: 'Versorger / Lieferant', typ: 'text' }, ], KRANKENHAUS: [ { name: 'grund', beschriftung: 'Grund der Aufnahme', typ: 'mehrzeilig' }, { name: 'station', beschriftung: 'Station', typ: 'text' }, { name: 'entlassbericht', beschriftung: 'Entlassbericht angefordert', typ: 'jaNein', erklaerung: 'Der Entlassbericht ist das wichtigste Papier für eine spätere ' + 'Höherstufung. Direkt anfordern, nicht darauf warten.', }, ], MEDIKATIONSAENDERUNG: [ { name: 'abgesetzt', beschriftung: 'Abgesetzt', typ: 'mehrzeilig' }, { name: 'neu', beschriftung: 'Neu angesetzt', typ: 'mehrzeilig' }, { name: 'veranlasstDurch', beschriftung: 'Veranlasst durch', typ: 'text' }, ], INHALATION: [ { name: 'praeparat', beschriftung: 'Präparat', typ: 'text' }, { name: 'haeufigkeit', beschriftung: 'Häufigkeit', typ: 'text' }, { name: 'handhabung', beschriftung: 'Handhabung durch die Betreute', typ: 'auswahl', optionen: ['selbstständig', 'mit Anleitung', 'durch Pflegekraft'], }, ], STURZ: [ { name: 'ort', beschriftung: 'Ort des Sturzes', typ: 'text' }, { name: 'folgen', beschriftung: 'Verletzungen / Folgen', typ: 'mehrzeilig' }, { name: 'sturzprotokoll', beschriftung: 'Sturzprotokoll des Heims vorhanden', typ: 'jaNein', erklaerung: 'Die Einrichtung muss jeden Sturz protokollieren. Lass dir das Protokoll ' + 'geben und lege es zur Akte.', }, ], HILFSMITTEL: [ { name: 'hilfsmittel', beschriftung: 'Hilfsmittel', typ: 'text', pflicht: true }, { name: 'kostentraeger', beschriftung: 'Kostenträger', typ: 'auswahl', optionen: ['Krankenkasse', 'Pflegekasse', 'selbst gezahlt', 'Leihgabe', 'unklar'], }, { name: 'verordnetVon', beschriftung: 'Verordnet von', typ: 'text' }, ], IMPFUNG: [ { name: 'impfstoff', beschriftung: 'Impfstoff', typ: 'text' }, { name: 'chargennummer', beschriftung: 'Chargennummer', typ: 'text' }, { name: 'einwilligung', beschriftung: 'Einwilligung', typ: 'auswahl', optionen: [ 'durch die Betreute selbst', 'durch mich nach ärztlicher Aufklärung', 'nach Patientenverfügung', ], erklaerung: 'Wer eingewilligt hat, gehört dokumentiert — das ist eine Frage von ' + '§ 1821 und § 1829 BGB, nicht nur eine Formalie.', }, ], PFLEGEBEGUTACHTUNG: [ { name: 'ergebnisGrad', beschriftung: 'Festgestellter Pflegegrad', typ: 'zahl' }, { name: 'gutachter', beschriftung: 'Gutachter / Dienst', typ: 'text' }, { name: 'ergebnis', beschriftung: 'Ergebnis', typ: 'auswahl', optionen: ['hochgestuft', 'unverändert', 'abgelehnt', 'herabgestuft'], }, { name: 'widerspruchsfrist', beschriftung: 'Widerspruch bis', typ: 'text' }, ], } export function detailfelderFuer(art: GesundheitsArt): readonly Detailfeld[] { return DETAILFELDER[art] ?? [] } /** * Baut den Beschreibungstext aus den Zusatzfeldern und dem Freitext. * Erst die beschrifteten Zeilen, dann eine Leerzeile, dann der Freitext - * so bleibt beides beim Wiederlesen unterscheidbar. */ export function detailsFormatieren( art: GesundheitsArt, werte: Record, freitext?: string | null, ): string | null { const zeilen: string[] = [] for (const feld of detailfelderFuer(art)) { const wert = werte[feld.name] if (wert === null || wert === undefined || wert === '') continue const einheit = feld.einheit ? ` ${feld.einheit}` : '' zeilen.push(`${feld.beschriftung}: ${wert}${einheit}`) } const freitextSauber = freitext?.trim() if (freitextSauber) { if (zeilen.length > 0) zeilen.push('') zeilen.push(freitextSauber) } return zeilen.length > 0 ? zeilen.join('\n') : null } /** * Liest den Text wieder auseinander - fuer das Bearbeitungsformular. * Was sich keinem Feld zuordnen laesst, bleibt Freitext. */ export function detailsLesen( art: GesundheitsArt, beschreibung: string | null | undefined, ): { werte: Record; freitext: string } { const werte: Record = {} const freitext: string[] = [] if (!beschreibung) return { werte, freitext: '' } const felder = detailfelderFuer(art) let imFreitext = false for (const zeile of beschreibung.split('\n')) { if (imFreitext) { freitext.push(zeile) continue } if (zeile.trim() === '') { imFreitext = true continue } const treffer = felder.find((f) => zeile.startsWith(`${f.beschriftung}: `)) if (treffer) { let wert = zeile.slice(treffer.beschriftung.length + 2).trim() if (treffer.einheit && wert.endsWith(treffer.einheit)) { wert = wert.slice(0, -treffer.einheit.length).trim() } werte[treffer.name] = wert } else { freitext.push(zeile) } } return { werte, freitext: freitext.join('\n').trim() } }