diff --git a/messages/de.json b/messages/de.json index f3e2a8458..332273ff2 100644 --- a/messages/de.json +++ b/messages/de.json @@ -12427,7 +12427,7 @@ "leafSurgicalHistory": "Operationen und Eingriffe", "leafImmunizations": "Impfungen", "leafEmergency": "Notfalldaten (Blutgruppe, Kontakte, Verfügungen)", - "leafMedicationEffect": "Veränderung rund um den Start jedes Medikaments", + "leafMedicationEffect": "Veränderung rund um den Start jedes Medikaments (braucht die Medikamentenliste)", "leafWorkouts": "Trainings (Übersicht je Sportart)", "leafHrvSdnn": "HRV (SDNN)" }, diff --git a/messages/en.json b/messages/en.json index 292300819..df76fca81 100644 --- a/messages/en.json +++ b/messages/en.json @@ -12427,7 +12427,7 @@ "leafSurgicalHistory": "Surgical history", "leafImmunizations": "Immunizations", "leafEmergency": "Emergency information (blood type, contacts, directives)", - "leafMedicationEffect": "Change around each medication start", + "leafMedicationEffect": "Change around each medication start (needs the medication list)", "leafWorkouts": "Workouts (summary per sport)", "leafHrvSdnn": "HRV (SDNN)" }, diff --git a/messages/es.json b/messages/es.json index 2e222ca60..a41820b06 100644 --- a/messages/es.json +++ b/messages/es.json @@ -12427,7 +12427,7 @@ "leafSurgicalHistory": "Antecedentes quirúrgicos", "leafImmunizations": "Vacunas", "leafEmergency": "Información de emergencia (grupo sanguíneo, contactos, voluntades)", - "leafMedicationEffect": "Cambio en torno al inicio de cada medicamento", + "leafMedicationEffect": "Cambio en torno al inicio de cada medicamento (requiere la lista de medicamentos)", "leafWorkouts": "Entrenamientos (resumen por deporte)", "leafHrvSdnn": "VFC (SDNN)" }, diff --git a/messages/fr.json b/messages/fr.json index ce2b8f4ff..2c7feebcd 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -12427,7 +12427,7 @@ "leafSurgicalHistory": "Antécédents chirurgicaux", "leafImmunizations": "Vaccinations", "leafEmergency": "Informations d'urgence (groupe sanguin, contacts, directives)", - "leafMedicationEffect": "Changement autour du démarrage de chaque médicament", + "leafMedicationEffect": "Changement autour du démarrage de chaque médicament (nécessite la liste des médicaments)", "leafWorkouts": "Entraînements (résumé par sport)", "leafHrvSdnn": "VFC (SDNN)" }, diff --git a/messages/it.json b/messages/it.json index f7a72c126..7d623bd08 100644 --- a/messages/it.json +++ b/messages/it.json @@ -12427,7 +12427,7 @@ "leafSurgicalHistory": "Anamnesi chirurgica", "leafImmunizations": "Vaccinazioni", "leafEmergency": "Informazioni di emergenza (gruppo sanguigno, contatti, disposizioni)", - "leafMedicationEffect": "Variazione intorno all'inizio di ogni farmaco", + "leafMedicationEffect": "Variazione intorno all'inizio di ogni farmaco (richiede l'elenco dei farmaci)", "leafWorkouts": "Allenamenti (riepilogo per sport)", "leafHrvSdnn": "HRV (SDNN)" }, diff --git a/messages/ko.json b/messages/ko.json index dce35a661..bbfc55376 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -12427,7 +12427,7 @@ "leafSurgicalHistory": "수술 이력", "leafImmunizations": "예방접종", "leafEmergency": "응급 정보 (혈액형, 연락처, 사전지시)", - "leafMedicationEffect": "각 약 시작 전후의 변화", + "leafMedicationEffect": "각 약 시작 전후의 변화 (약 목록 필요)", "leafWorkouts": "운동(종목별 요약)", "leafHrvSdnn": "심박변이도 (SDNN)" }, diff --git a/messages/nl.json b/messages/nl.json index 1af10b087..3b16274f8 100644 --- a/messages/nl.json +++ b/messages/nl.json @@ -12427,7 +12427,7 @@ "leafSurgicalHistory": "Operatiegeschiedenis", "leafImmunizations": "Vaccinaties", "leafEmergency": "Noodinformatie (bloedgroep, contactpersonen, wilsverklaringen)", - "leafMedicationEffect": "Verandering rond de start van elk medicijn", + "leafMedicationEffect": "Verandering rond de start van elk medicijn (vereist de medicatielijst)", "leafWorkouts": "Trainingen (overzicht per sport)", "leafHrvSdnn": "HRV (SDNN)" }, diff --git a/messages/pl.json b/messages/pl.json index ccbf60069..c1e0aee67 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -12427,7 +12427,7 @@ "leafSurgicalHistory": "Historia operacji", "leafImmunizations": "Szczepienia", "leafEmergency": "Informacje na wypadek nagły (grupa krwi, kontakty, oświadczenia)", - "leafMedicationEffect": "Zmiana wokół rozpoczęcia każdego leku", + "leafMedicationEffect": "Zmiana wokół rozpoczęcia każdego leku (wymaga listy leków)", "leafWorkouts": "Treningi (podsumowanie według dyscypliny)", "leafHrvSdnn": "HRV (SDNN)" }, diff --git a/src/components/clinician/__tests__/clinician-view.test.tsx b/src/components/clinician/__tests__/clinician-view.test.tsx index 2492591bc..7a765a28e 100644 --- a/src/components/clinician/__tests__/clinician-view.test.tsx +++ b/src/components/clinician/__tests__/clinician-view.test.tsx @@ -163,6 +163,53 @@ describe("", () => { expect(html).not.toContain("Current smoker"); }); + it("shows the medication effect only beside the medication list", () => { + const report = makeReport({ + medicationEffects: [ + { + medicationName: "Named effect drug", + startsOn: "2026-01-05", + targets: [ + { + type: "BLOOD_PRESSURE_SYS", + effect: { + present: false, + reason: "insufficient_after", + level: 0.95, + window: null, + days: { + before: 40, + after: 3, + excludedLowAdherence: 0, + excludedNotTaking: 0, + }, + adherence: { + status: "tracked", + floorPct: 80, + lookbackDays: 14, + }, + levelChange: null, + slopeChangePerWeek: null, + preSlopePerWeek: null, + fit: null, + confounders: [], + }, + }, + ], + }, + ], + }); + const leaves = ["MEDICATION_EFFECT", "BLOOD_PRESSURE_SYS"] as const; + expect( + render(report, { + selection: selectionFromLeaves([...leaves, "MEDICATION_LIST"]), + }), + ).toContain("Named effect drug"); + expect( + render(report, { selection: selectionFromLeaves([...leaves]) }), + ).not.toContain("Named effect drug"); + }); + it("names both windows when a lab's report and the saved band disagree", () => { // Two windows that disagree about the same number, and a card showing one // of them is a partial answer. The PDF settles this with a footnote under diff --git a/src/components/clinician/activity-effect-sections.tsx b/src/components/clinician/activity-effect-sections.tsx index a920eb3c7..33f07921a 100644 --- a/src/components/clinician/activity-effect-sections.tsx +++ b/src/components/clinician/activity-effect-sections.tsx @@ -113,7 +113,11 @@ export function MedicationEffectSection({ /** A stated calendar day (YYYY-MM-DD) in the owner's date order. */ fmtDay: (day: string) => string; }) { - const effects = report.medicationEffects ?? []; + // The block names medications: without the list leaf it shows nothing, + // whatever the payload carries. + const effects = scope.admits("MEDICATION_LIST") + ? (report.medicationEffects ?? []) + : []; const glucoseUnit = resolveGlucoseUnit(report.glucoseUnit ?? null); return ( { expect(off.medications.length).toBeGreaterThan(0); }); + it("withholds the medication effect without the medication list", async () => { + // The effect block names every medication it describes, so withholding + // the list must withhold the names here too. + const leaves: ReportLeafId[] = [ + "MEDICATION_EFFECT", + ...MEASUREMENT_LEAF_IDS, + ]; + const withList = await collectDoctorReportData( + "u1", + RANGE, + selectionFromLeaves([...leaves, "MEDICATION_LIST"]), + { moduleMap: moduleMap() }, + ); + expect(withList.medicationEffects?.length ?? 0).toBeGreaterThan(0); + + const withoutList = await collectDoctorReportData( + "u1", + RANGE, + selectionFromLeaves(leaves), + { moduleMap: moduleMap() }, + ); + expect(withoutList.medicationEffects ?? null).toBeNull(); + expect(JSON.stringify(withoutList)).not.toContain( + withList.medicationEffects![0].medicationName, + ); + }); + it("withholds the glucose panel while the raw series stays", async () => { const withPanel = await collectDoctorReportData( "u1", diff --git a/src/lib/doctor-report/collect.ts b/src/lib/doctor-report/collect.ts index a38c3313b..371d49918 100644 --- a/src/lib/doctor-report/collect.ts +++ b/src/lib/doctor-report/collect.ts @@ -674,7 +674,11 @@ export async function collectDoctorReportData( : Promise.resolve(null), // The effect around each listed medication's start. Its own leaf AND the // target's measurement leaf must both admit it; the loader asks the gate. - gate.admits("MEDICATION_EFFECT") + // The block names every medication it describes (and the confounders), + // so it also needs the medication list: an owner who withheld the list + // never releases the names through the effect block. Same rule as the + // clinician view's compliance rows, which are named from the list. + gate.admits("MEDICATION_EFFECT") && gate.admits("MEDICATION_LIST") ? loadReportMedicationEffects({ userId, medicationIds: medications.map((m) => m.id),