feat: Extend React/Java app to match Lua functionality

Backend:
- Add Pretreatment record for pre-treatment data
- Extend Sample with pretreatment, clinicalExamDate, internalNote
- Extend TherapyRecommendation with detail fields (count, duration, dosage, location)
- Add startvacVaccination and noAntibioticTreatment flags
- Add null-safety defaults for MongoDB compatibility

Frontend:
- Add pretreatment fields to SampleRegistrationPage
- Add special pathogens section to AnamnesisPage
- Add therapy detail pickers to TherapyPage
- Improve AntibiogramPage: full text labels, centered headers
- Fix AdminDashboardPage TypeScript error in chart tooltip

Styling:
- Enlarge matrix buttons for S/I/R text
- Add matrix-col class for centered table columns
This commit is contained in:
2026-03-17 16:50:40 +01:00
parent 7c59944646
commit d03dc94ad1
13 changed files with 569 additions and 37 deletions

View File

@@ -1,12 +1,13 @@
import { useEffect, useMemo, useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { apiGet, apiPut } from "../lib/api";
import type { ActiveCatalogSummary, QuarterKey, QuarterView, SampleDetail } from "../lib/types";
import type { ActiveCatalogSummary, PathogenKind, QuarterKey, QuarterView, SampleDetail } from "../lib/types";
type QuarterFormState = {
pathogenBusinessKey: string;
customPathogenName: string;
cellCount: string;
pathogenKind: PathogenKind | null;
};
function quarterStateFromSample(sample: SampleDetail) {
@@ -15,11 +16,18 @@ function quarterStateFromSample(sample: SampleDetail) {
pathogenBusinessKey: quarter.pathogenBusinessKey ?? "",
customPathogenName: quarter.customPathogenName ?? "",
cellCount: quarter.cellCount ? String(quarter.cellCount) : "",
pathogenKind: quarter.pathogenKind,
};
return accumulator;
}, {});
}
// Special pathogen options like in Lua
const SPECIAL_PATHOGENS = [
{ key: "NO_GROWTH", label: "Kein bakterielles Wachstum", kind: "NO_GROWTH" as PathogenKind },
{ key: "CONTAMINATED", label: "Verunreinigte Probe", kind: "CONTAMINATED" as PathogenKind },
];
export default function AnamnesisPage() {
const { sampleId } = useParams();
const navigate = useNavigate();
@@ -76,6 +84,20 @@ export default function AnamnesisPage() {
return Boolean(quarterState?.pathogenBusinessKey || quarterState?.customPathogenName?.trim());
}
function selectSpecialPathogen(quarterKey: QuarterKey, kind: PathogenKind) {
updateQuarter(quarterKey, {
pathogenBusinessKey: "",
customPathogenName: "",
pathogenKind: kind,
});
}
function isSpecialPathogenSelected(quarterKey: QuarterKey, kind: PathogenKind) {
return quarterStates[quarterKey]?.pathogenKind === kind &&
!quarterStates[quarterKey]?.pathogenBusinessKey &&
!quarterStates[quarterKey]?.customPathogenName;
}
async function handleSave() {
if (!sampleId || !sample) {
return;
@@ -117,6 +139,7 @@ export default function AnamnesisPage() {
pathogenBusinessKey: "",
customPathogenName: "",
cellCount: "",
pathogenKind: null,
};
return (
@@ -168,7 +191,25 @@ export default function AnamnesisPage() {
<div className="info-chip">Auffaelliges Viertel markiert</div>
) : null}
<p className="required-label">Erreger</p>
{/* Special pathogen options like in Lua */}
<p className="required-label">Sonderfaelle</p>
<div className="pathogen-grid">
{SPECIAL_PATHOGENS.map((pathogen) => (
<button
key={pathogen.key}
type="button"
className={`pathogen-button ${
isSpecialPathogenSelected(visibleQuarter.quarterKey, pathogen.kind) ? "is-selected" : ""
}`}
onClick={() => selectSpecialPathogen(visibleQuarter.quarterKey, pathogen.kind)}
disabled={!sample.anamnesisEditable}
>
<strong>{pathogen.label}</strong>
</button>
))}
</div>
<p className="required-label section-card__spacer">Erreger (Katalog)</p>
<div className={`pathogen-grid ${showValidation && !quarterHasPathogen(visibleQuarter.quarterKey) ? "is-invalid" : ""}`}>
{catalogs.pathogens.map((pathogen) => (
<button
@@ -181,6 +222,7 @@ export default function AnamnesisPage() {
updateQuarter(visibleQuarter.quarterKey, {
pathogenBusinessKey: pathogen.businessKey,
customPathogenName: "",
pathogenKind: null,
})
}
disabled={!sample.anamnesisEditable}
@@ -199,6 +241,7 @@ export default function AnamnesisPage() {
updateQuarter(visibleQuarter.quarterKey, {
customPathogenName: event.target.value,
pathogenBusinessKey: "",
pathogenKind: null,
})
}
disabled={!sample.anamnesisEditable}
@@ -221,8 +264,8 @@ export default function AnamnesisPage() {
<div className="info-panel info-panel--spaced">
<strong>Hinweis</strong>
<p>
Kein Wachstum oder verunreinigte Proben werden später automatisch vom
Antibiogramm ausgeschlossen.
Bei "Kein bakterielles Wachstum" oder "Verunreinigte Probe" wird das Antibiogramm
übersprungen und direkt zur Therapie weitergeleitet.
</p>
</div>
</article>