diff --git a/messages/de.json b/messages/de.json index 23c44b9ca..8db0b64a6 100644 --- a/messages/de.json +++ b/messages/de.json @@ -599,7 +599,9 @@ "typeWaistCircumference": "Taillenumfang", "typeWaistToHeight": "Taille-Größe-Verhältnis", "unitScore": "Pkt.", - "unitRatio": "Verhältnis" + "unitRatio": "Verhältnis", + "typeWho5Score": "WHO-5-Wert", + "typeSciScore": "SCI-Wert" }, "mood": { "title": "Stimmung", @@ -7639,8 +7641,8 @@ }, "mentalHealth": { "pageTitle": "Check-in zum seelischen Wohlbefinden", - "pageDescription": "Zwei validierte, freiwillige Selbsttests — PHQ-9 (Stimmung) und GAD-7 (Sorgen) — ergänzend zu deinem Stimmungstagebuch. Aussagekräftig ist der Verlauf über die Zeit, nicht ein einzelner Wert.", - "pageRhythmHint": "Beide Tests blicken auf die letzten zwei Wochen zurück — ein Check-in alle zwei bis vier Wochen ist deshalb ein guter Rhythmus.", + "pageDescription": "Vier validierte, freiwillige Selbsttests — PHQ-9 (Stimmung), GAD-7 (Sorgen), WHO-5 (Wohlbefinden) und SCI (Schlaf) — ergänzend zu deinem Stimmungstagebuch. Aussagekräftig ist der Verlauf über die Zeit, nicht ein einzelner Wert.", + "pageRhythmHint": "PHQ-9, GAD-7 und der Wohlbefindens-Check blicken auf die letzten zwei Wochen zurück, der Schlaf-Check auf den letzten Monat — ein Check-in alle zwei bis vier Wochen ist ein guter Rhythmus.", "attributionLabel": "Quelle", "choosePrompt": "Check-in auswählen", "start": "Starten", @@ -7649,14 +7651,17 @@ "submitting": "Wird gespeichert…", "saved": "Gespeichert", "error": "Etwas ist schiefgelaufen. Bitte versuche es erneut.", - "considerProfessional": "Dein Wert liegt in einem Bereich, in dem ein Gespräch mit einer Fachperson helfen kann. Das ist ein behutsamer Hinweis, keine Diagnose.", "instrument": { "phq9": "PHQ-9 · Stimmung", - "gad7": "GAD-7 · Sorgen" + "gad7": "GAD-7 · Sorgen", + "who5": "WHO-5 · Wohlbefinden", + "sci": "SCI · Schlaf" }, "instrumentSub": { "phq9": "Depressions-Screening, 9 Fragen", - "gad7": "Angst-Screening, 7 Fragen" + "gad7": "Angst-Screening, 7 Fragen", + "who5": "Wohlbefindens-Check, 5 Fragen", + "sci": "Schlaf-Check, 8 Fragen" }, "options": { "0": "Überhaupt nicht", @@ -7691,16 +7696,23 @@ "mild": "Leicht", "moderate": "Mittelgradig", "severe": "Schwer" + }, + "WHO5": { + "low": "Niedrigeres Wohlbefinden", + "good": "Gutes Wohlbefinden" + }, + "SCI": { + "belowThreshold": "Möglicher Insomnie-Bereich", + "aboveThreshold": "Kein Insomnie-Hinweis" } }, "history": { "title": "Dein Verlauf", "empty": "Noch keine Check-ins. Dein Verlauf erscheint hier, sobald du einen abgeschlossen hast.", "flaggedBadge": "Hilfe angezeigt", - "tab": { - "phq9": "PHQ-9", - "gad7": "GAD-7" - } + "chartTitle": "Punkteverlauf", + "totalLabel": "Punkte", + "bandLabel": "Einstufung" }, "crisis": { "title": "Du musst das nicht allein durchstehen", @@ -7728,6 +7740,23 @@ "5": "Rastlosigkeit, sodass Stillsitzen schwerfällt", "6": "Schnelle Verärgerung oder Gereiztheit", "7": "Gefühl der Angst, als würde etwas Schlimmes passieren" + }, + "who5": { + "1": "… war ich froh und guter Laune", + "2": "… habe ich mich ruhig und entspannt gefühlt", + "3": "… habe ich mich energisch und aktiv gefühlt", + "4": "… habe ich mich beim Aufwachen frisch und ausgeruht gefühlt", + "5": "… war mein Alltag voller Dinge, die mich interessieren" + }, + "sci": { + "1": "… how long does it take you to fall asleep?", + "2": "… if you then wake up during the night … how long are you awake for in total? (add all the wakenings up)", + "3": "… how many nights a week do you have a problem with your sleep?", + "4": "… how would you rate your sleep quality?", + "5": "… affected your mood, energy, or relationships?", + "6": "… affected your concentration, productivity, or ability to stay awake?", + "7": "… troubled you in general?", + "8": "… how long have you had a problem with your sleep?" } }, "crisisResource": { @@ -7755,14 +7784,79 @@ }, "instrumentDescription": { "phq9": "Depressions-Screening mit 9 Fragen. Beziehe dich auf die letzten zwei Wochen.", - "gad7": "Angst-Screening mit 7 Fragen. Beziehe dich auf die letzten zwei Wochen." + "gad7": "Angst-Screening mit 7 Fragen. Beziehe dich auf die letzten zwei Wochen.", + "who5": "Wohlbefindens-Check mit 5 Fragen. Beziehe dich auf die letzten zwei Wochen; höhere Werte bedeuten besseres Wohlbefinden.", + "sci": "Schlaf-Check mit 8 Fragen. Die meisten Fragen beziehen sich auf den letzten Monat; höhere Werte bedeuten besseren Schlaf." }, "progress": "{current} / {total}", "next": "Weiter", "lastResult": "Letzter Test", "lastScore": "Letztes Ergebnis", "noResultYet": "Noch kein Check-in", - "backToList": "Übersicht" + "backToList": "Übersicht", + "followUpHint": { + "phq9": "Dein Wert liegt in einem Bereich, in dem ein Gespräch mit einer Fachperson helfen kann. Das ist ein behutsamer Hinweis, keine Diagnose.", + "gad7": "Dein Wert liegt in einem Bereich, in dem ein Gespräch mit einer Fachperson helfen kann. Das ist ein behutsamer Hinweis, keine Diagnose.", + "who5": "Ein Wohlbefindens-Wert von 50 oder darunter kann einen genaueren Blick wert sein. Wenn du magst, schaut der PHQ-9-Check-in auf dieser Seite genauer auf die Stimmung. Das ist ein behutsamer Hinweis, keine Diagnose.", + "sci": "In der Studie zu diesem Fragebogen waren Werte von 16 oder darunter mit wahrscheinlicher Insomnie verbunden. Wenn schlechter Schlaf dich weiter belastet, kann ein Gespräch mit einer Fachperson helfen. Das ist keine Diagnose." + }, + "validatedInEnglishNote": "Dieser Fragebogen ist in englischer Sprache validiert; die Fragen erscheinen im englischen Original.", + "who5Options": { + "5": "Die ganze Zeit", + "4": "Meistens", + "3": "Etwas mehr als die Hälfte der Zeit", + "2": "Etwas weniger als die Hälfte der Zeit", + "1": "Ab und zu", + "0": "Zu keinem Zeitpunkt" + }, + "sciOptions": { + "duration": { + "4": "0–15 min", + "3": "16–30 min", + "2": "31–45 min", + "1": "46–60 min", + "0": "≥ 61 min" + }, + "nights": { + "4": "0–1", + "3": "2", + "2": "3", + "1": "4", + "0": "5–7" + }, + "quality": { + "4": "Very good", + "3": "Good", + "2": "Average", + "1": "Poor", + "0": "Very poor" + }, + "impact": { + "4": "Not at all", + "3": "A little", + "2": "Somewhat", + "1": "Much", + "0": "Very much" + }, + "problemDuration": { + "4": "I don't have a problem / < 1 month", + "3": "1–2 months", + "2": "3–6 months", + "1": "7–12 months", + "0": "> 1 year" + } + }, + "stems": { + "who5": { + "period": "In den letzten zwei Wochen …" + }, + "sci": { + "night": "Thinking about a typical night in the last month …", + "impact": "Thinking about the past month, to what extent has poor sleep …", + "finally": "Finally …" + } + }, + "openDetail": "Details anzeigen" }, "records": { "disclaimer": "Das sind deine eigenen Angaben, keine ärztliche Diagnose. HealthLog stellt keine Diagnose, behandelt nicht und deutet nichts.", diff --git a/messages/en.json b/messages/en.json index 9c733b7c2..a3c55c157 100644 --- a/messages/en.json +++ b/messages/en.json @@ -599,7 +599,9 @@ "typeWaistCircumference": "Waist circumference", "typeWaistToHeight": "Waist-to-height ratio", "unitScore": "pts", - "unitRatio": "ratio" + "unitRatio": "ratio", + "typeWho5Score": "WHO-5 score", + "typeSciScore": "SCI score" }, "mood": { "title": "Mood", @@ -7639,8 +7641,8 @@ }, "mentalHealth": { "pageTitle": "Mental wellbeing check-in", - "pageDescription": "Two validated, opt-in self-assessments — PHQ-9 (mood) and GAD-7 (worry) — that sit beside your mood tracking. The value is the trend over time, not any single number.", - "pageRhythmHint": "Both tests look back over the last two weeks, so a check-in every two to four weeks is a comfortable rhythm.", + "pageDescription": "Validated, opt-in self-checks — PHQ-9 (mood), GAD-7 (worry), WHO-5 (well-being) and SCI (sleep) — that sit beside your mood tracking. The value is the trend over time, not any single number.", + "pageRhythmHint": "PHQ-9, GAD-7 and the well-being check look back over the last two weeks; the sleep check asks about the past month. A check-in every two to four weeks is a comfortable rhythm.", "attributionLabel": "Attribution", "choosePrompt": "Choose a check-in", "start": "Start", @@ -7649,14 +7651,17 @@ "submitting": "Saving…", "saved": "Saved", "error": "Something went wrong. Please try again.", - "considerProfessional": "Your score is in a range where talking to a health professional may help. This is a gentle suggestion, not a diagnosis.", "instrument": { "phq9": "PHQ-9 · mood", - "gad7": "GAD-7 · worry" + "gad7": "GAD-7 · worry", + "who5": "WHO-5 · well-being", + "sci": "SCI · sleep" }, "instrumentSub": { "phq9": "Depression screen, 9 questions", - "gad7": "Anxiety screen, 7 questions" + "gad7": "Anxiety screen, 7 questions", + "who5": "Well-being check, 5 questions", + "sci": "Sleep check, 8 questions" }, "options": { "0": "Not at all", @@ -7691,16 +7696,23 @@ "mild": "Mild", "moderate": "Moderate", "severe": "Severe" + }, + "WHO5": { + "low": "Lower well-being", + "good": "Good well-being" + }, + "SCI": { + "belowThreshold": "Possible insomnia range", + "aboveThreshold": "No insomnia indication" } }, "history": { "title": "Your history", "empty": "No check-ins yet. Your trend will appear here once you complete one.", "flaggedBadge": "Support shown", - "tab": { - "phq9": "PHQ-9", - "gad7": "GAD-7" - } + "chartTitle": "Score trend", + "totalLabel": "Score", + "bandLabel": "Category" }, "crisis": { "title": "You don't have to face this alone", @@ -7728,6 +7740,23 @@ "5": "Being so restless that it is hard to sit still", "6": "Becoming easily annoyed or irritable", "7": "Feeling afraid as if something awful might happen" + }, + "who5": { + "1": "I have felt cheerful and in good spirits", + "2": "I have felt calm and relaxed", + "3": "I have felt active and vigorous", + "4": "I woke up feeling fresh and rested", + "5": "My daily life has been filled with things that interest me" + }, + "sci": { + "1": "… how long does it take you to fall asleep?", + "2": "… if you then wake up during the night … how long are you awake for in total? (add all the wakenings up)", + "3": "… how many nights a week do you have a problem with your sleep?", + "4": "… how would you rate your sleep quality?", + "5": "… affected your mood, energy, or relationships?", + "6": "… affected your concentration, productivity, or ability to stay awake?", + "7": "… troubled you in general?", + "8": "… how long have you had a problem with your sleep?" } }, "crisisResource": { @@ -7755,14 +7784,79 @@ }, "instrumentDescription": { "phq9": "9-question depression screen. Answer for the last two weeks.", - "gad7": "7-question anxiety screen. Answer for the last two weeks." + "gad7": "7-question anxiety screen. Answer for the last two weeks.", + "who5": "5-question well-being check. Answer for the last two weeks; higher numbers mean better well-being.", + "sci": "8-question sleep check. Most questions ask about the past month; higher scores mean better sleep." }, "progress": "{current} / {total}", "next": "Next", "lastResult": "Last test", "lastScore": "Last result", "noResultYet": "No check-in yet", - "backToList": "Overview" + "backToList": "Overview", + "followUpHint": { + "phq9": "Your score is in a range where talking to a health professional may help. This is a gentle suggestion, not a diagnosis.", + "gad7": "Your score is in a range where talking to a health professional may help. This is a gentle suggestion, not a diagnosis.", + "who5": "A well-being score of 50 or below can be worth a closer look. If you like, the PHQ-9 check-in on this page looks at mood in more depth. This is a gentle suggestion, not a diagnosis.", + "sci": "In the study behind this questionnaire, scores of 16 or below were associated with probable insomnia. If poor sleep keeps troubling you, talking to a health professional may help. This is not a diagnosis." + }, + "validatedInEnglishNote": "This questionnaire is validated in English; the questions are shown in the original English wording.", + "who5Options": { + "5": "All of the time", + "4": "Most of the time", + "3": "More than half of the time", + "2": "Less than half of the time", + "1": "Some of the time", + "0": "At no time" + }, + "sciOptions": { + "duration": { + "4": "0–15 min", + "3": "16–30 min", + "2": "31–45 min", + "1": "46–60 min", + "0": "≥ 61 min" + }, + "nights": { + "4": "0–1", + "3": "2", + "2": "3", + "1": "4", + "0": "5–7" + }, + "quality": { + "4": "Very good", + "3": "Good", + "2": "Average", + "1": "Poor", + "0": "Very poor" + }, + "impact": { + "4": "Not at all", + "3": "A little", + "2": "Somewhat", + "1": "Much", + "0": "Very much" + }, + "problemDuration": { + "4": "I don't have a problem / < 1 month", + "3": "1–2 months", + "2": "3–6 months", + "1": "7–12 months", + "0": "> 1 year" + } + }, + "stems": { + "who5": { + "period": "Over the last two weeks …" + }, + "sci": { + "night": "Thinking about a typical night in the last month …", + "impact": "Thinking about the past month, to what extent has poor sleep …", + "finally": "Finally …" + } + }, + "openDetail": "Show details" }, "records": { "disclaimer": "These are your own records, not a medical diagnosis. HealthLog does not diagnose, treat, or interpret them.", diff --git a/messages/es.json b/messages/es.json index 08069dda2..e3d4857e2 100644 --- a/messages/es.json +++ b/messages/es.json @@ -599,7 +599,9 @@ "typeWaistCircumference": "Perímetro de cintura", "typeWaistToHeight": "Índice cintura-altura", "unitScore": "pts", - "unitRatio": "ratio" + "unitRatio": "ratio", + "typeWho5Score": "Puntuación WHO-5", + "typeSciScore": "Puntuación SCI" }, "mood": { "title": "Estado de ánimo", @@ -7639,8 +7641,8 @@ }, "mentalHealth": { "pageTitle": "Revisión de bienestar mental", - "pageDescription": "Dos autoevaluaciones validadas y opcionales — PHQ-9 (estado de ánimo) y GAD-7 (preocupación) — junto a tu registro de ánimo. Lo valioso es la tendencia en el tiempo, no un único número.", - "pageRhythmHint": "Ambos tests se refieren a las últimas dos semanas, por lo que un check-in cada dos a cuatro semanas es un buen ritmo.", + "pageDescription": "Cuatro autoevaluaciones validadas y opcionales — PHQ-9 (estado de ánimo), GAD-7 (preocupación), WHO-5 (bienestar) y SCI (sueño) — junto a tu registro de ánimo. Lo valioso es la tendencia en el tiempo, no un único número.", + "pageRhythmHint": "El PHQ-9, el GAD-7 y el test de bienestar se refieren a las últimas dos semanas; el test de sueño pregunta por el último mes. Un check-in cada dos a cuatro semanas es un buen ritmo.", "attributionLabel": "Fuente", "choosePrompt": "Elige una revisión", "start": "Empezar", @@ -7649,14 +7651,17 @@ "submitting": "Guardando…", "saved": "Guardado", "error": "Algo salió mal. Inténtalo de nuevo.", - "considerProfessional": "Tu puntuación está en un rango en el que hablar con un profesional de salud puede ayudar. Es una sugerencia amable, no un diagnóstico.", "instrument": { "phq9": "PHQ-9 · ánimo", - "gad7": "GAD-7 · preocupación" + "gad7": "GAD-7 · preocupación", + "who5": "WHO-5 · bienestar", + "sci": "SCI · sueño" }, "instrumentSub": { "phq9": "Cribado de depresión, 9 preguntas", - "gad7": "Cribado de ansiedad, 7 preguntas" + "gad7": "Cribado de ansiedad, 7 preguntas", + "who5": "Test de bienestar, 5 preguntas", + "sci": "Test de sueño, 8 preguntas" }, "options": { "0": "Nunca", @@ -7691,16 +7696,23 @@ "mild": "Leve", "moderate": "Moderada", "severe": "Grave" + }, + "WHO5": { + "low": "Bienestar más bajo", + "good": "Buen bienestar" + }, + "SCI": { + "belowThreshold": "Rango de posible insomnio", + "aboveThreshold": "Sin indicios de insomnio" } }, "history": { "title": "Tu historial", "empty": "Aún no hay revisiones. Tu tendencia aparecerá aquí cuando completes una.", "flaggedBadge": "Apoyo mostrado", - "tab": { - "phq9": "PHQ-9", - "gad7": "GAD-7" - } + "chartTitle": "Evolución de la puntuación", + "totalLabel": "Puntuación", + "bandLabel": "Categoría" }, "crisis": { "title": "No tienes que afrontar esto a solas", @@ -7728,6 +7740,23 @@ "5": "Estar tan inquieto/a que es difícil quedarse sentado/a tranquilamente", "6": "Molestarse o irritarse fácilmente", "7": "Sentir miedo como si algo terrible fuera a suceder" + }, + "who5": { + "1": "Me he sentido alegre y de buen humor", + "2": "Me he sentido tranquilo y relajado", + "3": "Me he sentido activo y enérgico", + "4": "Me he despertado fresco y descansado", + "5": "Mi vida cotidiana ha estado llena de cosas que me interesan" + }, + "sci": { + "1": "… how long does it take you to fall asleep?", + "2": "… if you then wake up during the night … how long are you awake for in total? (add all the wakenings up)", + "3": "… how many nights a week do you have a problem with your sleep?", + "4": "… how would you rate your sleep quality?", + "5": "… affected your mood, energy, or relationships?", + "6": "… affected your concentration, productivity, or ability to stay awake?", + "7": "… troubled you in general?", + "8": "… how long have you had a problem with your sleep?" } }, "crisisResource": { @@ -7755,14 +7784,79 @@ }, "instrumentDescription": { "phq9": "Cribado de depresión de 9 preguntas. Responde pensando en las últimas dos semanas.", - "gad7": "Cribado de ansiedad de 7 preguntas. Responde pensando en las últimas dos semanas." + "gad7": "Cribado de ansiedad de 7 preguntas. Responde pensando en las últimas dos semanas.", + "who5": "Test de bienestar de 5 preguntas. Responde pensando en las últimas dos semanas; cifras mayores significan mayor bienestar.", + "sci": "Test de sueño de 8 preguntas. La mayoría se refiere al último mes; puntuaciones mayores significan mejor sueño." }, "progress": "{current} / {total}", "next": "Siguiente", "lastResult": "Última prueba", "lastScore": "Último resultado", "noResultYet": "Aún sin check-in", - "backToList": "Vista general" + "backToList": "Vista general", + "followUpHint": { + "phq9": "Tu puntuación está en un rango en el que hablar con un profesional de salud puede ayudar. Es una sugerencia amable, no un diagnóstico.", + "gad7": "Tu puntuación está en un rango en el que hablar con un profesional de salud puede ayudar. Es una sugerencia amable, no un diagnóstico.", + "who5": "Una puntuación de bienestar de 50 o menos puede merecer una mirada más atenta. Si quieres, el PHQ-9 de esta página explora el estado de ánimo con más detalle. Es una sugerencia amable, no un diagnóstico.", + "sci": "En el estudio de este cuestionario, puntuaciones de 16 o menos se asociaron con insomnio probable. Si dormir mal te sigue afectando, hablar con un profesional de salud puede ayudar. No es un diagnóstico." + }, + "validatedInEnglishNote": "Este cuestionario está validado en inglés; las preguntas se muestran en su versión original en inglés.", + "who5Options": { + "5": "Todo el tiempo", + "4": "La mayor parte del tiempo", + "3": "Más de la mitad del tiempo", + "2": "Menos de la mitad del tiempo", + "1": "De vez en cuando", + "0": "Nunca" + }, + "sciOptions": { + "duration": { + "4": "0–15 min", + "3": "16–30 min", + "2": "31–45 min", + "1": "46–60 min", + "0": "≥ 61 min" + }, + "nights": { + "4": "0–1", + "3": "2", + "2": "3", + "1": "4", + "0": "5–7" + }, + "quality": { + "4": "Very good", + "3": "Good", + "2": "Average", + "1": "Poor", + "0": "Very poor" + }, + "impact": { + "4": "Not at all", + "3": "A little", + "2": "Somewhat", + "1": "Much", + "0": "Very much" + }, + "problemDuration": { + "4": "I don't have a problem / < 1 month", + "3": "1–2 months", + "2": "3–6 months", + "1": "7–12 months", + "0": "> 1 year" + } + }, + "stems": { + "who5": { + "period": "Durante las últimas dos semanas:" + }, + "sci": { + "night": "Thinking about a typical night in the last month …", + "impact": "Thinking about the past month, to what extent has poor sleep …", + "finally": "Finally …" + } + }, + "openDetail": "Ver detalles" }, "records": { "disclaimer": "Son tus propios registros, no un diagnóstico médico. HealthLog no diagnostica, no trata ni los interpreta.", diff --git a/messages/fr.json b/messages/fr.json index 29e16188a..fdc353789 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -599,7 +599,9 @@ "typeWaistCircumference": "Tour de taille", "typeWaistToHeight": "Rapport taille-taille", "unitScore": "pts", - "unitRatio": "ratio" + "unitRatio": "ratio", + "typeWho5Score": "Score WHO-5", + "typeSciScore": "Score SCI" }, "mood": { "title": "Humeur", @@ -7639,8 +7641,8 @@ }, "mentalHealth": { "pageTitle": "Point sur le bien-être mental", - "pageDescription": "Deux auto-évaluations validées et facultatives — PHQ-9 (humeur) et GAD-7 (inquiétude) — en complément de ton suivi de l'humeur. Ce qui compte, c'est la tendance dans le temps, pas un chiffre isolé.", - "pageRhythmHint": "Les deux tests portent sur les deux dernières semaines — un check-in toutes les deux à quatre semaines est donc un bon rythme.", + "pageDescription": "Quatre auto-évaluations validées et facultatives — PHQ-9 (humeur), GAD-7 (inquiétude), WHO-5 (bien-être) et SCI (sommeil) — en complément de ton suivi de l'humeur. Ce qui compte, c'est la tendance dans le temps, pas un chiffre isolé.", + "pageRhythmHint": "Le PHQ-9, le GAD-7 et le test de bien-être portent sur les deux dernières semaines ; le test de sommeil porte sur le dernier mois. Un check-in toutes les deux à quatre semaines est un bon rythme.", "attributionLabel": "Source", "choosePrompt": "Choisis un bilan", "start": "Commencer", @@ -7649,14 +7651,17 @@ "submitting": "Enregistrement…", "saved": "Enregistré", "error": "Une erreur est survenue. Réessaie.", - "considerProfessional": "Ton score se situe dans une plage où parler à un professionnel de santé peut aider. C'est une suggestion bienveillante, pas un diagnostic.", "instrument": { "phq9": "PHQ-9 · humeur", - "gad7": "GAD-7 · inquiétude" + "gad7": "GAD-7 · inquiétude", + "who5": "WHO-5 · bien-être", + "sci": "SCI · sommeil" }, "instrumentSub": { "phq9": "Dépistage de la dépression, 9 questions", - "gad7": "Dépistage de l'anxiété, 7 questions" + "gad7": "Dépistage de l'anxiété, 7 questions", + "who5": "Test de bien-être, 5 questions", + "sci": "Test de sommeil, 8 questions" }, "options": { "0": "Jamais", @@ -7691,16 +7696,23 @@ "mild": "Légère", "moderate": "Modérée", "severe": "Sévère" + }, + "WHO5": { + "low": "Bien-être plus faible", + "good": "Bon bien-être" + }, + "SCI": { + "belowThreshold": "Zone d'insomnie possible", + "aboveThreshold": "Pas d'indice d'insomnie" } }, "history": { "title": "Ton historique", "empty": "Aucun bilan pour le moment. Ta tendance apparaîtra ici dès que tu en auras terminé un.", "flaggedBadge": "Aide affichée", - "tab": { - "phq9": "PHQ-9", - "gad7": "GAD-7" - } + "chartTitle": "Évolution du score", + "totalLabel": "Score", + "bandLabel": "Catégorie" }, "crisis": { "title": "Tu n'as pas à affronter cela seul(e)", @@ -7728,6 +7740,23 @@ "5": "Être si agité(e) qu'il est difficile de rester tranquille", "6": "Devenir facilement contrarié(e) ou irritable", "7": "Avoir peur que quelque chose d'épouvantable puisse arriver" + }, + "who5": { + "1": "Je me suis senti(e) bien et de bonne humeur", + "2": "Je me suis senti(e) calme et tranquille", + "3": "Je me suis senti(e) plein(e) d'énergie et vigoureux(se)", + "4": "Je me suis réveillé(e) en me sentant frais(che) et dispos(e)", + "5": "Ma vie quotidienne a été remplie de choses intéressantes" + }, + "sci": { + "1": "… how long does it take you to fall asleep?", + "2": "… if you then wake up during the night … how long are you awake for in total? (add all the wakenings up)", + "3": "… how many nights a week do you have a problem with your sleep?", + "4": "… how would you rate your sleep quality?", + "5": "… affected your mood, energy, or relationships?", + "6": "… affected your concentration, productivity, or ability to stay awake?", + "7": "… troubled you in general?", + "8": "… how long have you had a problem with your sleep?" } }, "crisisResource": { @@ -7755,14 +7784,79 @@ }, "instrumentDescription": { "phq9": "Dépistage de la dépression en 9 questions. Répondez en pensant aux deux dernières semaines.", - "gad7": "Dépistage de l'anxiété en 7 questions. Répondez en pensant aux deux dernières semaines." + "gad7": "Dépistage de l'anxiété en 7 questions. Répondez en pensant aux deux dernières semaines.", + "who5": "Test de bien-être en 5 questions. Répondez en pensant aux deux dernières semaines ; des chiffres plus élevés signifient un meilleur bien-être.", + "sci": "Test de sommeil en 8 questions. La plupart des questions portent sur le dernier mois ; des scores plus élevés signifient un meilleur sommeil." }, "progress": "{current} / {total}", "next": "Suivant", "lastResult": "Dernier test", "lastScore": "Dernier résultat", "noResultYet": "Pas encore de check-in", - "backToList": "Vue d'ensemble" + "backToList": "Vue d'ensemble", + "followUpHint": { + "phq9": "Ton score se situe dans une plage où parler à un professionnel de santé peut aider. C'est une suggestion bienveillante, pas un diagnostic.", + "gad7": "Ton score se situe dans une plage où parler à un professionnel de santé peut aider. C'est une suggestion bienveillante, pas un diagnostic.", + "who5": "Un score de bien-être de 50 ou moins peut mériter un regard plus attentif. Si tu le souhaites, le PHQ-9 sur cette page explore l'humeur plus en détail. C'est une suggestion bienveillante, pas un diagnostic.", + "sci": "Dans l'étude de ce questionnaire, des scores de 16 ou moins étaient associés à une insomnie probable. Si un mauvais sommeil continue de te peser, parler à un professionnel de santé peut aider. Ce n'est pas un diagnostic." + }, + "validatedInEnglishNote": "Ce questionnaire est validé en anglais ; les questions sont affichées dans leur version originale anglaise.", + "who5Options": { + "5": "Tout le temps", + "4": "La plupart du temps", + "3": "Plus de la moitié du temps", + "2": "Moins de la moitié du temps", + "1": "De temps en temps", + "0": "Jamais" + }, + "sciOptions": { + "duration": { + "4": "0–15 min", + "3": "16–30 min", + "2": "31–45 min", + "1": "46–60 min", + "0": "≥ 61 min" + }, + "nights": { + "4": "0–1", + "3": "2", + "2": "3", + "1": "4", + "0": "5–7" + }, + "quality": { + "4": "Very good", + "3": "Good", + "2": "Average", + "1": "Poor", + "0": "Very poor" + }, + "impact": { + "4": "Not at all", + "3": "A little", + "2": "Somewhat", + "1": "Much", + "0": "Very much" + }, + "problemDuration": { + "4": "I don't have a problem / < 1 month", + "3": "1–2 months", + "2": "3–6 months", + "1": "7–12 months", + "0": "> 1 year" + } + }, + "stems": { + "who5": { + "period": "Au cours des deux dernières semaines" + }, + "sci": { + "night": "Thinking about a typical night in the last month …", + "impact": "Thinking about the past month, to what extent has poor sleep …", + "finally": "Finally …" + } + }, + "openDetail": "Voir les détails" }, "records": { "disclaimer": "Ce sont vos propres informations, pas un diagnostic médical. HealthLog ne diagnostique pas, ne traite pas et ne les interprète pas.", diff --git a/messages/it.json b/messages/it.json index baafa256b..1feae5809 100644 --- a/messages/it.json +++ b/messages/it.json @@ -599,7 +599,9 @@ "typeWaistCircumference": "Circonferenza vita", "typeWaistToHeight": "Rapporto vita-altezza", "unitScore": "pti", - "unitRatio": "rapporto" + "unitRatio": "rapporto", + "typeWho5Score": "Punteggio WHO-5", + "typeSciScore": "Punteggio SCI" }, "mood": { "title": "Umore", @@ -7639,8 +7641,8 @@ }, "mentalHealth": { "pageTitle": "Check-in sul benessere mentale", - "pageDescription": "Due autovalutazioni validate e facoltative — PHQ-9 (umore) e GAD-7 (preoccupazione) — accanto al tuo diario dell'umore. Ciò che conta è l'andamento nel tempo, non un singolo numero.", - "pageRhythmHint": "Entrambi i test riguardano le ultime due settimane, quindi un check-in ogni due-quattro settimane è un buon ritmo.", + "pageDescription": "Quattro autovalutazioni validate e facoltative — PHQ-9 (umore), GAD-7 (preoccupazione), WHO-5 (benessere) e SCI (sonno) — accanto al tuo diario dell'umore. Ciò che conta è l'andamento nel tempo, non un singolo numero.", + "pageRhythmHint": "PHQ-9, GAD-7 e il test sul benessere riguardano le ultime due settimane; il test sul sonno chiede dell'ultimo mese. Un check-in ogni due-quattro settimane è un buon ritmo.", "attributionLabel": "Fonte", "choosePrompt": "Scegli un check-in", "start": "Inizia", @@ -7649,14 +7651,17 @@ "submitting": "Salvataggio…", "saved": "Salvato", "error": "Qualcosa è andato storto. Riprova.", - "considerProfessional": "Il tuo punteggio è in un intervallo in cui parlare con un professionista sanitario può aiutare. È un suggerimento gentile, non una diagnosi.", "instrument": { "phq9": "PHQ-9 · umore", - "gad7": "GAD-7 · preoccupazione" + "gad7": "GAD-7 · preoccupazione", + "who5": "WHO-5 · benessere", + "sci": "SCI · sonno" }, "instrumentSub": { "phq9": "Screening della depressione, 9 domande", - "gad7": "Screening dell'ansia, 7 domande" + "gad7": "Screening dell'ansia, 7 domande", + "who5": "Test sul benessere, 5 domande", + "sci": "Test sul sonno, 8 domande" }, "options": { "0": "Per niente", @@ -7691,16 +7696,23 @@ "mild": "Lieve", "moderate": "Moderata", "severe": "Grave" + }, + "WHO5": { + "low": "Benessere più basso", + "good": "Buon benessere" + }, + "SCI": { + "belowThreshold": "Fascia di possibile insonnia", + "aboveThreshold": "Nessun indizio di insonnia" } }, "history": { "title": "La tua cronologia", "empty": "Ancora nessun check-in. Il tuo andamento apparirà qui dopo che ne avrai completato uno.", "flaggedBadge": "Supporto mostrato", - "tab": { - "phq9": "PHQ-9", - "gad7": "GAD-7" - } + "chartTitle": "Andamento del punteggio", + "totalLabel": "Punteggio", + "bandLabel": "Categoria" }, "crisis": { "title": "Non devi affrontare tutto questo da solo/a", @@ -7728,6 +7740,23 @@ "5": "Essere così irrequieto/a da far fatica a stare seduto/a fermo/a", "6": "Infastidirsi o irritarsi facilmente", "7": "Avere paura come se potesse accadere qualcosa di terribile" + }, + "who5": { + "1": "Mi sono sentito/a allegro/a e di buon umore", + "2": "Mi sono sentito/a calmo/a e rilassato/a", + "3": "Mi sono sentito/a attivo/a ed energico/a", + "4": "Mi sono svegliato/a sentendomi fresco/a e riposato/a", + "5": "La mia vita di tutti i giorni è stata piena di cose che mi interessano" + }, + "sci": { + "1": "… how long does it take you to fall asleep?", + "2": "… if you then wake up during the night … how long are you awake for in total? (add all the wakenings up)", + "3": "… how many nights a week do you have a problem with your sleep?", + "4": "… how would you rate your sleep quality?", + "5": "… affected your mood, energy, or relationships?", + "6": "… affected your concentration, productivity, or ability to stay awake?", + "7": "… troubled you in general?", + "8": "… how long have you had a problem with your sleep?" } }, "crisisResource": { @@ -7755,14 +7784,79 @@ }, "instrumentDescription": { "phq9": "Screening della depressione in 9 domande. Rispondi pensando alle ultime due settimane.", - "gad7": "Screening dell'ansia in 7 domande. Rispondi pensando alle ultime due settimane." + "gad7": "Screening dell'ansia in 7 domande. Rispondi pensando alle ultime due settimane.", + "who5": "Test sul benessere in 5 domande. Rispondi pensando alle ultime due settimane; numeri più alti indicano un maggior benessere.", + "sci": "Test sul sonno in 8 domande. La maggior parte riguarda l'ultimo mese; punteggi più alti indicano un sonno migliore." }, "progress": "{current} / {total}", "next": "Avanti", "lastResult": "Ultimo test", "lastScore": "Ultimo risultato", "noResultYet": "Ancora nessun check-in", - "backToList": "Panoramica" + "backToList": "Panoramica", + "followUpHint": { + "phq9": "Il tuo punteggio è in un intervallo in cui parlare con un professionista sanitario può aiutare. È un suggerimento gentile, non una diagnosi.", + "gad7": "Il tuo punteggio è in un intervallo in cui parlare con un professionista sanitario può aiutare. È un suggerimento gentile, non una diagnosi.", + "who5": "Un punteggio di benessere pari o inferiore a 50 può meritare uno sguardo più attento. Se vuoi, il PHQ-9 in questa pagina esplora l'umore più in dettaglio. È un suggerimento gentile, non una diagnosi.", + "sci": "Nello studio di questo questionario, punteggi pari o inferiori a 16 erano associati a insonnia probabile. Se dormire male continua a pesarti, parlare con un professionista sanitario può aiutare. Non è una diagnosi." + }, + "validatedInEnglishNote": "Questo questionario è validato in inglese; le domande sono mostrate nella versione originale inglese.", + "who5Options": { + "5": "Sempre", + "4": "La maggior parte del tempo", + "3": "Più della metà del tempo", + "2": "Meno della metà del tempo", + "1": "A volte", + "0": "Mai" + }, + "sciOptions": { + "duration": { + "4": "0–15 min", + "3": "16–30 min", + "2": "31–45 min", + "1": "46–60 min", + "0": "≥ 61 min" + }, + "nights": { + "4": "0–1", + "3": "2", + "2": "3", + "1": "4", + "0": "5–7" + }, + "quality": { + "4": "Very good", + "3": "Good", + "2": "Average", + "1": "Poor", + "0": "Very poor" + }, + "impact": { + "4": "Not at all", + "3": "A little", + "2": "Somewhat", + "1": "Much", + "0": "Very much" + }, + "problemDuration": { + "4": "I don't have a problem / < 1 month", + "3": "1–2 months", + "2": "3–6 months", + "1": "7–12 months", + "0": "> 1 year" + } + }, + "stems": { + "who5": { + "period": "Nelle ultime due settimane" + }, + "sci": { + "night": "Thinking about a typical night in the last month …", + "impact": "Thinking about the past month, to what extent has poor sleep …", + "finally": "Finally …" + } + }, + "openDetail": "Mostra dettagli" }, "records": { "disclaimer": "Sono i tuoi dati, non una diagnosi medica. HealthLog non diagnostica, non cura e non li interpreta.", diff --git a/messages/pl.json b/messages/pl.json index d129e4f98..5e32e0dd1 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -599,7 +599,9 @@ "typeWaistCircumference": "Obwód talii", "typeWaistToHeight": "Stosunek talii do wzrostu", "unitScore": "pkt", - "unitRatio": "stosunek" + "unitRatio": "stosunek", + "typeWho5Score": "Wynik WHO-5", + "typeSciScore": "Wynik SCI" }, "mood": { "title": "Nastrój", @@ -7639,8 +7641,8 @@ }, "mentalHealth": { "pageTitle": "Sprawdzenie dobrostanu psychicznego", - "pageDescription": "Dwa zwalidowane, dobrowolne testy — PHQ-9 (nastrój) i GAD-7 (lęk) — obok Twojego dziennika nastroju. Liczy się trend w czasie, a nie pojedynczy wynik.", - "pageRhythmHint": "Oba testy dotyczą ostatnich dwóch tygodni, dlatego check-in co dwa–cztery tygodnie to dobry rytm.", + "pageDescription": "Cztery zwalidowane, dobrowolne testy — PHQ-9 (nastrój), GAD-7 (lęk), WHO-5 (samopoczucie) i SCI (sen) — obok Twojego dziennika nastroju. Liczy się trend w czasie, a nie pojedynczy wynik.", + "pageRhythmHint": "PHQ-9, GAD-7 i test samopoczucia dotyczą ostatnich dwóch tygodni; test snu pyta o ostatni miesiąc. Check-in co dwa–cztery tygodnie to dobry rytm.", "attributionLabel": "Źródło", "choosePrompt": "Wybierz sprawdzenie", "start": "Rozpocznij", @@ -7649,14 +7651,17 @@ "submitting": "Zapisywanie…", "saved": "Zapisano", "error": "Coś poszło nie tak. Spróbuj ponownie.", - "considerProfessional": "Twój wynik mieści się w zakresie, w którym rozmowa ze specjalistą może pomóc. To życzliwa sugestia, a nie diagnoza.", "instrument": { "phq9": "PHQ-9 · nastrój", - "gad7": "GAD-7 · lęk" + "gad7": "GAD-7 · lęk", + "who5": "WHO-5 · samopoczucie", + "sci": "SCI · sen" }, "instrumentSub": { "phq9": "Przesiew depresji, 9 pytań", - "gad7": "Przesiew lęku, 7 pytań" + "gad7": "Przesiew lęku, 7 pytań", + "who5": "Test samopoczucia, 5 pytań", + "sci": "Test snu, 8 pytań" }, "options": { "0": "Wcale", @@ -7691,16 +7696,23 @@ "mild": "Łagodny", "moderate": "Umiarkowany", "severe": "Ciężki" + }, + "WHO5": { + "low": "Niższe samopoczucie", + "good": "Dobre samopoczucie" + }, + "SCI": { + "belowThreshold": "Zakres możliwej bezsenności", + "aboveThreshold": "Brak wskazań na bezsenność" } }, "history": { "title": "Twoja historia", "empty": "Brak sprawdzeń. Twój trend pojawi się tutaj po ukończeniu pierwszego.", "flaggedBadge": "Pokazano wsparcie", - "tab": { - "phq9": "PHQ-9", - "gad7": "GAD-7" - } + "chartTitle": "Przebieg wyniku", + "totalLabel": "Wynik", + "bandLabel": "Kategoria" }, "crisis": { "title": "Nie musisz mierzyć się z tym sam(a)", @@ -7728,6 +7740,23 @@ "5": "Niepokój tak duży, że trudno usiedzieć w miejscu", "6": "Łatwe wpadanie w rozdrażnienie lub irytację", "7": "Odczuwanie lęku, jakby miało się zdarzyć coś strasznego" + }, + "who5": { + "1": "Czułam się wesoła/Czułem się wesoły i w dobrym nastroju", + "2": "Czułam się spokojna i odprężona/Czułem się spokojny i odprężony", + "3": "Czułam się aktywna i energiczna/Czułem się aktywny i energiczny", + "4": "Budziłam się z uczuciem świeżości i wypoczęta/Budziłem się z uczuciem świeżości i wypoczęty", + "5": "Moje życie codzienne było wypełnione interesującymi mnie sprawami" + }, + "sci": { + "1": "… how long does it take you to fall asleep?", + "2": "… if you then wake up during the night … how long are you awake for in total? (add all the wakenings up)", + "3": "… how many nights a week do you have a problem with your sleep?", + "4": "… how would you rate your sleep quality?", + "5": "… affected your mood, energy, or relationships?", + "6": "… affected your concentration, productivity, or ability to stay awake?", + "7": "… troubled you in general?", + "8": "… how long have you had a problem with your sleep?" } }, "crisisResource": { @@ -7755,14 +7784,79 @@ }, "instrumentDescription": { "phq9": "Przesiew w kierunku depresji, 9 pytań. Odpowiadaj, myśląc o ostatnich dwóch tygodniach.", - "gad7": "Przesiew w kierunku lęku, 7 pytań. Odpowiadaj, myśląc o ostatnich dwóch tygodniach." + "gad7": "Przesiew w kierunku lęku, 7 pytań. Odpowiadaj, myśląc o ostatnich dwóch tygodniach.", + "who5": "Test samopoczucia, 5 pytań. Odpowiadaj, myśląc o ostatnich dwóch tygodniach; wyższe cyfry oznaczają lepsze samopoczucie.", + "sci": "Test snu, 8 pytań. Większość pytań dotyczy ostatniego miesiąca; wyższe wyniki oznaczają lepszy sen." }, "progress": "{current} / {total}", "next": "Dalej", "lastResult": "Ostatni test", "lastScore": "Ostatni wynik", "noResultYet": "Brak check-inów", - "backToList": "Przegląd" + "backToList": "Przegląd", + "followUpHint": { + "phq9": "Twój wynik mieści się w zakresie, w którym rozmowa ze specjalistą może pomóc. To życzliwa sugestia, a nie diagnoza.", + "gad7": "Twój wynik mieści się w zakresie, w którym rozmowa ze specjalistą może pomóc. To życzliwa sugestia, a nie diagnoza.", + "who5": "Wynik samopoczucia 50 lub niższy może być wart bliższego spojrzenia. Jeśli chcesz, PHQ-9 na tej stronie przygląda się nastrojowi dokładniej. To życzliwa sugestia, a nie diagnoza.", + "sci": "W badaniu tego kwestionariusza wyniki 16 lub niższe wiązały się z prawdopodobną bezsennością. Jeśli zły sen nadal Ci doskwiera, rozmowa ze specjalistą może pomóc. To nie jest diagnoza." + }, + "validatedInEnglishNote": "Ten kwestionariusz jest zwalidowany w języku angielskim; pytania są wyświetlane w angielskim oryginale.", + "who5Options": { + "5": "Cały czas", + "4": "Prawie cały czas", + "3": "Więcej niż połowę czasu", + "2": "Mniej niż połowę czasu", + "1": "Od czasu do czasu", + "0": "Nigdy" + }, + "sciOptions": { + "duration": { + "4": "0–15 min", + "3": "16–30 min", + "2": "31–45 min", + "1": "46–60 min", + "0": "≥ 61 min" + }, + "nights": { + "4": "0–1", + "3": "2", + "2": "3", + "1": "4", + "0": "5–7" + }, + "quality": { + "4": "Very good", + "3": "Good", + "2": "Average", + "1": "Poor", + "0": "Very poor" + }, + "impact": { + "4": "Not at all", + "3": "A little", + "2": "Somewhat", + "1": "Much", + "0": "Very much" + }, + "problemDuration": { + "4": "I don't have a problem / < 1 month", + "3": "1–2 months", + "2": "3–6 months", + "1": "7–12 months", + "0": "> 1 year" + } + }, + "stems": { + "who5": { + "period": "W ciągu ostatnich dwóch tygodni" + }, + "sci": { + "night": "Thinking about a typical night in the last month …", + "impact": "Thinking about the past month, to what extent has poor sleep …", + "finally": "Finally …" + } + }, + "openDetail": "Pokaż szczegóły" }, "records": { "disclaimer": "To Twoje własne wpisy, a nie diagnoza lekarska. HealthLog nie diagnozuje, nie leczy i ich nie interpretuje.", diff --git a/src/__tests__/i18n-english-leak-guard.test.ts b/src/__tests__/i18n-english-leak-guard.test.ts index 9a3b00612..fadc16f8f 100644 --- a/src/__tests__/i18n-english-leak-guard.test.ts +++ b/src/__tests__/i18n-english-leak-guard.test.ts @@ -155,6 +155,39 @@ const LEGIT_IDENTICAL = new Set([ "mentalHealth.items.phq9.7", "mentalHealth.items.phq9.8", "mentalHealth.items.phq9.9", + // v1.27.9 — the Sleep Condition Indicator is validated in English only + // (Espie 2014, CC BY-NC). Item text, section stems and response anchors + // ship VERBATIM in the source language in every locale — a self-made + // translation would invalidate the instrument — and the UI shows a + // localized "validated in English" note instead. Identical-to-EN is the + // intended state here, not a leak. + "mentalHealth.items.sci.1", + "mentalHealth.items.sci.2", + "mentalHealth.items.sci.3", + "mentalHealth.items.sci.4", + "mentalHealth.items.sci.5", + "mentalHealth.items.sci.6", + "mentalHealth.items.sci.7", + "mentalHealth.items.sci.8", + "mentalHealth.stems.sci.night", + "mentalHealth.stems.sci.impact", + "mentalHealth.stems.sci.finally", + "mentalHealth.sciOptions.duration.0", + "mentalHealth.sciOptions.quality.4", + "mentalHealth.sciOptions.quality.3", + "mentalHealth.sciOptions.quality.2", + "mentalHealth.sciOptions.quality.1", + "mentalHealth.sciOptions.quality.0", + "mentalHealth.sciOptions.impact.4", + "mentalHealth.sciOptions.impact.3", + "mentalHealth.sciOptions.impact.2", + "mentalHealth.sciOptions.impact.1", + "mentalHealth.sciOptions.impact.0", + "mentalHealth.sciOptions.problemDuration.4", + "mentalHealth.sciOptions.problemDuration.3", + "mentalHealth.sciOptions.problemDuration.2", + "mentalHealth.sciOptions.problemDuration.1", + "mentalHealth.sciOptions.problemDuration.0", ]); describe("i18n English-leak guard", () => { diff --git a/src/components/measurement-reminders/vorsorge-section.tsx b/src/components/measurement-reminders/vorsorge-section.tsx index 5c03acd8c..920ed7629 100644 --- a/src/components/measurement-reminders/vorsorge-section.tsx +++ b/src/components/measurement-reminders/vorsorge-section.tsx @@ -116,11 +116,11 @@ const TYPE_GROUPS = [ ], }, // v1.27.6 — the mental-wellbeing screenings become plannable like any - // other Vorsorge item. Auto-resolve rides the server-written PHQ9_SCORE / - // GAD7_SCORE row a completed check-in produces. + // other Vorsorge item. Auto-resolve rides the server-written *_SCORE row a + // completed check-in produces (v1.27.9 adds WHO-5 + SCI). { group: "mentalWellbeing", - types: ["PHQ9_SCORE", "GAD7_SCORE"], + types: ["PHQ9_SCORE", "GAD7_SCORE", "WHO5_SCORE", "SCI_SCORE"], }, ] as const; diff --git a/src/components/mental-health/__tests__/mental-wellbeing.test.tsx b/src/components/mental-health/__tests__/mental-wellbeing.test.tsx index 0c6868b49..5268847ee 100644 --- a/src/components/mental-health/__tests__/mental-wellbeing.test.tsx +++ b/src/components/mental-health/__tests__/mental-wellbeing.test.tsx @@ -11,9 +11,9 @@ import en from "../../../../messages/en.json"; * means clicks can't be driven here — the wizard's advance/back grammar is * pinned in `check-in-nav.test.ts`. These tests pin the SSR-observable * contracts: the disclaimer renders ONLY on the landing (never while testing), - * a positive item-9 surfaces the crisis card, and the history paints its - * dated list with severity + "support shown" badges — and NO trend chart - * (v1.27.6: the score curve lives in Insights / Measurements, not here). + * a positive item-9 surfaces the crisis card, and (v1.27.9) the landing is + * intro + instrument cards only — the dated history + trend chart live in the + * per-instrument detail a card click opens, pinned to one instrument. */ vi.mock("@/lib/api/api-fetch", () => ({ @@ -26,6 +26,7 @@ import { CheckInWizard } from "../check-in-wizard"; import { AssessmentResult } from "../assessment-result"; import { AssessmentHistory } from "../assessment-history"; import { InstrumentCard } from "../instrument-card"; +import { InstrumentDetail } from "../instrument-detail"; import type { AssessmentRow, CreateResponse } from "../types"; const mh = en.mentalHealth; @@ -178,18 +179,17 @@ describe("history rendering", () => { }, ]; - const html = withProviders(); + const html = withProviders( + , + ); - it("paints the dated list WITHOUT a trend chart shell", () => { + it("paints the pinned dated list with the trend-chart shell, no instrument toggle", () => { + // v1.27.9 — the history is pinned to one instrument inside the detail + // surface: no PHQ-9/GAD-7 tablist, and the lazy chart's skeleton shell + // holds the layout in SSR (recharts itself stays off first-load JS). expect(html).toContain('data-slot="history-list"'); - // v1.27.6 — no chart on this surface: no lazy-chart skeleton, no - // recharts container. - expect(html).not.toContain('data-slot="skeleton"'); - expect(html).not.toContain("recharts"); - }); - - it("shows the PHQ-9 / GAD-7 toggle and a severity badge", () => { - expect(html).toContain('role="tablist"'); + expect(html).toContain('data-pinned="PHQ9"'); + expect(html).not.toContain('role="tablist"'); expect(html).toContain(mh.band.PHQ9.modSevere); }); @@ -198,14 +198,175 @@ describe("history rendering", () => { expect(html).toContain(mh.history.flaggedBadge); }); - it("renders the empty state with no rows", () => { - const empty = withProviders(); + it("renders the empty state with no rows for the pinned instrument", () => { + const empty = withProviders( + , + ); expect(empty).toContain(mh.history.empty); expect(empty).not.toContain('data-slot="history-list"'); }); + + it("stays OFF the landing entirely (intro + cards only)", () => { + const landing = withProviders(); + expect(landing).not.toContain('data-slot="mental-health-history"'); + expect(landing).not.toContain('data-slot="history-list"'); + }); }); -describe("instrument card (v1.27.6 — med-/Vorsorge card anatomy)", () => { +describe("WHO-5 / SCI on the same infrastructure (v1.27.9)", () => { + it("renders all four instrument cards on the landing", () => { + const html = withProviders(); + expect(html).toContain(mh.instrument.phq9); + expect(html).toContain(mh.instrument.gad7); + expect(html).toContain(mh.instrument.who5); + expect(html).toContain(mh.instrument.sci); + // The rhythm hint states both recall windows honestly. + expect(html).toContain(mh.pageRhythmHint); + }); + + it("WHO-5 wizard opens on item 1 with the six-point scale in source order and the recall stem", () => { + const html = withProviders( + {}} + onBack={() => {}} + isPending={false} + isError={false} + />, + ); + expect(html).toContain(mh.items.who5["1"]); + expect(html).toContain('data-slot="check-in-stem"'); + expect(html).toContain(mh.stems.who5.period); + // Six anchors, 5 → 0 (the WHO form leads with "All of the time"). + for (const v of ["5", "4", "3", "2", "1", "0"] as const) { + expect(html).toContain(mh.who5Options[v]); + } + // English items are the validated wording for en — no translation note. + expect(html).not.toContain('data-slot="check-in-validated-note"'); + // No functional follow-up outside the PHQ-9: 5 steps total. + expect(html).toContain("1 / 5"); + }); + + it("SCI wizard opens on item 1 with the item-specific duration anchors and section stem", () => { + const html = withProviders( + {}} + onBack={() => {}} + isPending={false} + isError={false} + />, + ); + expect(html).toContain(mh.items.sci["1"]); + expect(html).toContain(mh.stems.sci.night); + // Item 1 carries the sleep-latency time ranges, 4 → 0. + for (const v of ["4", "3", "2", "1", "0"] as const) { + expect(html).toContain(mh.sciOptions.duration[v]); + } + expect(html).toContain("1 / 8"); + }); + + it("shows the gentle PHQ-9 pointer on a WHO-5 total of 50 or below", () => { + const result: CreateResponse = { + assessment: { + id: "w1", + instrument: "WHO5", + locale: "en", + totalScore: 48, + severityBand: "low", + item9Flagged: false, + crisisShownAt: null, + takenAt: "2026-06-20T00:00:00.000Z", + }, + actionThreshold: 50, + crisis: null, + }; + const html = withProviders( + {}} + onBack={() => {}} + />, + ); + expect(html).toContain('data-slot="result-follow-up-hint"'); + expect(html).toContain(mh.followUpHint.who5); + // Required WHO attribution rides the result view. + expect(html).toContain("CC BY-NC-SA 3.0 IGO"); + expect(html).not.toContain('data-slot="mental-health-crisis-card"'); + }); + + it("suppresses the hint on a good WHO-5 total (direction is inverted vs PHQ-9)", () => { + const result: CreateResponse = { + assessment: { + id: "w2", + instrument: "WHO5", + locale: "en", + totalScore: 80, + severityBand: "good", + item9Flagged: false, + crisisShownAt: null, + takenAt: "2026-06-20T00:00:00.000Z", + }, + actionThreshold: 50, + crisis: null, + }; + const html = withProviders( + {}} + onBack={() => {}} + />, + ); + expect(html).not.toContain('data-slot="result-follow-up-hint"'); + }); + + it("shows the neutral SCI band wording at 16 or below, with the Sleepio/BMJ attribution", () => { + const result: CreateResponse = { + assessment: { + id: "s1", + instrument: "SCI", + locale: "en", + totalScore: 14, + severityBand: "belowThreshold", + item9Flagged: false, + crisisShownAt: null, + takenAt: "2026-06-20T00:00:00.000Z", + }, + actionThreshold: 16, + crisis: null, + }; + const html = withProviders( + {}} + onBack={() => {}} + />, + ); + expect(html).toContain(mh.followUpHint.sci); + expect(html).toContain(mh.band.SCI.belowThreshold); + expect(html).toContain("BMJ Open"); + }); + + it("pinned history renders the WHO-5 / SCI series through the same component", () => { + const row: AssessmentRow = { + id: "t1", + instrument: "WHO5", + locale: "en", + totalScore: 72, + severityBand: "good", + item9Flagged: false, + crisisShownAt: null, + takenAt: "2026-06-20T00:00:00.000Z", + }; + const html = withProviders( + , + ); + expect(html).toContain('data-pinned="WHO5"'); + expect(html).toContain(mh.band.WHO5.good); + }); +}); + +describe("instrument card (med-/Vorsorge card anatomy + detail opener)", () => { const lastRow: AssessmentRow = { id: "l1", instrument: "PHQ9", @@ -217,25 +378,43 @@ describe("instrument card (v1.27.6 — med-/Vorsorge card anatomy)", () => { takenAt: "2026-06-20T00:00:00.000Z", }; - it("renders the shared med-card header (title + category badge), no trend target", () => { - const html = withProviders( - {}} />, + function card( + instrument: AssessmentRow["instrument"], + last?: AssessmentRow, + ): string { + return withProviders( + {}} + onOpenDetail={() => {}} + />, ); + } + + it("renders the shared med-card header and the clickable detail body", () => { + const html = card("PHQ9"); // The med-card header primitive paints the bold name + category badge. expect(html).toContain(mh.instrument.phq9); expect(html).toContain(mh.instrumentSub.phq9); - // The former trend-detail button is gone… - expect(html).not.toContain('data-slot="instrument-card-open"'); + // v1.27.9 — the card body opens the per-instrument detail… + expect(html).toContain('data-slot="instrument-card-open"'); + expect(html).toContain(mh.openDetail); // …and the Start action remains the single bottom-pinned action. expect(html).toContain(mh.start); // No history yet → the calm no-check-in line, no fabricated dashes. expect(html).toContain(mh.noResultYet); + // v1.27.9 — the required attribution footer rides every card. + expect(html).toContain('data-slot="instrument-card-attribution"'); + }); + + it("carries the instrument's licence line on the WHO-5 / SCI cards", () => { + expect(card("WHO5")).toContain("CC BY-NC-SA 3.0 IGO"); + expect(card("SCI")).toContain("Sleepio Limited"); }); it("shows last test (relative) and last result (score + band word)", () => { - const html = withProviders( - {}} />, - ); + const html = card("PHQ9", lastRow); expect(html).toContain(mh.lastResult); expect(html).toContain(mh.lastScore); // Score + band word ride one value slot ("8 · Mild"). @@ -244,3 +423,46 @@ describe("instrument card (v1.27.6 — med-/Vorsorge card anatomy)", () => { expect(html).not.toContain(mh.noResultYet); }); }); + +describe("instrument detail (v1.27.9 — opened from a card)", () => { + const rows: AssessmentRow[] = [ + { + id: "d1", + instrument: "SCI", + locale: "en", + totalScore: 22, + severityBand: "aboveThreshold", + item9Flagged: false, + crisisShownAt: null, + takenAt: "2026-06-20T00:00:00.000Z", + }, + { + id: "d2", + instrument: "PHQ9", + locale: "en", + totalScore: 4, + severityBand: "minimal", + item9Flagged: false, + crisisShownAt: null, + takenAt: "2026-06-01T00:00:00.000Z", + }, + ]; + + it("shows last score + band, the Start action, the pinned history and the attribution", () => { + const html = withProviders( + {}} />, + ); + expect(html).toContain('data-slot="instrument-detail"'); + expect(html).toContain('data-slot="instrument-detail-last-score"'); + expect(html).toContain(mh.band.SCI.aboveThreshold); + expect(html).toContain('data-slot="instrument-detail-start"'); + expect(html).toContain(mh.start); + // The pinned history (chart shell + dated list) rides inside the detail. + expect(html).toContain('data-pinned="SCI"'); + expect(html).toContain('data-slot="history-list"'); + // …filtered to the pinned instrument: the PHQ-9 row stays out. + expect(html).not.toContain(mh.band.PHQ9.minimal); + // Required attribution line. + expect(html).toContain("Sleepio Limited"); + }); +}); diff --git a/src/components/mental-health/assessment-history-chart.tsx b/src/components/mental-health/assessment-history-chart.tsx new file mode 100644 index 000000000..95c4dc8fe --- /dev/null +++ b/src/components/mental-health/assessment-history-chart.tsx @@ -0,0 +1,167 @@ +"use client"; + +/** + * v1.25.3 — per-instrument screener trend chart. + * + * v1.25.5 — aligned to the dashboard's chart language exactly: a single + * `type="monotone"` line over the primary colour with a soft gradient area + * fill beneath (same `fillOpacity` + margins the dashboard line charts use), + * the shared grid / axis tokens, and `RichChartTooltip`. The earlier + * severity-band `ReferenceArea`s — unique to this surface — are gone so it + * reads like every other trend; the band still surfaces in the tooltip. + * + * v1.27.9 — back on the surface, but only inside the per-instrument detail + * (opened from a card): the landing stays free of any score curve, so the + * trend is opt-in behind a deliberate click. The Y domain and band wording + * come from the instrument registry, so the WHO-5 (0–100, higher = better) + * and the SCI (0–32, higher = better) render through the same component. + * + * One instrument at a time: the ranges and band sets differ, so the parent + * pins which series this paints. + */ +import { useMemo } from "react"; +import { + Area, + CartesianGrid, + ComposedChart, + Line, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from "recharts"; + +import { RichChartTooltip, type RichTooltipRow } from "../charts/chart-tooltip"; +import { CHART_HEIGHT_PX } from "@/lib/charts/constants"; +import { prefersReducedMotion } from "@/lib/charts/reduced-motion"; +import { formatDateShort } from "@/lib/format"; +import { useTranslations } from "@/lib/i18n/context"; +import { INSTRUMENTS } from "@/lib/mental-health/instruments"; + +import type { AssessmentRow, InstrumentId } from "./types"; + +interface ChartPoint { + timestamp: number; + value: number; + band: string; +} + +export function AssessmentHistoryChart({ + instrument, + rows, +}: { + instrument: InstrumentId; + /** Rows for THIS instrument, any order (sorted internally, ascending time). */ + rows: AssessmentRow[]; +}) { + const { t } = useTranslations(); + const def = INSTRUMENTS[instrument]; + + const points = useMemo(() => { + return [...rows] + .sort( + (a, b) => new Date(a.takenAt).getTime() - new Date(b.takenAt).getTime(), + ) + .map((r) => ({ + timestamp: new Date(r.takenAt).getTime(), + value: r.totalScore, + band: r.severityBand, + })); + }, [rows]); + + const animate = !prefersReducedMotion(); + const primary = "var(--primary)"; + const gradientId = `mh-trend-${instrument}`; + + if (points.length === 0) { + return ( +

+ {t("mentalHealth.history.empty")} +

+ ); + } + + return ( + + + + + + + + + + formatDateShort(new Date(ts))} + tick={{ fontSize: 11, fill: "var(--muted-foreground)" }} + stroke="var(--border)" + minTickGap={32} + /> + + { + const active = props.active ?? false; + const payload = props.payload as + ReadonlyArray<{ payload?: ChartPoint }> | undefined; + const point = payload?.[0]?.payload; + if (!active || !point) { + return ; + } + const rowsOut: RichTooltipRow[] = [ + { + name: t("mentalHealth.history.totalLabel"), + value: String(point.value), + color: primary, + }, + { + name: t("mentalHealth.history.bandLabel"), + value: t(`mentalHealth.band.${instrument}.${point.band}`), + color: "var(--muted-foreground)", + }, + ]; + return ( + + ); + }} + /> + + + + + ); +} diff --git a/src/components/mental-health/assessment-history.tsx b/src/components/mental-health/assessment-history.tsx index 1bda8bfe8..570e4c568 100644 --- a/src/components/mental-health/assessment-history.tsx +++ b/src/components/mental-health/assessment-history.tsx @@ -1,30 +1,31 @@ "use client"; /** - * v1.25.3 — the history card: a dated list with severity badges. + * v1.25.3 — the dated result history with severity badges. * - * v1.27.6 — the trend CHART is gone from this surface. A rising score curve - * on the check-in page itself reads as a verdict at the exact moment someone - * is taking stock of how they feel; the page stays a calm list of past - * check-ins. The score trend still exists — the totals ride PHQ9_SCORE / - * GAD7_SCORE measurement rows, so Insights / Measurements carry the curve - * for whoever seeks it out. + * v1.27.9 — pinned-only: the combined landing card (with the instrument + * toggle) is gone; the landing shows intro + instrument cards and nothing + * else. This component now renders ONE instrument's detail body — trend + * chart (lazy Recharts, the labs-detail precedent) over the dated list — + * inside the detail surface a card click opens. The history is deliberately + * opt-in behind that click, never pushed onto the main page. * - * PHQ-9 / GAD-7 toggle: the two instruments have different score ranges + - * bands, so the list shows one at a time. - * - * CRISIS-SAFETY: a flagged PHQ-9 row carries a discreet "support shown" marker; - * tapping it re-surfaces the STATIC crisis-resource card derived from the row's - * stored locale (`crisisResourcesForLocale`) — it NEVER decrypts or reveals the - * item-9 answer, and surfacing it triggers no third-party alert. No new data - * leaves the safety boundary. + * CRISIS-SAFETY: a flagged PHQ-9 row carries a discreet "support shown" + * marker; tapping it re-surfaces the STATIC crisis-resource card derived from + * the row's stored locale (`crisisResourcesForLocale`) — it NEVER decrypts or + * reveals the item-9 answer, and surfacing it triggers no third-party alert. + * No new data leaves the safety boundary. */ import { useMemo, useState } from "react"; +import dynamic from "next/dynamic"; +import type { ComponentProps } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { EmptyState } from "@/components/ui/empty-state"; +import { ChartErrorBoundary } from "@/components/charts/chart-error-state"; +import { ChartSkeleton } from "@/components/charts/chart-skeleton"; +import { importWithRetry } from "@/lib/retry-import"; import { useFormatters, useTranslations } from "@/lib/i18n/context"; import { crisisResourcesForLocale } from "@/lib/mental-health/crisis-resources"; import { Activity } from "lucide-react"; @@ -32,8 +33,24 @@ import { Activity } from "lucide-react"; import { CrisisCard } from "./crisis-card"; import type { AssessmentRow, CrisisSet, InstrumentId } from "./types"; -function lower(id: InstrumentId): "phq9" | "gad7" { - return id === "PHQ9" ? "phq9" : "gad7"; +// Defer the recharts trend chart so recharts stays off the surface's +// first-load JS (the labs detail precedent). The `` shell +// holds the layout. +const AssessmentHistoryChartLazy = dynamic( + () => + importWithRetry(() => import("./assessment-history-chart")).then((mod) => ({ + default: mod.AssessmentHistoryChart, + })), + { ssr: false, loading: () => }, +); +function AssessmentHistoryChart( + props: ComponentProps, +) { + return ( + + + + ); } /** Re-derive the STATIC crisis set from a row's locale — no decryption. */ @@ -48,122 +65,92 @@ function crisisFromRow(row: AssessmentRow): CrisisSet { }; } -export function AssessmentHistory({ rows }: { rows: AssessmentRow[] }) { +export function AssessmentHistory({ + rows, + instrument, +}: { + /** ALL assessments (any instrument); filtered internally. */ + rows: AssessmentRow[]; + /** The instrument this detail surface is pinned to. */ + instrument: InstrumentId; +}) { const { t } = useTranslations(); const { date: formatDate } = useFormatters(); - const [tab, setTab] = useState("PHQ9"); // Which flagged row's crisis card is currently expanded (re-surfaced). const [openCrisisRowId, setOpenCrisisRowId] = useState(null); - const forTab = useMemo( + const forInstrument = useMemo( () => rows - .filter((r) => r.instrument === tab) + .filter((r) => r.instrument === instrument) .sort( (a, b) => new Date(b.takenAt).getTime() - new Date(a.takenAt).getTime(), ), - [rows, tab], + [rows, instrument], ); - const isEmpty = rows.length === 0; + if (forInstrument.length === 0) { + return ( +
+ } + title={t("mentalHealth.history.title")} + description={t("mentalHealth.history.empty")} + /> +
+ ); + } return ( - - - - {t("mentalHealth.history.title")} - - - - {isEmpty ? ( - } - title={t("mentalHealth.history.title")} - description={t("mentalHealth.history.empty")} - /> - ) : ( - <> - {/* PHQ-9 / GAD-7 toggle — the two ranges + band sets differ, so - the list shows one instrument at a time. */} -
- {(["PHQ9", "GAD7"] as InstrumentId[]).map((id) => ( - - ))} -
+
+ - {forTab.length === 0 ? ( -

- {t("mentalHealth.history.empty")} -

- ) : ( -
    - {forTab.map((row) => { - const flagged = row.item9Flagged; - const open = openCrisisRowId === row.id; - return ( -
  • -
    - - {formatDate(row.takenAt)} - -
    - {flagged && ( - - )} - - {t( - `mentalHealth.band.${row.instrument}.${row.severityBand}`, - )} - - - {row.totalScore} - -
    -
    - {flagged && open && ( - - )} -
  • - ); - })} -
- )} - - )} - - +
    + {forInstrument.map((row) => { + const flagged = row.item9Flagged; + const open = openCrisisRowId === row.id; + return ( +
  • +
    + + {formatDate(row.takenAt)} + +
    + {flagged && ( + + )} + + {t( + `mentalHealth.band.${row.instrument}.${row.severityBand}`, + )} + + + {row.totalScore} + +
    +
    + {flagged && open && } +
  • + ); + })} +
