From d73656685ae7b0529eb846e340c9d8f1e28ace7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Tue, 30 Jun 2026 23:45:06 +0200 Subject: [PATCH] fix(settings): move pre-existing conditions into Anamnese The chronic / pre-existing conditions the Coach watches lived under the personal-context "About me" panel, but they belong with the rest of the medical history. Move the conditions and the "what should the Coach watch" focus into Settings -> Anamnese, next to allergies and family history, and make them editable inline there. The store is unchanged: both fields are still part of the /api/coach/about-me payload, read and written through the same query key, so the Coach and every other consumer keep reading the same data. The About-me panel keeps the free-text note and the allergies line and omits the two conditions fields on save, leaving them untouched. --- messages/de.json | 8 +- messages/en.json | 8 +- messages/es.json | 8 +- messages/fr.json | 8 +- messages/it.json | 8 +- messages/pl.json | 8 +- src/components/records/conditions-manager.tsx | 166 ++++++++++++++++++ .../records/conditions-overview.tsx | 98 ----------- src/components/settings/about-me-section.tsx | 108 ++++-------- src/components/settings/anamnesis-section.tsx | 17 +- 10 files changed, 233 insertions(+), 204 deletions(-) create mode 100644 src/components/records/conditions-manager.tsx delete mode 100644 src/components/records/conditions-overview.tsx diff --git a/messages/de.json b/messages/de.json index 6f5243478..38556d3ae 100644 --- a/messages/de.json +++ b/messages/de.json @@ -7828,12 +7828,12 @@ }, "conditions": { "cardTitle": "Vorerkrankungen", - "cardDescription": "Chronische Erkrankungen und worauf der Coach achten soll — aus deinem persönlichen Kontext.", + "cardDescription": "Chronische Erkrankungen und worauf der Coach achten soll.", "conditionsLabel": "Chronische Erkrankungen", "focusLabel": "Worauf der Coach achtet", - "empty": "Noch nichts hinterlegt.", - "editCta": "Im persönlichen Kontext bearbeiten", - "loadError": "Vorerkrankungen konnten nicht geladen werden." + "loadError": "Vorerkrankungen konnten nicht geladen werden.", + "savedToast": "Erkrankungen gespeichert.", + "saveError": "Speichern fehlgeschlagen. Bitte versuche es erneut." } }, "environment": { diff --git a/messages/en.json b/messages/en.json index a1094539e..b2da6e5f8 100644 --- a/messages/en.json +++ b/messages/en.json @@ -7828,12 +7828,12 @@ }, "conditions": { "cardTitle": "Pre-existing conditions", - "cardDescription": "Chronic conditions and what the Coach watches — from your personal context.", + "cardDescription": "Chronic conditions and what the Coach watches.", "conditionsLabel": "Chronic conditions", "focusLabel": "What the Coach watches", - "empty": "Nothing noted yet.", - "editCta": "Edit in personal context", - "loadError": "Couldn't load your conditions." + "loadError": "Couldn't load your conditions.", + "savedToast": "Conditions saved.", + "saveError": "Could not save. Please try again." } }, "environment": { diff --git a/messages/es.json b/messages/es.json index 5df7f651f..0389da016 100644 --- a/messages/es.json +++ b/messages/es.json @@ -7828,12 +7828,12 @@ }, "conditions": { "cardTitle": "Enfermedades preexistentes", - "cardDescription": "Enfermedades crónicas y aquello que el Coach debe vigilar, desde tu contexto personal.", + "cardDescription": "Enfermedades crónicas y aquello que el Coach debe vigilar.", "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." + "loadError": "No se pudieron cargar tus enfermedades.", + "savedToast": "Enfermedades guardadas.", + "saveError": "No se pudo guardar. Inténtalo de nuevo." } }, "environment": { diff --git a/messages/fr.json b/messages/fr.json index f0dbd75a7..fa4add4df 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -7828,12 +7828,12 @@ }, "conditions": { "cardTitle": "Affections préexistantes", - "cardDescription": "Affections chroniques et ce que le Coach doit surveiller, d'après votre contexte personnel.", + "cardDescription": "Affections chroniques et ce que le Coach doit surveiller.", "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." + "loadError": "Impossible de charger vos antécédents.", + "savedToast": "Affections enregistrées.", + "saveError": "Impossible d'enregistrer. Veuillez réessayer." } }, "environment": { diff --git a/messages/it.json b/messages/it.json index 4785ce709..a888be6cb 100644 --- a/messages/it.json +++ b/messages/it.json @@ -7828,12 +7828,12 @@ }, "conditions": { "cardTitle": "Patologie preesistenti", - "cardDescription": "Patologie croniche e ciò che il Coach deve tenere d'occhio, dal tuo contesto personale.", + "cardDescription": "Patologie croniche e ciò che il Coach deve tenere d'occhio.", "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." + "loadError": "Impossibile caricare le tue patologie.", + "savedToast": "Patologie salvate.", + "saveError": "Impossibile salvare. Riprova." } }, "environment": { diff --git a/messages/pl.json b/messages/pl.json index 4b6b91788..be44a8a3c 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -7828,12 +7828,12 @@ }, "conditions": { "cardTitle": "Choroby przewlekłe", - "cardDescription": "Choroby przewlekłe i to, na co Coach ma zwracać uwagę — z Twojego kontekstu osobistego.", + "cardDescription": "Choroby przewlekłe i to, na co Coach ma zwracać uwagę.", "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." + "loadError": "Nie udało się wczytać Twoich chorób.", + "savedToast": "Choroby zapisane.", + "saveError": "Nie udało się zapisać. Spróbuj ponownie." } }, "environment": { diff --git a/src/components/records/conditions-manager.tsx b/src/components/records/conditions-manager.tsx new file mode 100644 index 000000000..686a5dfa1 --- /dev/null +++ b/src/components/records/conditions-manager.tsx @@ -0,0 +1,166 @@ +"use client"; + +/** + * v1.25.12 — pre-existing / chronic conditions editor, at home in the Anamnese + * (medical-history) section. + * + * The chronic-conditions note and the explicit "what should the Coach watch" + * focus are foundational medical history, so they are edited here next to + * allergies + family history — one coherent medical-history home. They used to + * be edited under Settings → Profile → "About me"; the editing moved here so the + * conditions the Coach watches sit with the rest of the medical record. + * + * Data path is unchanged: both fields are part of the self-context payload + * (`GET`/`PUT /api/coach/about-me`) and are read/written through the same + * `queryKeys.coachAboutMe()` query- and mutation-key the rest of the app uses, + * so every consumer (the Coach above all) keeps reading the same store. The PUT + * echoes the current `aboutMe` free text (a required field that clears on empty) + * and omits `allergies` (optional, preserved server-side) so saving conditions + * never disturbs the other self-context fields. + * + * Plain text only — values render exclusively through inputs and React text + * children; no markdown renderer (XSS posture, see contributor notes). + */ +import { useState } from "react"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { Loader2 } from "lucide-react"; +import { toast } from "sonner"; + +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { useTranslations } from "@/lib/i18n/context"; +import { queryKeys } from "@/lib/query-keys"; +import { cn } from "@/lib/utils"; +import { apiGet, apiPut } from "@/lib/api/api-fetch"; + +const FALLBACK_FIELD_MAX_CHARS = 500; + +interface AboutMeData { + aboutMe: string | null; + conditions: string | null; + allergies: string | null; + coachFocus: string | null; + fieldMaxChars: number; +} + +const FIELD_CLASSES = + "border-input bg-background placeholder:text-muted-foreground focus-visible:ring-ring w-full rounded-md border px-3 py-2 text-sm focus-visible:ring-1 focus-visible:outline-none"; + +export function ConditionsManager() { + const { t } = useTranslations(); + const queryClient = useQueryClient(); + const [drafts, setDrafts] = useState<{ + conditions: string | null; + coachFocus: string | null; + }>({ conditions: null, coachFocus: null }); + + const query = useQuery({ + queryKey: queryKeys.coachAboutMe(), + queryFn: () => apiGet("/api/coach/about-me"), + }); + + const saved = { + conditions: query.data?.conditions ?? "", + coachFocus: query.data?.coachFocus ?? "", + }; + const fieldMaxChars = query.data?.fieldMaxChars ?? FALLBACK_FIELD_MAX_CHARS; + + const value = { + conditions: drafts.conditions ?? saved.conditions, + coachFocus: drafts.coachFocus ?? saved.coachFocus, + }; + const dirty = + value.conditions !== saved.conditions || + value.coachFocus !== saved.coachFocus; + + const save = useMutation({ + mutationKey: queryKeys.coachAboutMe(), + mutationFn: async (input: { conditions: string; coachFocus: string }) => { + // Echo the current free-text `aboutMe` (required; empty clears it) and + // omit `allergies` (optional, preserved server-side) so this write only + // touches the two conditions fields. + return apiPut("/api/coach/about-me", { + aboutMe: query.data?.aboutMe ?? "", + conditions: input.conditions, + coachFocus: input.coachFocus, + }); + }, + onSuccess: () => { + toast.success(t("records.conditions.savedToast")); + setDrafts({ conditions: null, coachFocus: null }); + queryClient.invalidateQueries({ queryKey: queryKeys.coachAboutMe() }); + queryClient.invalidateQueries({ + queryKey: queryKeys.coachAboutMeQuestions(), + }); + }, + onError: () => { + toast.error(t("records.conditions.saveError")); + }, + }); + + const disabled = query.isLoading || save.isPending; + + function field( + key: "conditions" | "coachFocus", + label: string, + placeholder: string, + ) { + return ( +
+ +