mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 13:22:14 +00:00
feat(mental-health): rebuild the test cards on the shared med-card anatomy
The PHQ-9 / GAD-7 cards adopt the medication / Vorsorge card grammar: MedicationCardHeader (bold title + outline category badge), a label/value block carrying "last test" as a relative day and "last result" as score + band word, and a single bottom-pinned Start action. Titles read "PHQ-9 · Stimmung" / "GAD-7 · Sorgen" (middle-dot separator, all six locales) and a new last-result label lands in every locale bundle. The card surface stays neutral regardless of the last band.
This commit is contained in:
1 parent
44bc91c3ea
commit
43251ba59b
8 files changed
+95
-46
No files matched your search
+3
-2
@@ -7636,8 +7636,8 @@
|
||||
"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"
|
||||
"phq9": "PHQ-9 · Stimmung",
|
||||
"gad7": "GAD-7 · Sorgen"
|
||||
},
|
||||
"instrumentSub": {
|
||||
"phq9": "Depressions-Screening, 9 Fragen",
|
||||
@@ -7745,6 +7745,7 @@
|
||||
"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",
|
||||
|
||||
+3
-2
@@ -7636,8 +7636,8 @@
|
||||
"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"
|
||||
"phq9": "PHQ-9 · mood",
|
||||
"gad7": "GAD-7 · worry"
|
||||
},
|
||||
"instrumentSub": {
|
||||
"phq9": "Depression screen, 9 questions",
|
||||
@@ -7745,6 +7745,7 @@
|
||||
"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",
|
||||
|
||||
+3
-2
@@ -7636,8 +7636,8 @@
|
||||
"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"
|
||||
"phq9": "PHQ-9 · ánimo",
|
||||
"gad7": "GAD-7 · preocupación"
|
||||
},
|
||||
"instrumentSub": {
|
||||
"phq9": "Cribado de depresión, 9 preguntas",
|
||||
@@ -7745,6 +7745,7 @@
|
||||
"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",
|
||||
|
||||
+3
-2
@@ -7636,8 +7636,8 @@
|
||||
"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"
|
||||
"phq9": "PHQ-9 · humeur",
|
||||
"gad7": "GAD-7 · inquiétude"
|
||||
},
|
||||
"instrumentSub": {
|
||||
"phq9": "Dépistage de la dépression, 9 questions",
|
||||
@@ -7745,6 +7745,7 @@
|
||||
"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",
|
||||
|
||||
+3
-2
@@ -7636,8 +7636,8 @@
|
||||
"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"
|
||||
"phq9": "PHQ-9 · umore",
|
||||
"gad7": "GAD-7 · preoccupazione"
|
||||
},
|
||||
"instrumentSub": {
|
||||
"phq9": "Screening della depressione, 9 domande",
|
||||
@@ -7745,6 +7745,7 @@
|
||||
"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",
|
||||
|
||||
+3
-2
@@ -7636,8 +7636,8 @@
|
||||
"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"
|
||||
"phq9": "PHQ-9 · nastrój",
|
||||
"gad7": "GAD-7 · lęk"
|
||||
},
|
||||
"instrumentSub": {
|
||||
"phq9": "Przesiew depresji, 9 pytań",
|
||||
@@ -7745,6 +7745,7 @@
|
||||
"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",
|
||||
|
||||
@@ -200,14 +200,42 @@ describe("history rendering", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("instrument card (v1.27.6 — no trend-detail target)", () => {
|
||||
it("renders the card body as plain content with Start as the single action", () => {
|
||||
describe("instrument card (v1.27.6 — med-/Vorsorge card anatomy)", () => {
|
||||
const lastRow: AssessmentRow = {
|
||||
id: "l1",
|
||||
instrument: "PHQ9",
|
||||
locale: "en",
|
||||
totalScore: 8,
|
||||
severityBand: "mild",
|
||||
item9Flagged: false,
|
||||
crisisShownAt: null,
|
||||
takenAt: "2026-06-20T00:00:00.000Z",
|
||||
};
|
||||
|
||||
it("renders the shared med-card header (title + category badge), no trend target", () => {
|
||||
const html = withProviders(
|
||||
<InstrumentCard instrument="PHQ9" last={undefined} onStart={() => {}} />,
|
||||
);
|
||||
// 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"');
|
||||
// …and the Start action remains.
|
||||
// …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);
|
||||
});
|
||||
|
||||
it("shows last test (relative) and last result (score + band word)", () => {
|
||||
const html = withProviders(
|
||||
<InstrumentCard instrument="PHQ9" last={lastRow} onStart={() => {}} />,
|
||||
);
|
||||
expect(html).toContain(mh.lastResult);
|
||||
expect(html).toContain(mh.lastScore);
|
||||
// Score + band word ride one value slot ("8 · Mild").
|
||||
expect(html).toContain('data-slot="instrument-card-last-score"');
|
||||
expect(html).toContain(mh.band.PHQ9.mild);
|
||||
expect(html).not.toContain(mh.noResultYet);
|
||||
});
|
||||
});
|
||||
@@ -3,14 +3,17 @@
|
||||
/**
|
||||
* v1.25.3 — one neutral card per instrument on the landing.
|
||||
*
|
||||
* Mirrors the Vorsorge / medication card grammar: a header (title + sub),
|
||||
* a discreet "last result" line when history exists (band badge + relative
|
||||
* date), and a single bottom-pinned Start action. The surface stays NEUTRAL
|
||||
* v1.27.6 — rebuilt onto the shared med-/Vorsorge card anatomy so the two
|
||||
* 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 a discreet badge only).
|
||||
* through discreet text only, never a card wash).
|
||||
*/
|
||||
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";
|
||||
|
||||
@@ -36,36 +39,48 @@ export function InstrumentCard({
|
||||
const title = t(`mentalHealth.instrument.${key}`);
|
||||
|
||||
return (
|
||||
<Card className="h-full gap-3" data-slot="instrument-card">
|
||||
<CardContent className="flex h-full flex-col space-y-3.5 p-4">
|
||||
{/* v1.27.6 — the card body is no longer a trend-detail target (the
|
||||
trend chart left this surface); the card informs, the Start button
|
||||
below is the single action. */}
|
||||
<div className="flex flex-1 flex-col gap-3.5">
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<span className="text-lg leading-none font-semibold">{title}</span>
|
||||
<span className="text-muted-foreground text-xs">
|
||||
{t(`mentalHealth.instrumentSub.${key}`)}
|
||||
</span>
|
||||
<Card className="h-full gap-3 md:gap-3" data-slot="instrument-card">
|
||||
<MedicationCardHeader
|
||||
name={title}
|
||||
dose=""
|
||||
categoryLabel={t(`mentalHealth.instrumentSub.${key}`)}
|
||||
/>
|
||||
<CardContent className="flex h-full flex-col space-y-3.5">
|
||||
{/* Label/value block in the Vorsorge card's next-last grammar. With
|
||||
no history yet, one calm line replaces the pair — no dashes
|
||||
pretending a value exists. */}
|
||||
{last ? (
|
||||
<div className="min-h-[2.75rem] space-y-1.5 text-sm">
|
||||
<div className="text-muted-foreground flex items-baseline justify-between gap-3">
|
||||
<span className="min-w-0 flex-shrink truncate font-medium">
|
||||
{t("mentalHealth.lastResult")}
|
||||
</span>
|
||||
<span className="text-foreground text-right">
|
||||
{relativeCalendarDate(last.takenAt, t, formatDate)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-muted-foreground flex items-baseline justify-between gap-3">
|
||||
<span className="min-w-0 flex-shrink truncate font-medium">
|
||||
{t("mentalHealth.lastScore")}
|
||||
</span>
|
||||
<span
|
||||
className="text-foreground text-right"
|
||||
data-slot="instrument-card-last-score"
|
||||
>
|
||||
{last.totalScore}
|
||||
{" · "}
|
||||
{t(`mentalHealth.band.${last.instrument}.${last.severityBand}`)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Last-test line in the medication-card grammar: label left, the
|
||||
relative day (today / yesterday / date) right-aligned. */}
|
||||
<div className="text-muted-foreground flex min-h-5 items-center justify-between gap-2 text-xs">
|
||||
{last ? (
|
||||
<>
|
||||
<span>{t("mentalHealth.lastResult")}</span>
|
||||
<span className="shrink-0">
|
||||
{relativeCalendarDate(last.takenAt, t, formatDate)}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<span>{t("mentalHealth.noResultYet")}</span>
|
||||
)}
|
||||
) : (
|
||||
<div className="text-muted-foreground flex min-h-[2.75rem] items-center text-sm">
|
||||
{t("mentalHealth.noResultYet")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="pt-0">
|
||||
{/* Bottom-pinned single primary action — the med-/Vorsorge card slot. */}
|
||||
<div className="mt-auto pt-0">
|
||||
<Button type="button" className="min-h-11 w-full" onClick={onStart}>
|
||||
{t("mentalHealth.start")}
|
||||
</Button>
|
||||
|
||||
Reference in new issue
Block a user