+
); } diff --git a/src/components/mental-health/assessment-result.tsx b/src/components/mental-health/assessment-result.tsx index 67cb1b6cb..ba3611ee7 100644 --- a/src/components/mental-health/assessment-result.tsx +++ b/src/components/mental-health/assessment-result.tsx @@ -6,6 +6,12 @@ * another". Content is unchanged from the v1.25 monolith; it is re-housed as * one stack inside the page spine instead of loose top-level sections. * + * v1.27.9 — direction-aware follow-up hints from the registry: PHQ-9/GAD-7 + * keep the ≥10 professional-help suggestion, a WHO-5 total of 50 or below + * gets a gentle pointer to the PHQ-9 check-in (per the WHO scoring comment), + * and an SCI total of 16 or below gets the paper's neutral band wording. + * The attribution footer now comes from the instrument definition. + * * SAFETY: the crisis set rides the POST response (`crisis`) on any non-zero * item 9 and renders immediately; this surface never invites an AI Coach * conversation about item content. @@ -16,14 +22,10 @@ import { ArrowLeft } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { useTranslations } from "@/lib/i18n/context"; -import { PHQ_GAD_ATTRIBUTION } from "@/lib/mental-health/instruments"; +import { INSTRUMENTS, needsFollowUp } from "@/lib/mental-health/instruments"; import { CrisisCard } from "./crisis-card"; -import type { CreateResponse, InstrumentId } from "./types"; - -function lower(id: InstrumentId): "phq9" | "gad7" { - return id === "PHQ9" ? "phq9" : "gad7"; -} +import type { CreateResponse } from "./types"; export function AssessmentResult({ result, @@ -36,7 +38,8 @@ export function AssessmentResult({ }) { const { t } = useTranslations(); const instrument = result.assessment.instrument; - const key = lower(instrument); + const def = INSTRUMENTS[instrument]; + const key = def.i18nKey; // a11y: on submit the wizard unmounts and this view mounts; move focus to the // result heading so keyboard / screen-reader users land on the new content @@ -104,9 +107,16 @@ export function AssessmentResult({ )} - {result.assessment.totalScore >= result.actionThreshold && ( -

