mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
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.
This commit is contained in:
1 parent
5bfba0ba60
commit
d73656685a
10 files changed
+233
-204
No files matched your search
+4
-4
@@ -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": {
|
||||
|
||||
+4
-4
@@ -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": {
|
||||
|
||||
+4
-4
@@ -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": {
|
||||
|
||||
+4
-4
@@ -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": {
|
||||
|
||||
+4
-4
@@ -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": {
|
||||
|
||||
+4
-4
@@ -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": {
|
||||
|
||||
@@ -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<AboutMeData>("/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<AboutMeData>("/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 (
|
||||
<div className="space-y-1.5">
|
||||
<Label htmlFor={`records-conditions-${key}`}>{label}</Label>
|
||||
<textarea
|
||||
id={`records-conditions-${key}`}
|
||||
data-testid={`records-conditions-${key}`}
|
||||
className={cn(FIELD_CLASSES, "min-h-16 resize-y")}
|
||||
rows={2}
|
||||
value={value[key]}
|
||||
maxLength={fieldMaxChars}
|
||||
placeholder={placeholder}
|
||||
disabled={disabled}
|
||||
onChange={(e) => setDrafts((d) => ({ ...d, [key]: e.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4" data-slot="conditions-manager">
|
||||
{query.isError ? (
|
||||
<p role="alert" className="text-destructive text-sm">
|
||||
{t("records.conditions.loadError")}
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
{field(
|
||||
"conditions",
|
||||
t("records.conditions.conditionsLabel"),
|
||||
t("settings.ai.aboutMe.conditionsPlaceholder"),
|
||||
)}
|
||||
{field(
|
||||
"coachFocus",
|
||||
t("records.conditions.focusLabel"),
|
||||
t("settings.ai.aboutMe.focusPlaceholder"),
|
||||
)}
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
className="min-h-11 sm:min-h-9"
|
||||
data-testid="records-conditions-save"
|
||||
disabled={disabled || !dirty}
|
||||
onClick={() => save.mutate(value)}
|
||||
>
|
||||
{save.isPending && (
|
||||
<Loader2
|
||||
className="size-4 animate-spin motion-reduce:animate-none"
|
||||
aria-hidden
|
||||
/>
|
||||
)}
|
||||
{t("settings.ai.aboutMe.save")}
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
"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<SelfContext>("/api/coach/about-me"),
|
||||
});
|
||||
|
||||
const conditions = (data?.conditions ?? "").trim();
|
||||
const coachFocus = (data?.coachFocus ?? "").trim();
|
||||
const hasAny = conditions.length > 0 || coachFocus.length > 0;
|
||||
|
||||
return (
|
||||
<div className="space-y-4" data-slot="conditions-overview">
|
||||
{isLoading && (
|
||||
<Loader2 className="text-muted-foreground h-5 w-5 animate-spin motion-reduce:animate-none" />
|
||||
)}
|
||||
|
||||
{isError && (
|
||||
<p role="alert" className="text-destructive text-sm">
|
||||
{t("records.conditions.loadError")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{!isLoading && !isError && (
|
||||
<>
|
||||
{hasAny ? (
|
||||
<dl className="space-y-3">
|
||||
{conditions.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
<dt className="text-sm font-medium">
|
||||
{t("records.conditions.conditionsLabel")}
|
||||
</dt>
|
||||
<dd className="text-muted-foreground text-sm whitespace-pre-wrap">
|
||||
{conditions}
|
||||
</dd>
|
||||
</div>
|
||||
)}
|
||||
{coachFocus.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
<dt className="text-sm font-medium">
|
||||
{t("records.conditions.focusLabel")}
|
||||
</dt>
|
||||
<dd className="text-muted-foreground text-sm whitespace-pre-wrap">
|
||||
{coachFocus}
|
||||
</dd>
|
||||
</div>
|
||||
)}
|
||||
</dl>
|
||||
) : (
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("records.conditions.empty")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<p className="text-xs">
|
||||
<Link
|
||||
href="/settings/account"
|
||||
className="text-dracula-purple underline-offset-4 hover:underline"
|
||||
>
|
||||
{t("records.conditions.editCta")}
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -3,14 +3,21 @@
|
||||
/**
|
||||
* v1.15.20 — Settings → AI "About me" panel (free text only).
|
||||
* v1.16.0 — structured self-context: alongside the free text the user
|
||||
* answers three short questions (chronic conditions, allergies /
|
||||
* intolerances, what the Coach should watch). All four fields are
|
||||
* encrypted at rest; age/gender stay on the profile and are merged
|
||||
* into the prompt server-side — the panel says so instead of asking
|
||||
* again.
|
||||
* notes allergies / intolerances. All fields are encrypted at rest;
|
||||
* age/gender stay on the profile and are merged into the prompt
|
||||
* server-side — the panel says so instead of asking again.
|
||||
*
|
||||
* v1.25.12 — the pre-existing / chronic conditions and the "what should the
|
||||
* Coach watch" focus moved out of this panel into Settings → Anamnese, where
|
||||
* they sit with allergies + family history as one medical history. Editing them
|
||||
* has a single home there now; this panel keeps the free-text note and the
|
||||
* allergies line. The store is unchanged — every field is still part of the
|
||||
* `/api/coach/about-me` payload; the PUT here simply omits the two conditions
|
||||
* fields, which are preserved server-side.
|
||||
*
|
||||
* GET /api/coach/about-me — full structured payload + pending questions
|
||||
* PUT /api/coach/about-me — writes all four fields (empty string clears)
|
||||
* PUT /api/coach/about-me — writes aboutMe (empty string clears) + allergies;
|
||||
* conditions / coachFocus are omitted here and left untouched
|
||||
*
|
||||
* After a save the server derives up to 3 clarifying questions (AI
|
||||
* when a provider + budget allow, deterministic hints otherwise); the
|
||||
@@ -54,7 +61,7 @@ interface AboutMeData {
|
||||
fieldMaxChars: number;
|
||||
}
|
||||
|
||||
type StructuredKey = "conditions" | "allergies" | "coachFocus";
|
||||
type StructuredKey = "allergies";
|
||||
|
||||
async function fetchAboutMe(): Promise<AboutMeData> {
|
||||
return apiGet<AboutMeData>("/api/coach/about-me");
|
||||
@@ -72,10 +79,8 @@ export function AboutMeSection({
|
||||
const queryClient = useQueryClient();
|
||||
const [drafts, setDrafts] = useState<{
|
||||
aboutMe: string | null;
|
||||
conditions: string | null;
|
||||
allergies: string | null;
|
||||
coachFocus: string | null;
|
||||
}>({ aboutMe: null, conditions: null, allergies: null, coachFocus: null });
|
||||
}>({ aboutMe: null, allergies: null });
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: queryKeys.coachAboutMe(),
|
||||
@@ -85,9 +90,7 @@ export function AboutMeSection({
|
||||
|
||||
const saved = {
|
||||
aboutMe: query.data?.aboutMe ?? "",
|
||||
conditions: query.data?.conditions ?? "",
|
||||
allergies: query.data?.allergies ?? "",
|
||||
coachFocus: query.data?.coachFocus ?? "",
|
||||
};
|
||||
const maxChars = query.data?.maxChars ?? FALLBACK_MAX_CHARS;
|
||||
const fieldMaxChars = query.data?.fieldMaxChars ?? FALLBACK_FIELD_MAX_CHARS;
|
||||
@@ -95,57 +98,33 @@ export function AboutMeSection({
|
||||
|
||||
const value = {
|
||||
aboutMe: drafts.aboutMe ?? saved.aboutMe,
|
||||
conditions: drafts.conditions ?? saved.conditions,
|
||||
allergies: drafts.allergies ?? saved.allergies,
|
||||
coachFocus: drafts.coachFocus ?? saved.coachFocus,
|
||||
};
|
||||
const dirty =
|
||||
value.aboutMe !== saved.aboutMe ||
|
||||
value.conditions !== saved.conditions ||
|
||||
value.allergies !== saved.allergies ||
|
||||
value.coachFocus !== saved.coachFocus;
|
||||
const hasAnyContent =
|
||||
saved.aboutMe.length > 0 ||
|
||||
saved.conditions.length > 0 ||
|
||||
saved.allergies.length > 0 ||
|
||||
saved.coachFocus.length > 0;
|
||||
value.aboutMe !== saved.aboutMe || value.allergies !== saved.allergies;
|
||||
const hasAnyContent = saved.aboutMe.length > 0 || saved.allergies.length > 0;
|
||||
|
||||
// Quiet completeness meter: one segment per answered field. Reads
|
||||
// the SAVED state (not the draft) so it only moves on persistence.
|
||||
const answered = [
|
||||
saved.conditions,
|
||||
saved.allergies,
|
||||
saved.coachFocus,
|
||||
saved.aboutMe,
|
||||
].filter((v) => v.length > 0).length;
|
||||
const answered = [saved.allergies, saved.aboutMe].filter(
|
||||
(v) => v.length > 0,
|
||||
).length;
|
||||
|
||||
const save = useMutation({
|
||||
mutationKey: queryKeys.coachAboutMe(),
|
||||
mutationFn: async (input: {
|
||||
aboutMe: string;
|
||||
conditions: string;
|
||||
allergies: string;
|
||||
coachFocus: string;
|
||||
}) => {
|
||||
// Send only the two fields this panel owns. `conditions` / `coachFocus`
|
||||
// are omitted so the server preserves them (they are edited under Anamnese).
|
||||
mutationFn: async (input: { aboutMe: string; allergies: string }) => {
|
||||
return apiPut<AboutMeData>("/api/coach/about-me", input);
|
||||
},
|
||||
onSuccess: (next) => {
|
||||
const cleared =
|
||||
!next.aboutMe &&
|
||||
!next.conditions &&
|
||||
!next.allergies &&
|
||||
!next.coachFocus;
|
||||
const cleared = !next.aboutMe && !next.allergies;
|
||||
toast.success(
|
||||
cleared
|
||||
? t("settings.ai.aboutMe.clearedToast")
|
||||
: t("settings.ai.aboutMe.savedToast"),
|
||||
);
|
||||
setDrafts({
|
||||
aboutMe: null,
|
||||
conditions: null,
|
||||
allergies: null,
|
||||
coachFocus: null,
|
||||
});
|
||||
setDrafts({ aboutMe: null, allergies: null });
|
||||
queryClient.invalidateQueries({ queryKey: queryKeys.coachAboutMe() });
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.coachAboutMeQuestions(),
|
||||
@@ -195,19 +174,19 @@ export function AboutMeSection({
|
||||
titleId="settings-ai-about-me-title"
|
||||
description={t("settings.ai.aboutMe.description")}
|
||||
/>
|
||||
{/* Completeness — four quiet segments, no percentage shouting. */}
|
||||
{/* Completeness — two quiet segments, no percentage shouting. */}
|
||||
<div
|
||||
className="flex items-center gap-1.5"
|
||||
aria-label={t("settings.ai.aboutMe.completeness", {
|
||||
answered,
|
||||
total: 4,
|
||||
total: 2,
|
||||
})}
|
||||
title={t("settings.ai.aboutMe.completeness", {
|
||||
answered,
|
||||
total: 4,
|
||||
total: 2,
|
||||
})}
|
||||
>
|
||||
{[0, 1, 2, 3].map((i) => (
|
||||
{[0, 1].map((i) => (
|
||||
<span
|
||||
key={i}
|
||||
aria-hidden="true"
|
||||
@@ -220,24 +199,12 @@ export function AboutMeSection({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-4 pl-7 sm:grid-cols-2">
|
||||
{structuredField(
|
||||
"conditions",
|
||||
t("settings.ai.aboutMe.conditionsLabel"),
|
||||
t("settings.ai.aboutMe.conditionsPlaceholder"),
|
||||
)}
|
||||
<div className="pl-7">
|
||||
{structuredField(
|
||||
"allergies",
|
||||
t("settings.ai.aboutMe.allergiesLabel"),
|
||||
t("settings.ai.aboutMe.allergiesPlaceholder"),
|
||||
)}
|
||||
<div className="sm:col-span-2">
|
||||
{structuredField(
|
||||
"coachFocus",
|
||||
t("settings.ai.aboutMe.focusLabel"),
|
||||
t("settings.ai.aboutMe.focusPlaceholder"),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-1.5 pl-7">
|
||||
@@ -277,14 +244,7 @@ export function AboutMeSection({
|
||||
className="min-h-11 sm:min-h-9"
|
||||
data-testid="settings-about-me-clear"
|
||||
disabled={!isAuthenticated || save.isPending}
|
||||
onClick={() =>
|
||||
save.mutate({
|
||||
aboutMe: "",
|
||||
conditions: "",
|
||||
allergies: "",
|
||||
coachFocus: "",
|
||||
})
|
||||
}
|
||||
onClick={() => save.mutate({ aboutMe: "", allergies: "" })}
|
||||
>
|
||||
<Trash2 className="size-4" aria-hidden />
|
||||
{t("settings.ai.aboutMe.clear")}
|
||||
@@ -351,9 +311,9 @@ export function AboutMeSection({
|
||||
{t("settings.ai.aboutMe.hint")} {t("settings.ai.aboutMe.profileHint")}
|
||||
</p>
|
||||
|
||||
{/* 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. */}
|
||||
{/* v1.25.12 — cross-link to the Anamnese (medical-history) home, where the
|
||||
chronic conditions the Coach watches now live alongside allergies +
|
||||
family history as one coherent medical history. */}
|
||||
<p className="text-muted-foreground pl-7 text-xs">
|
||||
{t("settings.ai.aboutMe.recordsLink")}{" "}
|
||||
<Link
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
* 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.
|
||||
* v1.25.12 — the section is the single home for the pre-existing / chronic
|
||||
* conditions the Coach watches, edited inline here so conditions + allergies +
|
||||
* family history read (and write) as one coherent medical history. The
|
||||
* conditions card is coach-gated (the data only feeds the Coach); it reads and
|
||||
* writes the same self-context store (`/api/coach/about-me`) the rest of the app
|
||||
* uses — the placement simply moved out of personal context into the medical
|
||||
* record where it belongs.
|
||||
*/
|
||||
|
||||
import {
|
||||
@@ -28,7 +29,7 @@ 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 { ConditionsManager } from "@/components/records/conditions-manager";
|
||||
import { FamilyHistoryManager } from "@/components/records/family-history-manager";
|
||||
|
||||
export function AnamnesisSection() {
|
||||
@@ -45,7 +46,7 @@ export function AnamnesisSection() {
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ConditionsOverview />
|
||||
<ConditionsManager />
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
Reference in new issue
Block a user