diff --git a/messages/de.json b/messages/de.json index 01784ec8b..3f3db97aa 100644 --- a/messages/de.json +++ b/messages/de.json @@ -3648,7 +3648,7 @@ }, "goals": { "title": "Was möchtest du erfassen?", - "body": "Wähle die Bereiche, die dir wichtig sind – das hilft dir zu entscheiden, was du verfolgen und als Nächstes einrichten willst.", + "body": "Wähle die Bereiche, die dir wichtig sind – wir zeigen sie zuerst auf deinem Dashboard an, und sie helfen dir zu entscheiden, was du als Nächstes einrichtest.", "helpHint": "Ein bis drei Schwerpunkte funktionieren erfahrungsgemäß am besten – mehr geht aber jederzeit.", "options": { "weightManagement": "Gewicht im Griff", @@ -3733,6 +3733,17 @@ "title": "Erkenne das Muster", "body": "Tageszusammenfassungen, Trend-Diagramme und ein Coach, der zeigt was sich verändert hat — alles auf deinem Gerät." } + }, + "anamnesis": { + "title": "Zu deiner Gesundheit (optional)", + "subtitle": "Etwas Kontext, damit HealthLog deine Werte fairer einordnet.", + "intro": "Teile so viel oder so wenig, wie du möchtest – jedes Feld ist optional und jederzeit in den Einstellungen änderbar.", + "conditionsLabel": "Bestehende Erkrankungen", + "conditionsPlaceholder": "z. B. Bluthochdruck, Typ-2-Diabetes", + "conditionsHint": "Hilft HealthLog, deine Werte im Kontext einzuordnen, statt Erwartbares zu melden.", + "allergiesLabel": "Allergien oder Unverträglichkeiten", + "allergiesPlaceholder": "z. B. Penicillin, Laktose", + "privacyNote": "Verschlüsselt gespeichert und nur für besseren Kontext genutzt. Medikamente fragen wir hier nicht ab – die kannst du jederzeit auf der Medikamenten-Seite ergänzen." } }, "gettingStarted": { diff --git a/messages/en.json b/messages/en.json index a78cc1a24..67925684e 100644 --- a/messages/en.json +++ b/messages/en.json @@ -3648,7 +3648,7 @@ }, "goals": { "title": "What would you like to track?", - "body": "Pick the areas you care about — it helps you decide what to track and set up next.", + "body": "Pick the areas you care about — we'll surface them first on your dashboard, and they help you decide what to set up next.", "helpHint": "One to three priorities tend to work best — you can always add more later.", "options": { "weightManagement": "Weight management", @@ -3733,6 +3733,17 @@ "title": "See the pattern", "body": "Daily summaries, trend charts, and a coach that surfaces what changed — all on your own device." } + }, + "anamnesis": { + "title": "About your health (optional)", + "subtitle": "A little context so HealthLog reads your numbers more fairly.", + "intro": "Share as much or as little as you like — every field is optional and you can edit it anytime in Settings.", + "conditionsLabel": "Ongoing conditions", + "conditionsPlaceholder": "e.g. high blood pressure, type 2 diabetes", + "conditionsHint": "Helps HealthLog frame your readings in context rather than flagging the expected.", + "allergiesLabel": "Allergies or intolerances", + "allergiesPlaceholder": "e.g. penicillin, lactose", + "privacyNote": "Stored encrypted and used only to give you better context. We don't ask for your medications here — add those anytime from the Medications page." } }, "gettingStarted": { diff --git a/messages/es.json b/messages/es.json index 07ef722cb..2a34af1a6 100644 --- a/messages/es.json +++ b/messages/es.json @@ -3648,7 +3648,7 @@ }, "goals": { "title": "¿Qué te gustaría registrar?", - "body": "Elige las áreas que te interesan: te ayuda a decidir qué seguir y qué configurar a continuación.", + "body": "Elige las áreas que te interesan: las mostraremos primero en tu panel y te ayudarán a decidir qué configurar a continuación.", "helpHint": "Una a tres prioridades suelen funcionar mejor — siempre podrás añadir más más adelante.", "options": { "weightManagement": "Control del peso", @@ -3733,6 +3733,17 @@ "title": "Descubre los patrones", "body": "Resúmenes diarios, gráficas de tendencias y un coach que destaca lo que cambió — todo en tu propio dispositivo." } + }, + "anamnesis": { + "title": "Sobre tu salud (opcional)", + "subtitle": "Un poco de contexto para que HealthLog interprete tus valores de forma más justa.", + "intro": "Comparte lo que quieras: cada campo es opcional y puedes editarlo cuando quieras en Ajustes.", + "conditionsLabel": "Afecciones actuales", + "conditionsPlaceholder": "p. ej. hipertensión, diabetes tipo 2", + "conditionsHint": "Ayuda a HealthLog a interpretar tus lecturas en contexto en lugar de marcar lo esperado.", + "allergiesLabel": "Alergias o intolerancias", + "allergiesPlaceholder": "p. ej. penicilina, lactosa", + "privacyNote": "Se almacena cifrado y solo se usa para darte mejor contexto. Aquí no te pedimos tu medicación: puedes añadirla cuando quieras desde la página de Medicación." } }, "gettingStarted": { diff --git a/messages/fr.json b/messages/fr.json index acec48c77..62b8a5f10 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -3648,7 +3648,7 @@ }, "goals": { "title": "Que souhaitez-vous suivre ?", - "body": "Sélectionnez les domaines qui comptent pour vous : cela vous aide à décider quoi suivre et configurer ensuite.", + "body": "Sélectionnez les domaines qui comptent pour vous : nous les afficherons en premier sur votre tableau de bord, et ils vous aident à décider quoi configurer ensuite.", "helpHint": "Un à trois axes prioritaires fonctionnent généralement le mieux, mais vous pourrez en ajouter plus tard.", "options": { "weightManagement": "Gestion du poids", @@ -3733,6 +3733,17 @@ "title": "Repérez les tendances", "body": "Résumés quotidiens, graphiques de tendances et un coach qui met en avant ce qui a changé — le tout sur votre appareil." } + }, + "anamnesis": { + "title": "À propos de votre santé (facultatif)", + "subtitle": "Un peu de contexte pour que HealthLog interprète vos valeurs plus justement.", + "intro": "Partagez autant ou aussi peu que vous le souhaitez : chaque champ est facultatif et modifiable à tout moment dans les Réglages.", + "conditionsLabel": "Affections en cours", + "conditionsPlaceholder": "p. ex. hypertension, diabète de type 2", + "conditionsHint": "Aide HealthLog à replacer vos mesures dans leur contexte plutôt que de signaler l'attendu.", + "allergiesLabel": "Allergies ou intolérances", + "allergiesPlaceholder": "p. ex. pénicilline, lactose", + "privacyNote": "Stocké chiffré et utilisé uniquement pour un meilleur contexte. Nous ne demandons pas vos médicaments ici — ajoutez-les quand vous voulez depuis la page Médicaments." } }, "gettingStarted": { diff --git a/messages/it.json b/messages/it.json index e825a1770..b65f81c21 100644 --- a/messages/it.json +++ b/messages/it.json @@ -3648,7 +3648,7 @@ }, "goals": { "title": "Cosa vuoi monitorare?", - "body": "Scegli gli ambiti che ti interessano: ti aiuta a decidere cosa monitorare e configurare in seguito.", + "body": "Scegli gli ambiti che ti interessano: li mostreremo per primi nella tua dashboard e ti aiutano a decidere cosa configurare in seguito.", "helpHint": "Da una a tre priorità funzionano di solito meglio — potrai aggiungerne altre in qualsiasi momento.", "options": { "weightManagement": "Gestione del peso", @@ -3733,6 +3733,17 @@ "title": "Scopri lo schema", "body": "Riepiloghi giornalieri, grafici di tendenza e un coach che evidenzia i cambiamenti — tutto sul tuo dispositivo." } + }, + "anamnesis": { + "title": "Sulla tua salute (facoltativo)", + "subtitle": "Un po' di contesto perché HealthLog interpreti i tuoi valori in modo più equo.", + "intro": "Condividi quanto vuoi: ogni campo è facoltativo e modificabile in qualsiasi momento nelle Impostazioni.", + "conditionsLabel": "Condizioni in corso", + "conditionsPlaceholder": "es. ipertensione, diabete di tipo 2", + "conditionsHint": "Aiuta HealthLog a inquadrare le tue misurazioni nel contesto invece di segnalare l'atteso.", + "allergiesLabel": "Allergie o intolleranze", + "allergiesPlaceholder": "es. penicillina, lattosio", + "privacyNote": "Memorizzato cifrato e usato solo per darti un contesto migliore. Qui non chiediamo i tuoi farmaci: aggiungili quando vuoi dalla pagina Farmaci." } }, "gettingStarted": { diff --git a/messages/pl.json b/messages/pl.json index b551d7b0b..22ee2065f 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -3648,7 +3648,7 @@ }, "goals": { "title": "Co chcesz śledzić?", - "body": "Wybierz obszary, które są dla Ciebie ważne — to pomaga zdecydować, co śledzić i co skonfigurować w następnej kolejności.", + "body": "Wybierz obszary, które są dla Ciebie ważne — pokażemy je najpierw na pulpicie i pomogą Ci zdecydować, co skonfigurować dalej.", "helpHint": "Od jednego do trzech priorytetów zwykle sprawdza się najlepiej — możesz dodać kolejne w dowolnej chwili.", "options": { "weightManagement": "Kontrola wagi", @@ -3733,6 +3733,17 @@ "title": "Zobacz wzorce", "body": "Codzienne podsumowania, wykresy trendów i coach, który wskazuje co się zmieniło — wszystko na Twoim urządzeniu." } + }, + "anamnesis": { + "title": "O Twoim zdrowiu (opcjonalnie)", + "subtitle": "Trochę kontekstu, aby HealthLog rzetelniej odczytywał Twoje wyniki.", + "intro": "Podziel się tym, czym chcesz — każde pole jest opcjonalne i możesz je zmienić w dowolnej chwili w Ustawieniach.", + "conditionsLabel": "Aktualne schorzenia", + "conditionsPlaceholder": "np. nadciśnienie, cukrzyca typu 2", + "conditionsHint": "Pomaga HealthLog ujmować Twoje pomiary w kontekście, zamiast oznaczać to, czego należy się spodziewać.", + "allergiesLabel": "Alergie lub nietolerancje", + "allergiesPlaceholder": "np. penicylina, laktoza", + "privacyNote": "Przechowywane w postaci zaszyfrowanej i używane tylko po to, by dać Ci lepszy kontekst. Nie pytamy tu o leki — dodasz je w dowolnej chwili na stronie Leki." } }, "gettingStarted": { diff --git a/src/components/onboarding/AnamnesisCard.tsx b/src/components/onboarding/AnamnesisCard.tsx new file mode 100644 index 000000000..723d6f98e --- /dev/null +++ b/src/components/onboarding/AnamnesisCard.tsx @@ -0,0 +1,155 @@ +"use client"; + +import { useId, useState } from "react"; +import { ChevronDown, HeartHandshake } from "lucide-react"; + +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { useTranslations } from "@/lib/i18n/context"; +import { cn } from "@/lib/utils"; + +/** + * v1.17.1 — optional "About your health" card on the onboarding + * Baseline step (3). + * + * Captures the genuinely-new baseline the app can act on — chronic + * conditions and allergies / intolerances — that the height/sex/dob + * fields above do NOT already cover. Both write through the existing + * encrypted self-context path (`PUT /api/coach/about-me`), so there is + * no new model and no new contract; the parent BaselineForm preserves + * any existing free-text `aboutMe` on save. + * + * Collapsed by default with a prominent skip framing — the step stays + * übersichtlich and every field is optional and editable later in + * Settings → AI. Warm, medically-grounded copy: framed as "so + * HealthLog reads your numbers in context," never as a clinical form. + * + * Controlled: the parent owns the values so it can persist them in the + * same submit as the profile fields. + */ +export interface AnamnesisValue { + conditions: string; + allergies: string; +} + +/** + * Build the `PUT /api/coach/about-me` body for the anamnesis answers, + * or `null` when the card was left untouched (so an untouched collapsed + * card never round-trips). Preserves the existing free-text `aboutMe` + * (the PUT schema requires it and an empty value clears it) and only + * includes conditions / allergies when the user typed something — so an + * unrelated stored value is never cleared. Field-by-field assembly: no + * mass-spread of the value object into the body. + */ +export function buildAnamnesisAboutMeBody( + baseAboutMe: string, + value: AnamnesisValue, +): Record | null { + const conditions = value.conditions.trim(); + const allergies = value.allergies.trim(); + if (!conditions && !allergies) return null; + const body: Record = { aboutMe: baseAboutMe }; + if (conditions) body.conditions = conditions; + if (allergies) body.allergies = allergies; + return body; +} + +export interface AnamnesisCardProps { + value: AnamnesisValue; + onChange: (next: AnamnesisValue) => void; + disabled?: boolean; +} + +/** Per-field cap mirrors ABOUT_ME_FIELD_MAX_CHARS on the server. */ +const FIELD_MAX = 500; + +export function AnamnesisCard({ value, onChange, disabled }: AnamnesisCardProps) { + const { t } = useTranslations(); + const [expanded, setExpanded] = useState(false); + const conditionsId = useId(); + const allergiesId = useId(); + const panelId = useId(); + + return ( +
+ + + {expanded ? ( +
+

+ {t("onboarding.anamnesis.intro")} +

+ +
+ +