- {t("mentalHealth.considerProfessional")} + {/* Direction-aware follow-up hint: PHQ-9/GAD-7 point up (≥ 10), + WHO-5 (≤ 50 → gentle PHQ-9 pointer) and SCI (≤ 16 → the paper's + neutral band wording) point down. Soft suggestions, never a + diagnosis. */} + {needsFollowUp(instrument, result.assessment.totalScore) && ( +

+ {t(`mentalHealth.followUpHint.${key}`)}

)}
@@ -114,11 +124,11 @@ export function AssessmentResult({ {result.crisis && } -

+

{t("mentalHealth.attributionLabel")}: {" "} - {PHQ_GAD_ATTRIBUTION} + {def.attribution}

); })} diff --git a/src/components/mental-health/instrument-card.tsx b/src/components/mental-health/instrument-card.tsx index 3f0858500..e908ae919 100644 --- a/src/components/mental-health/instrument-card.tsx +++ b/src/components/mental-health/instrument-card.tsx @@ -3,54 +3,81 @@ /** * v1.25.3 — one neutral card per instrument on the landing. * - * v1.27.6 — rebuilt onto the shared med-/Vorsorge card anatomy so the two - * test cards read exactly like their peers: `MedicationCardHeader` (bold + * v1.27.6 — rebuilt onto the shared med-/Vorsorge card anatomy so the test + * cards read exactly like their peers: `MedicationCardHeader` (bold * name + outline category badge), a label/value block in the next-last * grammar ("last test" → relative day, "last result" → score + band word), * and a single bottom-pinned Start action. The surface stays NEUTRAL * regardless of the last band — no severity tint (house rule: status reads * through discreet text only, never a card wash). + * + * v1.27.9 — the card generalises over the four-instrument registry and + * carries the instrument's required attribution/licence line as a small + * factual footer (WHO CC BY-NC-SA citation for the WHO-5, the Sleepio + * CC BY-NC citation for the SCI, the Pfizer grant for PHQ/GAD). A locale + * without validated item wording additionally gets the honest + * "validated in English" note. The card BODY (header + last-result block) + * opens the per-instrument detail — the Vorsorge-/med-card interaction — + * while the bottom-pinned Start button stays a separate, non-overlapping + * action. */ import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { MedicationCardHeader } from "@/components/medications/medication-card-header"; import { useFormatters, useTranslations } from "@/lib/i18n/context"; import { relativeCalendarDate } from "@/lib/i18n/relative-time"; +import { + INSTRUMENTS, + hasValidatedItems, +} from "@/lib/mental-health/instruments"; import type { AssessmentRow, InstrumentId } from "./types"; -function lower(id: InstrumentId): "phq9" | "gad7" { - return id === "PHQ9" ? "phq9" : "gad7"; -} - export function InstrumentCard({ instrument, last, onStart, + onOpenDetail, }: { instrument: InstrumentId; /** Most-recent assessment for this instrument, or undefined when none yet. */ last: AssessmentRow | undefined; onStart: () => void; + /** + * Open this instrument's detail (last score + band, trend chart, dated + * history). The card body is the target; Start stays separate. + */ + onOpenDetail: () => void; }) { - const { t } = useTranslations(); + const { t, locale } = useTranslations(); const { date: formatDate } = useFormatters(); - const key = lower(instrument); + const def = INSTRUMENTS[instrument]; + const key = def.i18nKey; const title = t(`mentalHealth.instrument.${key}`); return ( - - + {/* Body is the detail target: clicking the header / last-result block + opens this instrument's detail (chart + dated history), like a + medication card. The Start button below stays a separate action. */} + + {/* Bottom-pinned single primary action — the med-/Vorsorge card slot. */} -
+
+ {/* Required attribution — factual and unobtrusive; identical slot on + every card so the grid stays symmetric. */} +

+ {!hasValidatedItems(instrument, locale) && ( + <>{t("mentalHealth.validatedInEnglishNote")} + )} + {def.attribution} +

diff --git a/src/components/mental-health/instrument-detail.tsx b/src/components/mental-health/instrument-detail.tsx new file mode 100644 index 000000000..caf468b71 --- /dev/null +++ b/src/components/mental-health/instrument-detail.tsx @@ -0,0 +1,86 @@ +"use client"; + +/** + * v1.27.9 — per-instrument detail surface, opened by clicking an instrument + * card (the Vorsorge-/med-card detail interaction). Hosted in a + * `ResponsiveSheet` by the page orchestrator; this component owns the calm + * detail spine: last score + band → Start action → trend chart + dated + * history (via the pinned `AssessmentHistory`) → the instrument's required + * attribution line. The history and the score curve live ONLY here — the + * landing stays a quiet card grid, so the trend is opt-in behind a + * deliberate click, never pushed at the moment someone arrives to check in. + */ +import { Button } from "@/components/ui/button"; +import { useFormatters, useTranslations } from "@/lib/i18n/context"; +import { relativeCalendarDate } from "@/lib/i18n/relative-time"; +import { INSTRUMENTS } from "@/lib/mental-health/instruments"; + +import { AssessmentHistory } from "./assessment-history"; +import type { AssessmentRow, InstrumentId } from "./types"; + +export function InstrumentDetail({ + instrument, + rows, + onStart, +}: { + instrument: InstrumentId; + /** ALL assessments; the pinned history filters to this instrument. */ + rows: AssessmentRow[]; + /** Start a check-in for this instrument (closes the host sheet). */ + onStart: () => void; +}) { + const { t } = useTranslations(); + const { date: formatDate } = useFormatters(); + const def = INSTRUMENTS[instrument]; + const last = rows.find((r) => r.instrument === instrument); + + return ( +
+ {/* Last result, in the card's label/value grammar — score + band word + on one slot, the relative day on the other. Neutral text, no tint. */} + {last && ( +
+
+ + {t("mentalHealth.lastResult")} + + + {relativeCalendarDate(last.takenAt, t, formatDate)} + +
+
+ + {t("mentalHealth.lastScore")} + + + {last.totalScore} + {" · "} + {t(`mentalHealth.band.${last.instrument}.${last.severityBand}`)} + +
+
+ )} + + {/* The Start action stays reachable here too — the detail is a calm + reading surface, not a dead end. */} + + + + + {/* Required attribution — same factual line as the card + result view. */} +

+ {def.attribution} +

+
+ ); +} diff --git a/src/components/mental-health/mental-wellbeing.tsx b/src/components/mental-health/mental-wellbeing.tsx index ddb75d9c0..267c4e06d 100644 --- a/src/components/mental-health/mental-wellbeing.tsx +++ b/src/components/mental-health/mental-wellbeing.tsx @@ -20,9 +20,12 @@ * cards (last-result line + Start) + a history card. The disclaimer renders * ONLY here (a muted caption) and behind the InfoHint — never while testing. * - * v1.27.6 — the trend chart (and the per-card trend sheet) left this surface; - * the score curve lives in Insights / Measurements via the PHQ9_SCORE / - * GAD7_SCORE measurement rows. The landing keeps the calm dated list only. + * v1.27.9 — the landing is intro + instrument cards, nothing else: the + * combined history card left the main page entirely. Each card opens a + * per-instrument detail sheet (the Vorsorge-/med-card detail interaction) + * carrying last score + band, the trend chart, the dated history, and the + * Start action — the Verlauf is opt-in behind that click, never pushed onto + * the page someone arrives at to take stock. */ import { useMemo, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; @@ -30,12 +33,14 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useTranslations } from "@/lib/i18n/context"; import { apiGet, apiPost } from "@/lib/api/api-fetch"; import { queryKeys } from "@/lib/query-keys"; +import { INSTRUMENTS, INSTRUMENT_ORDER } from "@/lib/mental-health/instruments"; import { PageHeader } from "@/components/ui/page-header"; +import { ResponsiveSheet } from "@/components/ui/responsive-sheet"; -import { AssessmentHistory } from "./assessment-history"; import { AssessmentResult } from "./assessment-result"; import { CheckInWizard } from "./check-in-wizard"; import { InstrumentCard } from "./instrument-card"; +import { InstrumentDetail } from "./instrument-detail"; import type { AssessmentRow, CreateResponse, @@ -50,6 +55,11 @@ export function MentalWellbeing() { const [phase, setPhase] = useState("choose"); const [instrument, setInstrument] = useState("PHQ9"); const [result, setResult] = useState(null); + // Which instrument's detail sheet is open (null = closed). Clicking a card + // body opens THAT instrument's detail; Start stays a separate action. + const [detailInstrument, setDetailInstrument] = useState( + null, + ); const { data: history } = useQuery({ queryKey: queryKeys.mentalHealthAssessments(), @@ -82,6 +92,7 @@ export function MentalWellbeing() { }, [history]); function begin(id: InstrumentId) { + setDetailInstrument(null); setInstrument(id); setResult(null); mutation.reset(); @@ -116,9 +127,10 @@ export function MentalWellbeing() { description={t("mentalHealth.pageDescription")} /> - {/* v1.27.6 — one gentle cadence hint: both instruments ask about - the last two weeks, so every 2–4 weeks is a sensible rhythm. - Body prose (foreground), not another muted description line. */} + {/* v1.27.6 — one gentle cadence hint; v1.27.9 states each recall + window honestly (PHQ-9 / GAD-7 / WHO-5 look back two weeks, the + SCI asks about the past month). Body prose (foreground), not + another muted description line. */}

{t("mentalHealth.pageRhythmHint")}

@@ -126,19 +138,45 @@ export function MentalWellbeing() {

{t("mentalHealth.choosePrompt")}

    - {(["PHQ9", "GAD7"] as InstrumentId[]).map((id) => ( + {INSTRUMENT_ORDER.map((id) => (
  • begin(id)} + onOpenDetail={() => setDetailInstrument(id)} />
  • ))}
- + {/* Per-instrument detail, opened from a card body: last result, + trend chart, dated history, Start — the Verlauf lives here, + not on the landing. */} + { + if (!open) setDetailInstrument(null); + }} + contentWidth="lg" + title={ + detailInstrument + ? t( + `mentalHealth.instrument.${INSTRUMENTS[detailInstrument].i18nKey}`, + ) + : t("mentalHealth.history.title") + } + description={t("mentalHealth.history.chartTitle")} + > + {detailInstrument && ( + begin(detailInstrument)} + /> + )} + )}