From 133b84fa72af463ce2a67fb3a8f6e168564547ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 5 Jul 2026 18:40:59 +0200 Subject: [PATCH] feat(mental-health): ask the PHQ-9 functional follow-up as the last question The PHQ-9's validated functional-impairment item left the web wizard with the review step; the API kept the field but no web client captured it. It returns as a regular question 10 of 10 in the same style as the nine scored items: optional per the instrument (submit stays armed without an answer, a second tap clears the selection, a short hint says skipping is fine) and never scored into the total. The answer rides the existing functionalDifficulty field; GAD-7 keeps its seven steps. The validated wording returns to all six locales. --- messages/de.json | 8 ++ messages/en.json | 8 ++ messages/es.json | 8 ++ messages/fr.json | 8 ++ messages/it.json | 8 ++ messages/pl.json | 8 ++ .../mental-health/check-in-wizard.tsx | 80 +++++++++++++------ .../mental-health/mental-wellbeing.tsx | 10 +-- 8 files changed, 110 insertions(+), 28 deletions(-) diff --git a/messages/de.json b/messages/de.json index 1047b2c5b..23c44b9ca 100644 --- a/messages/de.json +++ b/messages/de.json @@ -7664,6 +7664,14 @@ "2": "An mehr als der Hälfte der Tage", "3": "Beinahe jeden Tag" }, + "functionalTitle": "Wenn du eine oder mehrere Beschwerden angegeben hast: Wie sehr haben diese deine Arbeit, deinen Haushalt oder den Umgang mit anderen Menschen erschwert?", + "functional": { + "0": "Überhaupt nicht erschwert", + "1": "Etwas erschwert", + "2": "Sehr erschwert", + "3": "Extrem erschwert" + }, + "functionalOptionalHint": "Optional — du kannst auch ohne Antwort auf diese Frage absenden.", "result": { "title": "Dein Ergebnis", "totalLabel": "Gesamtwert", diff --git a/messages/en.json b/messages/en.json index 39de8b24b..9c733b7c2 100644 --- a/messages/en.json +++ b/messages/en.json @@ -7664,6 +7664,14 @@ "2": "More than half the days", "3": "Nearly every day" }, + "functionalTitle": "If you checked off any problems, how difficult have these made it to do your work, take care of things at home, or get along with other people?", + "functional": { + "0": "Not difficult at all", + "1": "Somewhat difficult", + "2": "Very difficult", + "3": "Extremely difficult" + }, + "functionalOptionalHint": "Optional — you can submit without answering this question.", "result": { "title": "Your result", "totalLabel": "Total score", diff --git a/messages/es.json b/messages/es.json index ae92a339a..08069dda2 100644 --- a/messages/es.json +++ b/messages/es.json @@ -7664,6 +7664,14 @@ "2": "Más de la mitad de los días", "3": "Casi todos los días" }, + "functionalTitle": "Si marcaste algún problema, ¿cuánta dificultad te han causado para trabajar, ocuparte de la casa o relacionarte con otras personas?", + "functional": { + "0": "Ninguna dificultad", + "1": "Algo de dificultad", + "2": "Mucha dificultad", + "3": "Dificultad extrema" + }, + "functionalOptionalHint": "Opcional: puedes enviar sin responder esta pregunta.", "result": { "title": "Tu resultado", "totalLabel": "Puntuación total", diff --git a/messages/fr.json b/messages/fr.json index 23645cb90..29e16188a 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -7664,6 +7664,14 @@ "2": "Plus de la moitié des jours", "3": "Presque tous les jours" }, + "functionalTitle": "Si tu as coché des problèmes, à quel point t'ont-ils gêné(e) dans ton travail, à la maison ou dans tes relations avec les autres ?", + "functional": { + "0": "Pas du tout gênant", + "1": "Un peu gênant", + "2": "Très gênant", + "3": "Extrêmement gênant" + }, + "functionalOptionalHint": "Facultatif — tu peux envoyer sans répondre à cette question.", "result": { "title": "Ton résultat", "totalLabel": "Score total", diff --git a/messages/it.json b/messages/it.json index 04c1c0381..baafa256b 100644 --- a/messages/it.json +++ b/messages/it.json @@ -7664,6 +7664,14 @@ "2": "Più della metà dei giorni", "3": "Quasi ogni giorno" }, + "functionalTitle": "Se hai indicato dei problemi, quanto ti hanno reso difficile lavorare, occuparti della casa o andare d'accordo con gli altri?", + "functional": { + "0": "Per niente difficile", + "1": "Un po' difficile", + "2": "Molto difficile", + "3": "Estremamente difficile" + }, + "functionalOptionalHint": "Facoltativo — puoi inviare anche senza rispondere a questa domanda.", "result": { "title": "Il tuo risultato", "totalLabel": "Punteggio totale", diff --git a/messages/pl.json b/messages/pl.json index 914bbd61b..d129e4f98 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -7664,6 +7664,14 @@ "2": "Więcej niż połowę dni", "3": "Niemal codziennie" }, + "functionalTitle": "Jeśli zaznaczyłeś(-aś) jakiekolwiek problemy, na ile utrudniły Ci one pracę, prowadzenie domu lub kontakty z innymi ludźmi?", + "functional": { + "0": "Wcale nie utrudniły", + "1": "Trochę utrudniły", + "2": "Bardzo utrudniły", + "3": "Skrajnie utrudniły" + }, + "functionalOptionalHint": "Opcjonalne — możesz wysłać bez odpowiedzi na to pytanie.", "result": { "title": "Twój wynik", "totalLabel": "Wynik łączny", diff --git a/src/components/mental-health/check-in-wizard.tsx b/src/components/mental-health/check-in-wizard.tsx index d7b178857..f5873b1ab 100644 --- a/src/components/mental-health/check-in-wizard.tsx +++ b/src/components/mental-health/check-in-wizard.tsx @@ -47,8 +47,8 @@ export function CheckInWizard({ isError, }: { instrument: InstrumentId; - /** Submit the completed check-in. */ - onSubmit: (items: number[]) => void; + /** Submit the completed check-in (+ the optional PHQ-9 follow-up). */ + onSubmit: (items: number[], functionalDifficulty?: number) => void; /** Return to the landing (abandons the in-progress check-in). */ onBack: () => void; isPending: boolean; @@ -57,15 +57,28 @@ export function CheckInWizard({ const { t } = useTranslations(); const key = lower(instrument); const itemCount = INSTRUMENTS[instrument].itemCount; + // v1.27.8 — the PHQ-9 asks its validated functional-impairment + // follow-up as a regular LAST question (10 of 10) instead of a + // separate review-step widget. It is OPTIONAL per the instrument + // (answering is not required to submit) and never scores into the + // total — the value rides the existing `functionalDifficulty` API + // field. GAD-7 has no such item and keeps its 7 steps. + const hasFunctionalStep = instrument === "PHQ9"; + const totalSteps = itemCount + (hasFunctionalStep ? 1 : 0); const [step, setStep] = useState(1); const [items, setItems] = useState(() => Array(itemCount).fill(UNANSWERED), ); + const [functional, setFunctional] = useState(UNANSWERED); - const onLast = isLastStep(step, itemCount); + const onFunctionalStep = hasFunctionalStep && step === itemCount + 1; + const onLast = isLastStep(step, totalSteps); const questionIndex = step - 1; // 0-based item index for the current question const complete = isComplete(items); + const questionLabel = onFunctionalStep + ? t("mentalHealth.functionalTitle") + : t(`mentalHealth.items.${key}.${step}`); // a11y: announce each step by moving focus to its heading (the question // text). Mirrors the medication dialog's per-step title focus so a @@ -77,6 +90,13 @@ export function CheckInWizard({ }, [step]); function selectAnswer(value: number) { + if (onFunctionalStep) { + // Optional follow-up: selecting never advances (it IS the last + // step) and tapping the selected option again clears it — the + // answer stays skippable right up to submit. + setFunctional((prev) => (prev === value ? UNANSWERED : value)); + return; + } setItems((prev) => { const next = [...prev]; next[questionIndex] = value; @@ -85,7 +105,7 @@ export function CheckInWizard({ // Forward-on-select: advance to the next question. On the last question // the selection stays visible and the footer submit arms — submitting is // a deliberate second tap, never a side effect of answering. - if (!onLast) setStep((s) => nextStep(s, itemCount)); + if (!onLast) setStep((s) => nextStep(s, totalSteps)); } return ( @@ -123,7 +143,7 @@ export function CheckInWizard({

{t("mentalHealth.progress", { current: step, - total: itemCount, + total: totalSteps, })}

- {t(`mentalHealth.items.${key}.${step}`)} + {questionLabel}

+ {onFunctionalStep && ( +

+ {t("mentalHealth.functionalOptionalHint")} +

+ )}
- {SCALE.map((v) => ( - - ))} + {SCALE.map((v) => { + const selected = onFunctionalStep + ? functional === v + : items[questionIndex] === v; + return ( + + ); + })}
{isError && ( @@ -177,7 +209,7 @@ export function CheckInWizard({