mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
insights(mood): order classification first, route every tile through TileHeader
Reorder the mood subpage spine so the classification tiles (in-target share + stability band) lead, then the better-days assessment, then the descriptive breakdown sections. The classification answers where the user stands; the better-days board, directly beneath, answers what associates with their good days. Every mood card header now routes through the shared TileHeader (icon + white heading) so the subpage speaks one card language. The in-target and stability tiles gain proper headings (new inTargetTitle / stability.title keys across all six locales) and drop the icon-in-a-circle headline glyph; the narrative feed and the discovered-relations board move into headed cards. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
85c9f3ff3f
commit
fcd8eccf6e
10 files changed
+167
-113
No files matched your search
+7
-8
@@ -2583,20 +2583,19 @@
|
||||
"bandVerySteady": "Deine Stimmung war sehr gleichmäßig",
|
||||
"bandSteady": "Deine Stimmung war recht gleichmäßig",
|
||||
"bandVariable": "Deine Stimmung war merklich schwankend",
|
||||
"bandVeryVariable": "Deine Stimmung hat zuletzt stark geschwankt"
|
||||
"bandVeryVariable": "Deine Stimmung hat zuletzt stark geschwankt",
|
||||
"title": "Stabilität"
|
||||
},
|
||||
"influence": {
|
||||
"title": "Einfluss auf die Stimmung",
|
||||
"detail": "Durchschnitt {withAvg} an den {withDays} Tagen mit diesem Tag, {withoutAvg} ohne.",
|
||||
"confidenceLow": "Geringe Sicherheit",
|
||||
"confidenceMedium": "Mittlere Sicherheit",
|
||||
"confidenceHigh": "Hohe Sicherheit",
|
||||
"disclaimer": "Das sind Zusammenhänge in deinen eigenen Daten, kein Beweis für eine Ursache. Muster, die einen Blick wert sind."
|
||||
"confidenceHigh": "Hohe Sicherheit"
|
||||
},
|
||||
"betterDays": {
|
||||
"title": "Was zu deinen besseren Tagen passt",
|
||||
"description": "Tags und Werte, die mit deinen Tagen besserer und schlechterer Stimmung einhergehen, sortiert danach, wie stark sie sich zusammen bewegen.",
|
||||
"disclaimer": "Nur Zusammenhänge, keine Ursachen. Sprich mit deiner Ärztin oder deinem Arzt, bevor du etwas änderst."
|
||||
"description": "Tags und Werte, die mit deinen Tagen besserer und schlechterer Stimmung einhergehen, sortiert danach, wie stark sie sich zusammen bewegen."
|
||||
},
|
||||
"crosstab": {
|
||||
"title": "Tags und deine Gesundheitswerte",
|
||||
@@ -2609,8 +2608,7 @@
|
||||
"unitKcal": "kcal",
|
||||
"unitScore": "Pkt.",
|
||||
"detailSameDay": "Durchschnittlich {withAvg} {unit} an den {withDays} Tagen mit diesem Tag gegenüber {withoutAvg} {unit} ohne.",
|
||||
"detailNextDay": "Durchschnittlich {withAvg} {unit} am Tag nach den {withDays} Tagen mit diesem Tag gegenüber {withoutAvg} {unit} sonst.",
|
||||
"disclaimer": "Das sind Zusammenhänge in deinen eigenen Daten, kein Beweis für Ursachen. Muster, die einen Blick wert sind."
|
||||
"detailNextDay": "Durchschnittlich {withAvg} {unit} am Tag nach den {withDays} Tagen mit diesem Tag gegenüber {withoutAvg} {unit} sonst."
|
||||
},
|
||||
"discovery": {
|
||||
"title": "Was auffällt",
|
||||
@@ -2625,7 +2623,8 @@
|
||||
"stat": "{n} gepaarte Tage · r = {r} · q = {q}",
|
||||
"statLabel": "Wie das berechnet wurde",
|
||||
"explainerLabel": "Wie das berechnet wurde"
|
||||
}
|
||||
},
|
||||
"inTargetTitle": "Im Zielbereich"
|
||||
},
|
||||
"derived": {
|
||||
"coverage": {
|
||||
|
||||
+7
-8
@@ -2583,20 +2583,19 @@
|
||||
"bandVerySteady": "Your mood has been very steady",
|
||||
"bandSteady": "Your mood has been fairly steady",
|
||||
"bandVariable": "Your mood has been noticeably variable",
|
||||
"bandVeryVariable": "Your mood has swung a lot lately"
|
||||
"bandVeryVariable": "Your mood has swung a lot lately",
|
||||
"title": "Stability"
|
||||
},
|
||||
"influence": {
|
||||
"title": "Influence on mood",
|
||||
"detail": "Averages {withAvg} on the {withDays} days you tagged this vs {withoutAvg} without it.",
|
||||
"confidenceLow": "Low confidence",
|
||||
"confidenceMedium": "Medium confidence",
|
||||
"confidenceHigh": "High confidence",
|
||||
"disclaimer": "These are associations in your own data, not proof of cause. Patterns worth watching."
|
||||
"confidenceHigh": "High confidence"
|
||||
},
|
||||
"betterDays": {
|
||||
"title": "What goes with your better days",
|
||||
"description": "Tags and metrics that line up with your higher- and lower-mood days, ranked by how strongly they move together.",
|
||||
"disclaimer": "Associations only, not causes. Talk to your doctor before changing anything based on these."
|
||||
"description": "Tags and metrics that line up with your higher- and lower-mood days, ranked by how strongly they move together."
|
||||
},
|
||||
"crosstab": {
|
||||
"title": "Tags and your health metrics",
|
||||
@@ -2609,8 +2608,7 @@
|
||||
"unitKcal": "kcal",
|
||||
"unitScore": "pts",
|
||||
"detailSameDay": "Averages {withAvg} {unit} on the {withDays} days you tagged this vs {withoutAvg} {unit} without it.",
|
||||
"detailNextDay": "Averages {withAvg} {unit} the day after the {withDays} days you tagged this vs {withoutAvg} {unit} otherwise.",
|
||||
"disclaimer": "These are associations in your own data, not proof of cause. Patterns worth watching."
|
||||
"detailNextDay": "Averages {withAvg} {unit} the day after the {withDays} days you tagged this vs {withoutAvg} {unit} otherwise."
|
||||
},
|
||||
"discovery": {
|
||||
"title": "What stands out",
|
||||
@@ -2625,7 +2623,8 @@
|
||||
"stat": "{n} paired days · r = {r} · q = {q}",
|
||||
"statLabel": "How this was computed",
|
||||
"explainerLabel": "How this was computed"
|
||||
}
|
||||
},
|
||||
"inTargetTitle": "In your range"
|
||||
},
|
||||
"derived": {
|
||||
"coverage": {
|
||||
|
||||
+7
-8
@@ -2583,20 +2583,19 @@
|
||||
"bandVerySteady": "Tu estado de ánimo ha sido muy estable",
|
||||
"bandSteady": "Tu estado de ánimo ha sido bastante estable",
|
||||
"bandVariable": "Tu estado de ánimo ha sido notablemente variable",
|
||||
"bandVeryVariable": "Tu estado de ánimo ha fluctuado mucho últimamente"
|
||||
"bandVeryVariable": "Tu estado de ánimo ha fluctuado mucho últimamente",
|
||||
"title": "Estabilidad"
|
||||
},
|
||||
"influence": {
|
||||
"title": "Influencia en el estado de ánimo",
|
||||
"detail": "Promedio de {withAvg} en los {withDays} días que lo etiquetaste, frente a {withoutAvg} sin él.",
|
||||
"confidenceLow": "Confianza baja",
|
||||
"confidenceMedium": "Confianza media",
|
||||
"confidenceHigh": "Confianza alta",
|
||||
"disclaimer": "Son asociaciones en tus propios datos, no una prueba de causa. Patrones que vale la pena observar."
|
||||
"confidenceHigh": "Confianza alta"
|
||||
},
|
||||
"betterDays": {
|
||||
"title": "Qué acompaña a tus mejores días",
|
||||
"description": "Etiquetas y métricas que coinciden con tus días de mejor y peor ánimo, ordenadas por la fuerza con que se mueven juntas.",
|
||||
"disclaimer": "Solo asociaciones, no causas. Consulta a tu médico antes de cambiar nada por esto."
|
||||
"description": "Etiquetas y métricas que coinciden con tus días de mejor y peor ánimo, ordenadas por la fuerza con que se mueven juntas."
|
||||
},
|
||||
"crosstab": {
|
||||
"title": "Etiquetas y tus métricas de salud",
|
||||
@@ -2609,8 +2608,7 @@
|
||||
"unitKcal": "kcal",
|
||||
"unitScore": "pts",
|
||||
"detailSameDay": "Promedia {withAvg} {unit} en los {withDays} días que lo etiquetaste frente a {withoutAvg} {unit} sin él.",
|
||||
"detailNextDay": "Promedia {withAvg} {unit} el día después de los {withDays} días que lo etiquetaste frente a {withoutAvg} {unit} el resto.",
|
||||
"disclaimer": "Son asociaciones en tus propios datos, no prueba de causa. Patrones que vale la pena observar."
|
||||
"detailNextDay": "Promedia {withAvg} {unit} el día después de los {withDays} días que lo etiquetaste frente a {withoutAvg} {unit} el resto."
|
||||
},
|
||||
"discovery": {
|
||||
"title": "Lo que destaca",
|
||||
@@ -2625,7 +2623,8 @@
|
||||
"stat": "{n} días emparejados · r = {r} · q = {q}",
|
||||
"statLabel": "Cómo se calculó esto",
|
||||
"explainerLabel": "Cómo se calculó esto"
|
||||
}
|
||||
},
|
||||
"inTargetTitle": "En tu rango"
|
||||
},
|
||||
"derived": {
|
||||
"coverage": {
|
||||
|
||||
+7
-8
@@ -2583,20 +2583,19 @@
|
||||
"bandVerySteady": "Votre humeur a été très stable",
|
||||
"bandSteady": "Votre humeur a été plutôt stable",
|
||||
"bandVariable": "Votre humeur a été nettement variable",
|
||||
"bandVeryVariable": "Votre humeur a beaucoup fluctué récemment"
|
||||
"bandVeryVariable": "Votre humeur a beaucoup fluctué récemment",
|
||||
"title": "Stabilité"
|
||||
},
|
||||
"influence": {
|
||||
"title": "Influence sur l'humeur",
|
||||
"detail": "Moyenne de {withAvg} les {withDays} jours où vous l'avez indiqué, contre {withoutAvg} sans.",
|
||||
"confidenceLow": "Confiance faible",
|
||||
"confidenceMedium": "Confiance moyenne",
|
||||
"confidenceHigh": "Confiance élevée",
|
||||
"disclaimer": "Ce sont des associations dans vos propres données, pas une preuve de cause. Des tendances à surveiller."
|
||||
"confidenceHigh": "Confiance élevée"
|
||||
},
|
||||
"betterDays": {
|
||||
"title": "Ce qui accompagne vos meilleurs jours",
|
||||
"description": "Les tags et mesures qui coïncident avec vos jours de meilleure et de moins bonne humeur, classés selon la force du lien.",
|
||||
"disclaimer": "Des associations seulement, pas des causes. Parlez-en à votre médecin avant de changer quoi que ce soit."
|
||||
"description": "Les tags et mesures qui coïncident avec vos jours de meilleure et de moins bonne humeur, classés selon la force du lien."
|
||||
},
|
||||
"crosstab": {
|
||||
"title": "Tags et vos métriques de santé",
|
||||
@@ -2609,8 +2608,7 @@
|
||||
"unitKcal": "kcal",
|
||||
"unitScore": "pts",
|
||||
"detailSameDay": "En moyenne {withAvg} {unit} les {withDays} jours avec ce tag contre {withoutAvg} {unit} sans.",
|
||||
"detailNextDay": "En moyenne {withAvg} {unit} le lendemain des {withDays} jours avec ce tag contre {withoutAvg} {unit} sinon.",
|
||||
"disclaimer": "Ce sont des associations dans vos propres données, pas une preuve de cause. Des tendances à surveiller."
|
||||
"detailNextDay": "En moyenne {withAvg} {unit} le lendemain des {withDays} jours avec ce tag contre {withoutAvg} {unit} sinon."
|
||||
},
|
||||
"discovery": {
|
||||
"title": "Ce qui ressort",
|
||||
@@ -2625,7 +2623,8 @@
|
||||
"stat": "{n} jours appariés · r = {r} · q = {q}",
|
||||
"statLabel": "Comment ceci est calculé",
|
||||
"explainerLabel": "Comment ceci est calculé"
|
||||
}
|
||||
},
|
||||
"inTargetTitle": "Dans votre plage"
|
||||
},
|
||||
"derived": {
|
||||
"coverage": {
|
||||
|
||||
+7
-8
@@ -2583,20 +2583,19 @@
|
||||
"bandVerySteady": "Il tuo umore è stato molto stabile",
|
||||
"bandSteady": "Il tuo umore è stato abbastanza stabile",
|
||||
"bandVariable": "Il tuo umore è stato notevolmente variabile",
|
||||
"bandVeryVariable": "Il tuo umore ha oscillato molto di recente"
|
||||
"bandVeryVariable": "Il tuo umore ha oscillato molto di recente",
|
||||
"title": "Stabilità"
|
||||
},
|
||||
"influence": {
|
||||
"title": "Influenza sull'umore",
|
||||
"detail": "Media di {withAvg} nei {withDays} giorni in cui l'hai indicato, contro {withoutAvg} senza.",
|
||||
"confidenceLow": "Affidabilità bassa",
|
||||
"confidenceMedium": "Affidabilità media",
|
||||
"confidenceHigh": "Affidabilità alta",
|
||||
"disclaimer": "Sono associazioni nei tuoi dati, non una prova di causa. Schemi da tenere d'occhio."
|
||||
"confidenceHigh": "Affidabilità alta"
|
||||
},
|
||||
"betterDays": {
|
||||
"title": "Cosa accompagna le tue giornate migliori",
|
||||
"description": "Tag e parametri che coincidono con le tue giornate di umore migliore e peggiore, ordinati per quanto si muovono insieme.",
|
||||
"disclaimer": "Solo associazioni, non cause. Parla con il tuo medico prima di cambiare qualcosa."
|
||||
"description": "Tag e parametri che coincidono con le tue giornate di umore migliore e peggiore, ordinati per quanto si muovono insieme."
|
||||
},
|
||||
"crosstab": {
|
||||
"title": "Tag e le tue metriche di salute",
|
||||
@@ -2609,8 +2608,7 @@
|
||||
"unitKcal": "kcal",
|
||||
"unitScore": "pt",
|
||||
"detailSameDay": "In media {withAvg} {unit} nei {withDays} giorni con questo tag contro {withoutAvg} {unit} senza.",
|
||||
"detailNextDay": "In media {withAvg} {unit} il giorno dopo i {withDays} giorni con questo tag contro {withoutAvg} {unit} altrimenti.",
|
||||
"disclaimer": "Sono associazioni nei tuoi dati, non prova di causa. Schemi che vale la pena osservare."
|
||||
"detailNextDay": "In media {withAvg} {unit} il giorno dopo i {withDays} giorni con questo tag contro {withoutAvg} {unit} altrimenti."
|
||||
},
|
||||
"discovery": {
|
||||
"title": "Cosa risalta",
|
||||
@@ -2625,7 +2623,8 @@
|
||||
"stat": "{n} giorni appaiati · r = {r} · q = {q}",
|
||||
"statLabel": "Come è stato calcolato",
|
||||
"explainerLabel": "Come è stato calcolato"
|
||||
}
|
||||
},
|
||||
"inTargetTitle": "Nel tuo intervallo"
|
||||
},
|
||||
"derived": {
|
||||
"coverage": {
|
||||
|
||||
+7
-8
@@ -2583,20 +2583,19 @@
|
||||
"bandVerySteady": "Twój nastrój był bardzo stabilny",
|
||||
"bandSteady": "Twój nastrój był dość stabilny",
|
||||
"bandVariable": "Twój nastrój był zauważalnie zmienny",
|
||||
"bandVeryVariable": "Twój nastrój ostatnio mocno się wahał"
|
||||
"bandVeryVariable": "Twój nastrój ostatnio mocno się wahał",
|
||||
"title": "Stabilność"
|
||||
},
|
||||
"influence": {
|
||||
"title": "Wpływ na nastrój",
|
||||
"detail": "Średnia {withAvg} w {withDays} dniach z tym tagiem wobec {withoutAvg} bez niego.",
|
||||
"confidenceLow": "Niska pewność",
|
||||
"confidenceMedium": "Średnia pewność",
|
||||
"confidenceHigh": "Wysoka pewność",
|
||||
"disclaimer": "To powiązania w Twoich danych, a nie dowód przyczyny. Wzorce warte obserwacji."
|
||||
"confidenceHigh": "Wysoka pewność"
|
||||
},
|
||||
"betterDays": {
|
||||
"title": "Co towarzyszy Twoim lepszym dniom",
|
||||
"description": "Tagi i wskaźniki, które pokrywają się z dniami lepszego i gorszego nastroju, uszeregowane według siły powiązania.",
|
||||
"disclaimer": "Tylko powiązania, nie przyczyny. Skonsultuj się z lekarzem, zanim coś zmienisz."
|
||||
"description": "Tagi i wskaźniki, które pokrywają się z dniami lepszego i gorszego nastroju, uszeregowane według siły powiązania."
|
||||
},
|
||||
"crosstab": {
|
||||
"title": "Tagi a Twoje wskaźniki zdrowia",
|
||||
@@ -2609,8 +2608,7 @@
|
||||
"unitKcal": "kcal",
|
||||
"unitScore": "pkt",
|
||||
"detailSameDay": "Średnio {withAvg} {unit} w {withDays} dniach z tym tagiem wobec {withoutAvg} {unit} bez niego.",
|
||||
"detailNextDay": "Średnio {withAvg} {unit} dzień po {withDays} dniach z tym tagiem wobec {withoutAvg} {unit} w pozostałe dni.",
|
||||
"disclaimer": "To powiązania w Twoich danych, nie dowód przyczyny. Wzorce warte obserwacji."
|
||||
"detailNextDay": "Średnio {withAvg} {unit} dzień po {withDays} dniach z tym tagiem wobec {withoutAvg} {unit} w pozostałe dni."
|
||||
},
|
||||
"discovery": {
|
||||
"title": "Co się wyróżnia",
|
||||
@@ -2625,7 +2623,8 @@
|
||||
"stat": "{n} sparowanych dni · r = {r} · q = {q}",
|
||||
"statLabel": "Jak to obliczono",
|
||||
"explainerLabel": "Jak to obliczono"
|
||||
}
|
||||
},
|
||||
"inTargetTitle": "W Twoim zakresie"
|
||||
},
|
||||
"derived": {
|
||||
"coverage": {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
"use client";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Compass } from "lucide-react";
|
||||
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { TileHeader } from "@/components/insights/tile-header";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
@@ -81,26 +84,32 @@ export function MoodDiscoveredRelations() {
|
||||
if (moodPairs.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="space-y-2" data-slot="mood-discovered-relations">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<h3 className="text-base font-semibold">
|
||||
{t("insights.mood.discovery.title")}
|
||||
</h3>
|
||||
<Card data-slot="mood-discovered-relations">
|
||||
<CardHeader className="pb-2">
|
||||
{/* v1.12.4 (C3) — the false-discovery footer + observational
|
||||
disclaimer were two full-width footnote rows. Fold both into a
|
||||
single explainer icon so the explanation is one hover/focus away
|
||||
instead of a low-density block under the list. */}
|
||||
<MoodExplainerIcon
|
||||
label={t("insights.mood.discovery.explainerLabel")}
|
||||
detail={`${t("insights.mood.discovery.footer", {
|
||||
tested: data.pairsTested,
|
||||
})} ${t("insights.mood.discovery.disclaimer")}`}
|
||||
instead of a low-density block under the list. v1.12.6 — the
|
||||
heading now rides the canonical `TileHeader`, with the explainer
|
||||
icon pinned to its trailing slot. */}
|
||||
<TileHeader
|
||||
icon={Compass}
|
||||
title={t("insights.mood.discovery.title")}
|
||||
right={
|
||||
<MoodExplainerIcon
|
||||
label={t("insights.mood.discovery.explainerLabel")}
|
||||
detail={`${t("insights.mood.discovery.footer", {
|
||||
tested: data.pairsTested,
|
||||
})} ${t("insights.mood.discovery.disclaimer")}`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("insights.mood.discovery.description")}
|
||||
</p>
|
||||
<ul className="divide-border divide-y">
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-muted-foreground mb-2 text-sm">
|
||||
{t("insights.mood.discovery.description")}
|
||||
</p>
|
||||
<ul className="divide-border divide-y">
|
||||
{moodPairs.map((pair) => {
|
||||
const moodIsOutcome = pair.outcome === "MOOD";
|
||||
const channelKey = moodIsOutcome ? pair.behaviour : pair.outcome;
|
||||
@@ -139,7 +148,8 @@ export function MoodDiscoveredRelations() {
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
import { Target } from "lucide-react";
|
||||
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { TileHeader } from "@/components/insights/tile-header";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
|
||||
/**
|
||||
@@ -12,6 +13,10 @@ import { useTranslations } from "@/lib/i18n/context";
|
||||
* the LLM snapshot) to a visible stat: the share of recent days that
|
||||
* landed in the good-mood band. Renders nothing when the number is
|
||||
* unavailable (no recent data) so the page degrades gracefully.
|
||||
*
|
||||
* v1.12.6 — the tile now leads with the canonical `TileHeader`
|
||||
* (icon + white heading) so it speaks the same card language as every
|
||||
* other Insights tile; the icon-in-a-circle headline glyph is dropped.
|
||||
*/
|
||||
export function MoodInTargetTile({ pct }: { pct: number | null }) {
|
||||
const { t } = useTranslations();
|
||||
@@ -22,18 +27,16 @@ export function MoodInTargetTile({ pct }: { pct: number | null }) {
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="flex items-center gap-4 py-4">
|
||||
<div className="bg-muted text-muted-foreground flex size-10 shrink-0 items-center justify-center rounded-full">
|
||||
<Target className="size-5" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-foreground text-2xl font-semibold tabular-nums">
|
||||
{t("insights.mood.inTargetValue", { pct: String(rounded) })}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("insights.mood.inTargetCaption")}
|
||||
</p>
|
||||
</div>
|
||||
<CardHeader className="pb-2">
|
||||
<TileHeader icon={Target} title={t("insights.mood.inTargetTitle")} />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-foreground text-2xl font-semibold tabular-nums">
|
||||
{t("insights.mood.inTargetValue", { pct: String(rounded) })}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t("insights.mood.inTargetCaption")}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,22 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import type { ComponentType } from "react";
|
||||
import {
|
||||
BarChart3,
|
||||
CalendarDays,
|
||||
CalendarRange,
|
||||
Clock,
|
||||
Grid3x3,
|
||||
Link2,
|
||||
Sparkles,
|
||||
Tag,
|
||||
Tags,
|
||||
TrendingUp,
|
||||
} from "lucide-react";
|
||||
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { TileHeader } from "@/components/insights/tile-header";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { queryKeys } from "@/lib/query-keys";
|
||||
@@ -136,15 +151,19 @@ interface MoodInsightsResponse {
|
||||
|
||||
function SectionCard({
|
||||
title,
|
||||
icon,
|
||||
children,
|
||||
}: {
|
||||
title: string;
|
||||
// v1.12.6 — every mood section header now routes through `TileHeader`
|
||||
// (icon + white heading) so the subpage reads as one card language.
|
||||
icon: ComponentType<{ className?: string }>;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">{title}</CardTitle>
|
||||
<TileHeader icon={icon} title={title} />
|
||||
</CardHeader>
|
||||
<CardContent>{children}</CardContent>
|
||||
</Card>
|
||||
@@ -210,7 +229,14 @@ export function MoodInsightsSections() {
|
||||
const hasCrosstab = crosstabRows.length > 0;
|
||||
|
||||
return (
|
||||
// v1.12.6 — the mood spine is ordered as: Einordnung (the classification
|
||||
// tiles: in-target share + stability band) FIRST, then the "what goes with
|
||||
// your better days" assessment read, then the descriptive breakdown
|
||||
// sections. The classification answers "where do I stand", the better-days
|
||||
// board answers "what associates with my good days", and the rest is the
|
||||
// supporting detail.
|
||||
<div className="space-y-4">
|
||||
{/* Einordnung — the classification tiles. */}
|
||||
{(hasInTargetTile || hasStabilityTile) && (
|
||||
// Two-up only when BOTH tiles render; a lone tile spans full width so
|
||||
// it never leaves a half-width orphan with dead space beside it.
|
||||
@@ -229,15 +255,25 @@ export function MoodInsightsSections() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<MoodNarrativeFeed items={narratives} />
|
||||
|
||||
{/* "Was zu deinen besseren Tagen passt" — the better-days assessment,
|
||||
ranked by effect size. The substance of the Einschätzung sits here,
|
||||
directly under the classification. */}
|
||||
{hasBetterDays && (
|
||||
<SectionCard title={t("insights.mood.betterDays.title")}>
|
||||
<SectionCard
|
||||
title={t("insights.mood.betterDays.title")}
|
||||
icon={Sparkles}
|
||||
>
|
||||
<MoodBetterDays factors={betterDays} />
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
<SectionCard title={t("insights.mood.heatmapTitle")}>
|
||||
{narratives.length > 0 && (
|
||||
<SectionCard title={t("insights.mood.narrative.title")} icon={TrendingUp}>
|
||||
<MoodNarrativeFeed items={narratives} />
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
<SectionCard title={t("insights.mood.heatmapTitle")} icon={CalendarDays}>
|
||||
<MoodHeatmap
|
||||
cells={heatmapCells}
|
||||
days={data.heatmap.windowDays}
|
||||
@@ -246,49 +282,58 @@ export function MoodInsightsSections() {
|
||||
</SectionCard>
|
||||
|
||||
<div className="grid gap-4 lg:grid-cols-2">
|
||||
<SectionCard title={t("insights.mood.distributionTitle")}>
|
||||
<SectionCard
|
||||
title={t("insights.mood.distributionTitle")}
|
||||
icon={BarChart3}
|
||||
>
|
||||
<MoodDistributionChart distribution={data.distribution} />
|
||||
</SectionCard>
|
||||
<SectionCard title={t("insights.mood.weekdayTitle")}>
|
||||
<SectionCard
|
||||
title={t("insights.mood.weekdayTitle")}
|
||||
icon={CalendarRange}
|
||||
>
|
||||
<MoodWeekdayChart weekday={data.weekday} />
|
||||
</SectionCard>
|
||||
</div>
|
||||
|
||||
{data.timeOfDay.reliable && (
|
||||
<SectionCard title={t("insights.mood.timeOfDay.title")}>
|
||||
<SectionCard title={t("insights.mood.timeOfDay.title")} icon={Clock}>
|
||||
<MoodTimeOfDayChart pattern={data.timeOfDay} />
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
{hasStructuredTags && (
|
||||
<SectionCard title={t("insights.mood.structuredTagsTitle")}>
|
||||
<SectionCard
|
||||
title={t("insights.mood.structuredTagsTitle")}
|
||||
icon={Tags}
|
||||
>
|
||||
<MoodStructuredTagBreakdown tags={data.structuredTags} />
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
{hasTags && (
|
||||
<SectionCard title={t("insights.mood.tagsTitle")}>
|
||||
<SectionCard title={t("insights.mood.tagsTitle")} icon={Tag}>
|
||||
<MoodTagBreakdown tags={data.tags} />
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
{hasInfluence && (
|
||||
<SectionCard title={t("insights.mood.influence.title")}>
|
||||
<SectionCard title={t("insights.mood.influence.title")} icon={TrendingUp}>
|
||||
<MoodTagInfluence rows={influenceRows} />
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
{hasCrosstab && (
|
||||
<SectionCard title={t("insights.mood.crosstab.title")}>
|
||||
<SectionCard title={t("insights.mood.crosstab.title")} icon={Grid3x3}>
|
||||
<MoodTagMetricCrosstab rows={crosstabRows} />
|
||||
</SectionCard>
|
||||
)}
|
||||
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-base font-semibold">
|
||||
{t("insights.mood.correlationsTitle")}
|
||||
</h3>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
<SectionCard
|
||||
title={t("insights.mood.correlationsTitle")}
|
||||
icon={Link2}
|
||||
>
|
||||
<p className="text-muted-foreground mb-2 text-sm">
|
||||
{t("insights.mood.correlationsDescription")}
|
||||
</p>
|
||||
<MoodCorrelationCards
|
||||
@@ -298,7 +343,7 @@ export function MoodInsightsSections() {
|
||||
weight={data.correlations.weight}
|
||||
bloodPressureSystolic={data.correlations.bloodPressureSystolic}
|
||||
/>
|
||||
</div>
|
||||
</SectionCard>
|
||||
|
||||
{/* F3 — the FDR-controlled discovered mood relations. Self-fetches the
|
||||
correlation-discovery surface and renders nothing when the operator
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
import { Activity } from "lucide-react";
|
||||
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||
import { TileHeader } from "@/components/insights/tile-header";
|
||||
import { useTranslations } from "@/lib/i18n/context";
|
||||
|
||||
/**
|
||||
@@ -46,20 +47,21 @@ export function MoodStabilityTile({
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardContent className="flex items-center gap-4 py-4">
|
||||
<div className="bg-muted text-muted-foreground flex size-10 shrink-0 items-center justify-center rounded-full">
|
||||
<Activity className="size-5" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-foreground text-2xl font-semibold tabular-nums">
|
||||
{t("insights.mood.stability.value", {
|
||||
score: String(stability.score),
|
||||
})}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t(BAND_KEY[stability.band])}
|
||||
</p>
|
||||
</div>
|
||||
<CardHeader className="pb-2">
|
||||
<TileHeader
|
||||
icon={Activity}
|
||||
title={t("insights.mood.stability.title")}
|
||||
/>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<p className="text-foreground text-2xl font-semibold tabular-nums">
|
||||
{t("insights.mood.stability.value", {
|
||||
score: String(stability.score),
|
||||
})}
|
||||
</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
{t(BAND_KEY[stability.band])}
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user