From d3967bcdd0c3c6925b9fbd1d657e27da2bc573a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 28 Jun 2026 20:29:21 +0200 Subject: [PATCH] Surface pre-existing conditions in the medical-history section The chronic-conditions and Coach-focus self-context read as foundational medical history, so the Anamnese section now surfaces them as a read-only shared view next to allergies and family history, with a link to their single editing home in personal context. Personal context links back to the medical history. The card is coach-gated and reads through the existing /api/coach/about-me query key, so nothing is moved or duplicated and there is no schema change. --- messages/de.json | 13 ++- messages/en.json | 13 ++- messages/es.json | 13 ++- messages/fr.json | 13 ++- messages/it.json | 13 ++- messages/pl.json | 13 ++- .../records/conditions-overview.tsx | 98 +++++++++++++++++++ src/components/settings/about-me-section.tsx | 13 +++ src/components/settings/anamnesis-section.tsx | 24 +++++ 9 files changed, 207 insertions(+), 6 deletions(-) create mode 100644 src/components/records/conditions-overview.tsx diff --git a/messages/de.json b/messages/de.json index a90fecd6a..23d0ff46c 100644 --- a/messages/de.json +++ b/messages/de.json @@ -4665,7 +4665,9 @@ "focusPlaceholder": "Z. B. behalte meinen Blutdruck im Blick, motiviere mich zu mehr Bewegung …", "completeness": "{answered} von {total} beantwortet", "questionsTitle": "Der Coach hat Rückfragen", - "questionsOpenCoach": "Im Coach beantworten" + "questionsOpenCoach": "Im Coach beantworten", + "recordsLink": "Allergien, Familienanamnese und deine Vorerkrankungen gehören zu deiner Anamnese.", + "recordsLinkCta": "Anamnese öffnen" }, "responseTimeoutHeading": "Antwort-Timeout", "responseTimeoutBody": "Wie lange auf die Antwort der KI gewartet wird, bevor abgebrochen wird. Erhöhe den Wert für ein lokales oder selbst gehostetes Modell, das beim ersten Aufruf Zeit zum Laden braucht.", @@ -7774,6 +7776,15 @@ "updatedToast": "Eintrag aktualisiert", "saveError": "Der Eintrag konnte nicht gespeichert werden." } + }, + "conditions": { + "cardTitle": "Vorerkrankungen", + "cardDescription": "Chronische Erkrankungen und worauf der Coach achten soll — aus deinem persönlichen Kontext.", + "conditionsLabel": "Chronische Erkrankungen", + "focusLabel": "Worauf der Coach achtet", + "empty": "Noch nichts hinterlegt.", + "editCta": "Im persönlichen Kontext bearbeiten", + "loadError": "Vorerkrankungen konnten nicht geladen werden." } }, "environment": { diff --git a/messages/en.json b/messages/en.json index 511493b1c..810ecfc9b 100644 --- a/messages/en.json +++ b/messages/en.json @@ -4665,7 +4665,9 @@ "focusPlaceholder": "E.g. keep an eye on my blood pressure, motivate me to move more…", "completeness": "{answered} of {total} answered", "questionsTitle": "The Coach has follow-up questions", - "questionsOpenCoach": "Answer them in the Coach" + "questionsOpenCoach": "Answer them in the Coach", + "recordsLink": "Allergies, family history and your pre-existing conditions are part of your medical history.", + "recordsLinkCta": "Open medical history" }, "responseTimeoutHeading": "Response timeout", "responseTimeoutBody": "How long to wait for the AI to respond before giving up. Raise it for a local or self-hosted model that needs time to load on the first request.", @@ -7774,6 +7776,15 @@ "updatedToast": "Entry updated", "saveError": "Couldn't save the entry." } + }, + "conditions": { + "cardTitle": "Pre-existing conditions", + "cardDescription": "Chronic conditions and what the Coach watches — from your personal context.", + "conditionsLabel": "Chronic conditions", + "focusLabel": "What the Coach watches", + "empty": "Nothing noted yet.", + "editCta": "Edit in personal context", + "loadError": "Couldn't load your conditions." } }, "environment": { diff --git a/messages/es.json b/messages/es.json index 298329f0c..595bdcb39 100644 --- a/messages/es.json +++ b/messages/es.json @@ -4665,7 +4665,9 @@ "focusPlaceholder": "P. ej. vigila mi tensión arterial, motívame a moverme más…", "completeness": "{answered} de {total} respondidas", "questionsTitle": "El Coach tiene preguntas", - "questionsOpenCoach": "Responderlas en el Coach" + "questionsOpenCoach": "Responderlas en el Coach", + "recordsLink": "Las alergias, los antecedentes familiares y tus enfermedades preexistentes forman parte de tu historia clínica.", + "recordsLinkCta": "Abrir historia clínica" }, "responseTimeoutHeading": "Tiempo de espera de la respuesta", "responseTimeoutBody": "Cuánto esperar a que responda la IA antes de cancelar. Aumenta el valor para un modelo local o autoalojado que necesita tiempo para cargarse en la primera solicitud.", @@ -7774,6 +7776,15 @@ "updatedToast": "Entrada actualizada", "saveError": "No se ha podido guardar la entrada." } + }, + "conditions": { + "cardTitle": "Enfermedades preexistentes", + "cardDescription": "Enfermedades crónicas y aquello que el Coach debe vigilar, desde tu contexto personal.", + "conditionsLabel": "Enfermedades crónicas", + "focusLabel": "Qué vigila el Coach", + "empty": "Aún no hay nada anotado.", + "editCta": "Editar en el contexto personal", + "loadError": "No se pudieron cargar tus enfermedades." } }, "environment": { diff --git a/messages/fr.json b/messages/fr.json index ae54eb4e2..0bd9561de 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -4665,7 +4665,9 @@ "focusPlaceholder": "P. ex. surveille ma tension, motive-moi à bouger davantage…", "completeness": "{answered} sur {total} renseignés", "questionsTitle": "Le Coach a des questions", - "questionsOpenCoach": "Y répondre dans le Coach" + "questionsOpenCoach": "Y répondre dans le Coach", + "recordsLink": "Les allergies, les antécédents familiaux et vos affections préexistantes font partie de vos antécédents médicaux.", + "recordsLinkCta": "Ouvrir les antécédents médicaux" }, "responseTimeoutHeading": "Délai de réponse", "responseTimeoutBody": "Durée d'attente de la réponse de l'IA avant d'abandonner. Augmentez-la pour un modèle local ou auto-hébergé qui a besoin de temps pour se charger à la première requête.", @@ -7774,6 +7776,15 @@ "updatedToast": "Entrée mise à jour", "saveError": "Impossible d'enregistrer l'entrée." } + }, + "conditions": { + "cardTitle": "Affections préexistantes", + "cardDescription": "Affections chroniques et ce que le Coach doit surveiller, d'après votre contexte personnel.", + "conditionsLabel": "Affections chroniques", + "focusLabel": "Ce que le Coach surveille", + "empty": "Rien de noté pour l'instant.", + "editCta": "Modifier dans le contexte personnel", + "loadError": "Impossible de charger vos antécédents." } }, "environment": { diff --git a/messages/it.json b/messages/it.json index 7d47c79f9..b2717dd0a 100644 --- a/messages/it.json +++ b/messages/it.json @@ -4665,7 +4665,9 @@ "focusPlaceholder": "Es. tieni d'occhio la mia pressione, motivami a muovermi di più…", "completeness": "{answered} su {total} compilati", "questionsTitle": "Il Coach ha alcune domande", - "questionsOpenCoach": "Rispondi nel Coach" + "questionsOpenCoach": "Rispondi nel Coach", + "recordsLink": "Allergie, anamnesi familiare e le tue patologie preesistenti fanno parte della tua anamnesi.", + "recordsLinkCta": "Apri anamnesi" }, "responseTimeoutHeading": "Timeout della risposta", "responseTimeoutBody": "Quanto attendere la risposta dell'IA prima di annullare. Aumentalo per un modello locale o self-hosted che ha bisogno di tempo per caricarsi alla prima richiesta.", @@ -7774,6 +7776,15 @@ "updatedToast": "Voce aggiornata", "saveError": "Impossibile salvare la voce." } + }, + "conditions": { + "cardTitle": "Patologie preesistenti", + "cardDescription": "Patologie croniche e ciò che il Coach deve tenere d'occhio, dal tuo contesto personale.", + "conditionsLabel": "Patologie croniche", + "focusLabel": "Cosa tiene d'occhio il Coach", + "empty": "Ancora nessuna annotazione.", + "editCta": "Modifica nel contesto personale", + "loadError": "Impossibile caricare le tue patologie." } }, "environment": { diff --git a/messages/pl.json b/messages/pl.json index 6e0a5ce53..07760e057 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -4665,7 +4665,9 @@ "focusPlaceholder": "Np. pilnuj mojego ciśnienia, motywuj mnie do ruchu…", "completeness": "Wypełnione: {answered} z {total}", "questionsTitle": "Coach ma pytania", - "questionsOpenCoach": "Odpowiedz w Coachu" + "questionsOpenCoach": "Odpowiedz w Coachu", + "recordsLink": "Alergie, wywiad rodzinny i Twoje choroby przewlekłe są częścią Twojego wywiadu medycznego.", + "recordsLinkCta": "Otwórz wywiad medyczny" }, "responseTimeoutHeading": "Limit czasu odpowiedzi", "responseTimeoutBody": "Jak długo czekać na odpowiedź AI, zanim nastąpi przerwanie. Zwiększ wartość dla lokalnego lub samodzielnie hostowanego modelu, który potrzebuje czasu na załadowanie przy pierwszym żądaniu.", @@ -7774,6 +7776,15 @@ "updatedToast": "Wpis zaktualizowany", "saveError": "Nie udało się zapisać wpisu." } + }, + "conditions": { + "cardTitle": "Choroby przewlekłe", + "cardDescription": "Choroby przewlekłe i to, na co Coach ma zwracać uwagę — z Twojego kontekstu osobistego.", + "conditionsLabel": "Choroby przewlekłe", + "focusLabel": "Na co zwraca uwagę Coach", + "empty": "Nic jeszcze nie zapisano.", + "editCta": "Edytuj w kontekście osobistym", + "loadError": "Nie udało się wczytać Twoich chorób." } }, "environment": { diff --git a/src/components/records/conditions-overview.tsx b/src/components/records/conditions-overview.tsx new file mode 100644 index 000000000..2accbd77b --- /dev/null +++ b/src/components/records/conditions-overview.tsx @@ -0,0 +1,98 @@ +"use client"; + +/** + * v1.25.1 — pre-existing / chronic conditions, surfaced in the Anamnese + * (medical-history) home. + * + * The chronic-conditions note and the explicit "what should the Coach watch" + * focus the user entered in Settings → Profile → "About me" are foundational + * medical history, so they are surfaced here next to allergies + family history + * to give one coherent medical-history overview. + * + * This is a READ-ONLY shared view of the existing self-context + * (`GET /api/coach/about-me`) — it reads through the same query-key the "About + * me" panel writes, so the two stay in lockstep. Editing keeps its single home + * in personal context (linked below); nothing is moved, no new storage, no + * schema change. Plain text only — values render as React text children, no + * markdown renderer (XSS posture, see contributor notes). + */ +import Link from "next/link"; +import { useQuery } from "@tanstack/react-query"; +import { Loader2 } from "lucide-react"; + +import { useTranslations } from "@/lib/i18n/context"; +import { queryKeys } from "@/lib/query-keys"; +import { apiGet } from "@/lib/api/api-fetch"; + +interface SelfContext { + conditions: string | null; + coachFocus: string | null; +} + +export function ConditionsOverview() { + const { t } = useTranslations(); + const { data, isLoading, isError } = useQuery({ + queryKey: queryKeys.coachAboutMe(), + queryFn: () => apiGet("/api/coach/about-me"), + }); + + const conditions = (data?.conditions ?? "").trim(); + const coachFocus = (data?.coachFocus ?? "").trim(); + const hasAny = conditions.length > 0 || coachFocus.length > 0; + + return ( +
+ {isLoading && ( + + )} + + {isError && ( +

+ {t("records.conditions.loadError")} +

+ )} + + {!isLoading && !isError && ( + <> + {hasAny ? ( +
+ {conditions.length > 0 && ( +
+
+ {t("records.conditions.conditionsLabel")} +
+
+ {conditions} +
+
+ )} + {coachFocus.length > 0 && ( +
+
+ {t("records.conditions.focusLabel")} +
+
+ {coachFocus} +
+
+ )} +
+ ) : ( +

+ {t("records.conditions.empty")} +

+ )} + +

+ + {t("records.conditions.editCta")} + +

+ + )} +
+ ); +} diff --git a/src/components/settings/about-me-section.tsx b/src/components/settings/about-me-section.tsx index 6683d45a4..12638978d 100644 --- a/src/components/settings/about-me-section.tsx +++ b/src/components/settings/about-me-section.tsx @@ -350,6 +350,19 @@ export function AboutMeSection({

{t("settings.ai.aboutMe.hint")} {t("settings.ai.aboutMe.profileHint")}

+ + {/* v1.25.1 — cross-link to the Anamnese (medical-history) home. The + chronic conditions noted above plus allergies + family history read as + one coherent medical history there. */} +

+ {t("settings.ai.aboutMe.recordsLink")}{" "} + + {t("settings.ai.aboutMe.recordsLinkCta")} + +

); } diff --git a/src/components/settings/anamnesis-section.tsx b/src/components/settings/anamnesis-section.tsx index 546e10384..6b4cfe942 100644 --- a/src/components/settings/anamnesis-section.tsx +++ b/src/components/settings/anamnesis-section.tsx @@ -8,6 +8,13 @@ * card. These are patient-reported reference records — not a time-series * signal and not a clinical diagnosis — surfaced alongside the existing * tracking-domain settings sections (Labs / Illness / Vorsorge). + * + * v1.25.1 — the section also surfaces the pre-existing / chronic conditions the + * Coach watches (the self-context entered under Profile → "About me") as a + * read-only shared view, so conditions + allergies + family history read as one + * coherent medical history. The conditions card is coach-gated (the data only + * feeds the Coach) and links back to its single editing home in personal + * context. */ import { @@ -18,14 +25,31 @@ import { CardTitle, } from "@/components/ui/card"; import { useTranslations } from "@/lib/i18n/context"; +import { useModuleEnabled } from "@/hooks/use-module-enabled"; import { AllergyManager } from "@/components/records/allergy-manager"; +import { ConditionsOverview } from "@/components/records/conditions-overview"; import { FamilyHistoryManager } from "@/components/records/family-history-manager"; export function AnamnesisSection() { const { t } = useTranslations(); + const coachEnabled = useModuleEnabled("coach"); return (
+ {coachEnabled && ( + + + {t("records.conditions.cardTitle")} + + {t("records.conditions.cardDescription")} + + + + + + + )} + {t("records.allergies.cardTitle")}