From f7920d81a6a113d5450709f172b3e0086d7fc7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 5 Jul 2026 14:52:10 +0200 Subject: [PATCH] feat(mental-health): slim the check-in to questions only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The question-overview strip (the dot/jump row borrowed from the medication wizard) and the "N of N answered" review interstitial are gone — a screening is a quiet, linear moment, not a form to audit. The Back button and a small "3 / 9" caption stay for orientation; answering the last question arms the submit directly and the next screen is the result view. The optional functional-difficulty follow-up leaves the web wizard with the review step (the API keeps accepting it). Unused wizard keys removed from all six locale bundles. --- messages/de.json | 16 +- messages/en.json | 16 +- messages/es.json | 16 +- messages/fr.json | 16 +- messages/it.json | 16 +- messages/pl.json | 16 +- .../__tests__/check-in-nav.test.ts | 80 ++----- .../__tests__/mental-wellbeing.test.tsx | 11 +- src/components/mental-health/check-in-nav.ts | 57 ++--- .../mental-health/check-in-wizard.tsx | 213 ++++++------------ .../mental-health/mental-wellbeing.tsx | 7 +- 11 files changed, 121 insertions(+), 343 deletions(-) diff --git a/messages/de.json b/messages/de.json index 49f998221..69d71c137 100644 --- a/messages/de.json +++ b/messages/de.json @@ -7650,13 +7650,6 @@ "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" - }, "result": { "title": "Dein Ergebnis", "totalLabel": "Gesamtwert", @@ -7743,18 +7736,11 @@ "gad7": "Angst-Screening mit 7 Fragen. Beziehe dich auf die letzten zwei Wochen." }, "progress": "{current} / {total}", - "questionAria": "Frage {current} von {total}", "next": "Weiter", "lastResult": "Letzter Test", "lastScore": "Letztes Ergebnis", "noResultYet": "Noch kein Check-in", - "backToList": "Übersicht", - "jumpFirst": "Zur ersten Frage springen", - "jumpLast": "Zur letzten Frage springen", - "review": { - "title": "Überblick", - "recap": "Du hast {answered} von {total} Fragen beantwortet." - } + "backToList": "Übersicht" }, "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 aec969447..2f1811cdf 100644 --- a/messages/en.json +++ b/messages/en.json @@ -7650,13 +7650,6 @@ "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" - }, "result": { "title": "Your result", "totalLabel": "Total score", @@ -7743,18 +7736,11 @@ "gad7": "7-question anxiety screen. Answer for the last two weeks." }, "progress": "{current} / {total}", - "questionAria": "Question {current} of {total}", "next": "Next", "lastResult": "Last test", "lastScore": "Last result", "noResultYet": "No check-in yet", - "backToList": "Overview", - "jumpFirst": "Jump to first question", - "jumpLast": "Jump to last question", - "review": { - "title": "Review", - "recap": "You answered {answered} of {total} questions." - } + "backToList": "Overview" }, "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 2174a68d1..731843472 100644 --- a/messages/es.json +++ b/messages/es.json @@ -7650,13 +7650,6 @@ "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" - }, "result": { "title": "Tu resultado", "totalLabel": "Puntuación total", @@ -7743,18 +7736,11 @@ "gad7": "Cribado de ansiedad de 7 preguntas. Responde pensando en las últimas dos semanas." }, "progress": "{current} / {total}", - "questionAria": "Pregunta {current} de {total}", "next": "Siguiente", "lastResult": "Última prueba", "lastScore": "Último resultado", "noResultYet": "Aún sin check-in", - "backToList": "Vista general", - "jumpFirst": "Ir a la primera pregunta", - "jumpLast": "Ir a la última pregunta", - "review": { - "title": "Resumen", - "recap": "Has respondido {answered} de {total} preguntas." - } + "backToList": "Vista general" }, "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 e80ea7316..12ffdd534 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -7650,13 +7650,6 @@ "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" - }, "result": { "title": "Ton résultat", "totalLabel": "Score total", @@ -7743,18 +7736,11 @@ "gad7": "Dépistage de l'anxiété en 7 questions. Répondez en pensant aux deux dernières semaines." }, "progress": "{current} / {total}", - "questionAria": "Question {current} sur {total}", "next": "Suivant", "lastResult": "Dernier test", "lastScore": "Dernier résultat", "noResultYet": "Pas encore de check-in", - "backToList": "Vue d'ensemble", - "jumpFirst": "Aller à la première question", - "jumpLast": "Aller à la dernière question", - "review": { - "title": "Récapitulatif", - "recap": "Vous avez répondu à {answered} questions sur {total}." - } + "backToList": "Vue d'ensemble" }, "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 759817a6d..15225f0b5 100644 --- a/messages/it.json +++ b/messages/it.json @@ -7650,13 +7650,6 @@ "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" - }, "result": { "title": "Il tuo risultato", "totalLabel": "Punteggio totale", @@ -7743,18 +7736,11 @@ "gad7": "Screening dell'ansia in 7 domande. Rispondi pensando alle ultime due settimane." }, "progress": "{current} / {total}", - "questionAria": "Domanda {current} di {total}", "next": "Avanti", "lastResult": "Ultimo test", "lastScore": "Ultimo risultato", "noResultYet": "Ancora nessun check-in", - "backToList": "Panoramica", - "jumpFirst": "Vai alla prima domanda", - "jumpLast": "Vai all'ultima domanda", - "review": { - "title": "Riepilogo", - "recap": "Hai risposto a {answered} domande su {total}." - } + "backToList": "Panoramica" }, "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 6e376b390..72c898efe 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -7650,13 +7650,6 @@ "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" - }, "result": { "title": "Twój wynik", "totalLabel": "Wynik łączny", @@ -7743,18 +7736,11 @@ "gad7": "Przesiew w kierunku lęku, 7 pytań. Odpowiadaj, myśląc o ostatnich dwóch tygodniach." }, "progress": "{current} / {total}", - "questionAria": "Pytanie {current} z {total}", "next": "Dalej", "lastResult": "Ostatni test", "lastScore": "Ostatni wynik", "noResultYet": "Brak check-inów", - "backToList": "Przegląd", - "jumpFirst": "Przejdź do pierwszego pytania", - "jumpLast": "Przejdź do ostatniego pytania", - "review": { - "title": "Podsumowanie", - "recap": "Odpowiedziano na {answered} z {total} pytań." - } + "backToList": "Przegląd" }, "records": { "disclaimer": "To Twoje własne wpisy, a nie diagnoza lekarska. HealthLog nie diagnozuje, nie leczy i ich nie interpretuje.", diff --git a/src/components/mental-health/__tests__/check-in-nav.test.ts b/src/components/mental-health/__tests__/check-in-nav.test.ts index 9e0d4cdd1..784690c16 100644 --- a/src/components/mental-health/__tests__/check-in-nav.test.ts +++ b/src/components/mental-health/__tests__/check-in-nav.test.ts @@ -2,23 +2,19 @@ import { describe, it, expect } from "vitest"; import { UNANSWERED, - answeredCount, - buildStepList, isComplete, - isReviewStep, + isLastStep, nextStep, prevStep, - reachableUntilIndex, - reviewStep, } from "../check-in-nav"; /** - * The one-question-at-a-time check-in is LINEAR: the step list is - * `[1..itemCount, review]`, backward navigation is always allowed, and the - * forward ceiling never lets a user skip ahead onto an unanswered question. - * These are the contracts the wizard's advance / back / reachability behaviour - * rests on (the SSR-only test convention can't drive the clicks, so the pure - * grammar is pinned here — the medication wizard's `wizard-payload` precedent). + * The one-question-at-a-time check-in is LINEAR: the steps are exactly the + * questions `1..itemCount`, backward navigation is always allowed, and the + * last question arms the submit directly (v1.27.6 — no review/summary step). + * These are the contracts the wizard's advance / back behaviour rests on + * (the SSR-only test convention can't drive the clicks, so the pure grammar + * is pinned here — the medication wizard's `wizard-payload` precedent). */ const PHQ9 = 9; @@ -28,71 +24,35 @@ function blank(count: number): number[] { return Array(count).fill(UNANSWERED); } -describe("check-in step grammar", () => { - it("builds questions + a single review dot (itemCount + 1)", () => { - expect(buildStepList(PHQ9)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - expect(buildStepList(GAD7)).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); - expect(reviewStep(PHQ9)).toBe(10); - expect(reviewStep(GAD7)).toBe(8); - }); - - it("advances forward on a question and into review from the last question", () => { +describe("check-in step grammar (v1.27.6 — questions only, no review step)", () => { + it("advances forward and stays put on the last question (submit, not advance)", () => { expect(nextStep(1, PHQ9)).toBe(2); expect(nextStep(8, PHQ9)).toBe(9); - // last question (9) → review (10) - expect(nextStep(9, PHQ9)).toBe(10); - // review stays put (submit, not advance) - expect(nextStep(10, PHQ9)).toBe(10); + // The last question is the end of the line — no review step behind it. + expect(nextStep(9, PHQ9)).toBe(9); + expect(nextStep(7, GAD7)).toBe(7); }); it("steps back, clamped at the first question", () => { - expect(prevStep(10)).toBe(9); + expect(prevStep(9)).toBe(8); expect(prevStep(2)).toBe(1); expect(prevStep(1)).toBe(1); }); - it("identifies the review step", () => { - expect(isReviewStep(9, PHQ9)).toBe(false); - expect(isReviewStep(10, PHQ9)).toBe(true); - expect(isReviewStep(7, GAD7)).toBe(false); - expect(isReviewStep(8, GAD7)).toBe(true); + it("identifies the last question per instrument", () => { + expect(isLastStep(8, PHQ9)).toBe(false); + expect(isLastStep(9, PHQ9)).toBe(true); + expect(isLastStep(6, GAD7)).toBe(false); + expect(isLastStep(7, GAD7)).toBe(true); }); }); -describe("reachability ceiling (never skip ahead to an unanswered question)", () => { - it("with nothing answered, only the first question is reachable", () => { - expect(reachableUntilIndex(blank(PHQ9))).toBe(0); - }); - - it("the frontier follows the first unanswered item", () => { - const items = blank(PHQ9); - items[0] = 2; // Q1 answered → can reach Q2 (index 1) - expect(reachableUntilIndex(items)).toBe(1); - items[1] = 0; - items[2] = 3; // Q3 answered too → frontier at the first gap (index 3) - expect(reachableUntilIndex(items)).toBe(3); - }); - - it("once every item is answered the review slot (last index) is reachable", () => { - const items = blank(GAD7).map(() => 1); - // step list length is itemCount + 1; review index == itemCount - expect(reachableUntilIndex(items)).toBe(GAD7); - expect(buildStepList(GAD7).length - 1).toBe(GAD7); - }); -}); - -describe("completeness + recap", () => { +describe("completeness (gates the submit on the last question)", () => { it("isComplete requires every item answered", () => { expect(isComplete(blank(PHQ9))).toBe(false); const partial = blank(PHQ9).map((_, i) => (i < 5 ? 1 : UNANSWERED)); expect(isComplete(partial)).toBe(false); expect(isComplete(blank(PHQ9).map(() => 0))).toBe(true); - }); - - it("answeredCount counts only real (0–3) answers", () => { - const items = blank(PHQ9); - items[0] = 0; - items[1] = 3; - expect(answeredCount(items)).toBe(2); + expect(isComplete(blank(GAD7).map(() => 3))).toBe(true); }); }); diff --git a/src/components/mental-health/__tests__/mental-wellbeing.test.tsx b/src/components/mental-health/__tests__/mental-wellbeing.test.tsx index 8ddfcdfa1..0c6868b49 100644 --- a/src/components/mental-health/__tests__/mental-wellbeing.test.tsx +++ b/src/components/mental-health/__tests__/mental-wellbeing.test.tsx @@ -75,7 +75,7 @@ describe("landing chrome (v1.25.5 — disclaimer + heading removed)", () => { }); }); -describe("check-in wizard scaffolding", () => { +describe("check-in wizard scaffolding (v1.27.6 — questions only)", () => { const html = withProviders( { />, ); - it("reuses the medication wizard stepper", () => { - expect(html).toContain('data-slot="wizard-stepper"'); + it("no longer renders the question-overview strip", () => { + expect(html).not.toContain('data-slot="wizard-stepper"'); + }); + + it("no longer renders the review recap or the functional follow-up", () => { + expect(html).not.toContain("You have answered"); + expect(html).not.toContain('data-slot="check-in-submit"'); }); it("opens on the first question with the Back/Next footer", () => { diff --git a/src/components/mental-health/check-in-nav.ts b/src/components/mental-health/check-in-nav.ts index 125d04a6b..dbe4fe983 100644 --- a/src/components/mental-health/check-in-nav.ts +++ b/src/components/mental-health/check-in-nav.ts @@ -2,61 +2,32 @@ * v1.25.3 — pure step grammar for the one-question-at-a-time check-in. * * The PHQ-9 / GAD-7 screeners are LINEAR (no mode/cadence branching like the - * medication wizard), so the step list is a fixed `[1..itemCount, review]` and - * the reachability ceiling is just "the question you are currently working on" - * — you can always step backward to change an answer, never skip ahead onto an - * unanswered question. Keeping the navigation here (mirroring the medication - * wizard's `wizard-payload.ts`) makes the advance/back/reachability contract + * medication wizard), so the steps are exactly the questions `1..itemCount`: + * you can always step backward to change an answer, and never skip ahead past + * an unanswered question. Keeping the navigation here (mirroring the + * medication wizard's `wizard-payload.ts`) makes the advance/back contract * unit-testable without driving a DOM. * - * Step numbers are 1-based and run `1..itemCount` for the questions; the review - * step is `itemCount + 1`. The optional functional-difficulty control folds - * into the review step (it is unscored), so the dot count stays at - * `itemCount + 1` rather than gaining its own dot. + * v1.27.6 — the review/summary step is gone: answering the last question + * arms the submit directly (no "9 of 9 answered" interstitial demanding one + * more decision). Step numbers are 1-based and run `1..itemCount`. * * An unanswered item carries the sentinel `-1`. */ export const UNANSWERED = -1; -/** The 1-based review step number for an instrument of `itemCount` items. */ -export function reviewStep(itemCount: number): number { - return itemCount + 1; -} - -/** Ordered raw step numbers fed straight to `WizardStepper`: questions + review. */ -export function buildStepList(itemCount: number): number[] { - return Array.from({ length: itemCount + 1 }, (_, i) => i + 1); -} - -/** Index (into the step list) of the highest forward-reachable slot. */ -export function reachableUntilIndex(items: readonly number[]): number { - const firstUnanswered = items.findIndex((v) => v < 0); - // All answered → the review slot (last index) is reachable. - if (firstUnanswered === -1) return items.length; - // Otherwise the frontier is the question currently being answered; its index - // in the step list equals its 0-based item index. - return firstUnanswered; -} - -/** Every item answered (0–3) — gates the review step + submit. */ +/** Every item answered (0–3) — gates the submit on the last question. */ export function isComplete(items: readonly number[]): boolean { return items.length > 0 && items.every((v) => v >= 0); } -/** How many items carry a real answer (for the review recap). */ -export function answeredCount(items: readonly number[]): number { - return items.filter((v) => v >= 0).length; -} - /** - * The next step after `step`. A question advances to the next question, the - * last question advances to the review step, and the review step stays put - * (submit, not advance). + * The next step after `step`: a question advances to the next question; the + * last question stays put (submit, not advance). */ export function nextStep(step: number, itemCount: number): number { - const review = reviewStep(itemCount); - if (step >= review) return review; + if (step >= itemCount) return itemCount; return step + 1; } @@ -65,7 +36,7 @@ export function prevStep(step: number): number { return step > 1 ? step - 1 : 1; } -/** Whether `step` is the review step for this instrument. */ -export function isReviewStep(step: number, itemCount: number): boolean { - return step >= reviewStep(itemCount); +/** Whether `step` is the last question for this instrument. */ +export function isLastStep(step: number, itemCount: number): boolean { + return step >= itemCount; } diff --git a/src/components/mental-health/check-in-wizard.tsx b/src/components/mental-health/check-in-wizard.tsx index 8d337b859..d7b178857 100644 --- a/src/components/mental-health/check-in-wizard.tsx +++ b/src/components/mental-health/check-in-wizard.tsx @@ -1,44 +1,39 @@ "use client"; /** - * v1.25.3 — one-question-at-a-time check-in. + * v1.25.3 — one-question-at-a-time check-in, rendered INLINE on the page + * (not a modal), because here the page *is* the surface. * - * Replaces the all-questions-at-once `
    ` wall with a linear stepper that - * reuses the medication wizard's presentational `WizardStepper` (the dot row + - * "Question 3 of 9" caption) and its Back/Next footer grammar — rendered INLINE - * on the page (not a modal), because here the page *is* the surface. The step - * navigation lives in `check-in-nav.ts` (pure, linear: backward always allowed, - * never skip ahead to an unanswered question). + * v1.27.6 — slimmed to questions only. The question-overview strip (the + * dot/jump row borrowed from the medication wizard) and the "N of N + * answered" review interstitial are gone: a screening is a quiet, linear + * moment, not a form to audit. The Back button stays; a small "3 / 9" + * caption keeps orientation without turning progress into a scoreboard. + * Answering the last question arms the submit directly — the next thing + * the user sees is the dignified result view. * - * Header: instrument title + ONE standardized explanation line, NO disclaimer - * (§2 — the "voluntary self-test, not a diagnosis" disclaimer renders only on - * the landing + behind the InfoHint, never while taking the test). The optional - * functional-difficulty control folds into the review step. + * Header: instrument title + ONE standardized explanation line, NO + * disclaimer (§2 — the "voluntary self-test, not a diagnosis" disclaimer + * renders only on the landing, never while taking the test). */ import { useEffect, useRef, useState } from "react"; import { ArrowLeft } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; -import { WizardStepper } from "@/components/medications/wizard/wizard-stepper"; import { useTranslations } from "@/lib/i18n/context"; -import { cn } from "@/lib/utils"; import { INSTRUMENTS } from "@/lib/mental-health/instruments"; import { - UNANSWERED, - answeredCount, - buildStepList, isComplete, - isReviewStep, + isLastStep, nextStep, prevStep, - reachableUntilIndex, + UNANSWERED, } from "./check-in-nav"; import type { InstrumentId } from "./types"; const SCALE = [0, 1, 2, 3] as const; -const FUNCTIONAL = [0, 1, 2, 3] as const; function lower(id: InstrumentId): "phq9" | "gad7" { return id === "PHQ9" ? "phq9" : "gad7"; @@ -52,8 +47,8 @@ export function CheckInWizard({ isError, }: { instrument: InstrumentId; - /** Submit the completed check-in. `functional` is null when unanswered. */ - onSubmit: (items: number[], functional: number | null) => void; + /** Submit the completed check-in. */ + onSubmit: (items: number[]) => void; /** Return to the landing (abandons the in-progress check-in). */ onBack: () => void; isPending: boolean; @@ -67,17 +62,14 @@ export function CheckInWizard({ const [items, setItems] = useState(() => Array(itemCount).fill(UNANSWERED), ); - const [functional, setFunctional] = useState(null); - const stepList = buildStepList(itemCount); - const onReview = isReviewStep(step, itemCount); - const questionIndex = step - 1; // 0-based item index for a question step + const onLast = isLastStep(step, itemCount); + const questionIndex = step - 1; // 0-based item index for the current question const complete = isComplete(items); - const reachableUntil = reachableUntilIndex(items); - // a11y: announce each step by moving focus to its heading (the question text - // / the review title). Mirrors the medication dialog's per-step title focus - // so a keyboard / screen-reader user hears the new question after an + // a11y: announce each step by moving focus to its heading (the question + // text). Mirrors the medication dialog's per-step title focus so a + // keyboard / screen-reader user hears the new question after an // auto-advance, not silence. const headingRef = useRef(null); useEffect(() => { @@ -90,15 +82,12 @@ export function CheckInWizard({ next[questionIndex] = value; return next; }); - // Forward-on-select: advance to the next question, or onto the review step - // from the last question. The footer Next is the redundant keyboard path. - setStep((s) => nextStep(s, itemCount)); + // 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)); } - const srLabel = onReview - ? t("mentalHealth.review.title") - : t("mentalHealth.questionAria", { current: step, total: itemCount }); - return (
    {/* In-test header — instrument title + ONE standardized explanation line. - No disclaimer here (§2); the (i) tooltip on the landing carries it. */} + No disclaimer here (§2); the landing carries it. */}

    {t(`mentalHealth.instrument.${key}`)} @@ -129,114 +118,48 @@ export function CheckInWizard({

    -
    - [ - i + 1, - String(i + 1), - ]), - [itemCount + 1, t("mentalHealth.review.title")], - ])} - onJump={(target) => setStep(target)} - onFirst={() => setStep(1)} - onLast={() => setStep(itemCount + 1)} - firstEnabled={step > 1} - lastEnabled={complete && !onReview} - firstLabel={t("mentalHealth.jumpFirst")} - lastLabel={t("mentalHealth.jumpLast")} - srLabel={srLabel} - /> -
    - - {!onReview ? ( -
    -

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

    -

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

    -
    - {SCALE.map((v) => ( - - ))} -
    -
    - ) : ( -
    -

    - {t("mentalHealth.review.title")} -

    -

    - {t("mentalHealth.review.recap", { - answered: answeredCount(items), - total: itemCount, - })} -

    - - {/* The optional, unscored functional-difficulty follow-up folds - into the review step rather than its own dot (§1). */} -
    - - {t("mentalHealth.functionalTitle")} - -
    +

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

    +

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

    +
    + {SCALE.map((v) => ( + - ))} -
    -
    - - {isError && ( -

    - {t("mentalHealth.error")} -

    - )} + {t(`mentalHealth.options.${v}`)} + + ))}
    - )} + + {isError && ( +

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

    + )} +
    @@ -250,7 +173,7 @@ export function CheckInWizard({ > {t("mentalHealth.back")} - {!onReview ? ( + {!onLast ? (