diff --git a/messages/de.json b/messages/de.json index a1e1232e1..3635e3cc5 100644 --- a/messages/de.json +++ b/messages/de.json @@ -1642,7 +1642,7 @@ "regenerateAnalysis": "Einschätzung neu erstellen", "regenerateSuccess": "Einschätzung wurde neu erstellt", "warmStarted": "Einschätzungen werden im Hintergrund erstellt", - "narrativeTitle": "Dein Zeitraum im Rückblick", + "narrativeTitle": "Zeitraum im Rückblick", "narrativeWeek": "Diese Woche", "narrativeMonth": "Dieser Monat", "narrativePreparing": "Deine Zusammenfassung wird erstellt…", diff --git a/messages/en.json b/messages/en.json index 862cc9cc6..5685bbc0f 100644 --- a/messages/en.json +++ b/messages/en.json @@ -1642,7 +1642,7 @@ "regenerateAnalysis": "Re-run assessment", "regenerateSuccess": "Assessment refreshed", "warmStarted": "Assessments are being prepared in the background", - "narrativeTitle": "Your period in review", + "narrativeTitle": "Period in review", "narrativeWeek": "This week", "narrativeMonth": "This month", "narrativePreparing": "Preparing your summary…", diff --git a/messages/es.json b/messages/es.json index 72aef43b0..2621b9d2e 100644 --- a/messages/es.json +++ b/messages/es.json @@ -1642,7 +1642,7 @@ "regenerateAnalysis": "Volver a generar la valoración", "regenerateSuccess": "Valoración regenerada", "warmStarted": "Las valoraciones se están preparando en segundo plano", - "narrativeTitle": "Tu período en resumen", + "narrativeTitle": "Período en resumen", "narrativeWeek": "Esta semana", "narrativeMonth": "Este mes", "narrativePreparing": "Preparando tu resumen…", diff --git a/messages/fr.json b/messages/fr.json index 0f58f7f5f..a77dc2824 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -1642,7 +1642,7 @@ "regenerateAnalysis": "Relancer l’évaluation", "regenerateSuccess": "Évaluation regénérée", "warmStarted": "Les évaluations sont en cours de préparation en arrière-plan", - "narrativeTitle": "Votre période en revue", + "narrativeTitle": "Période en revue", "narrativeWeek": "Cette semaine", "narrativeMonth": "Ce mois-ci", "narrativePreparing": "Préparation de votre résumé…", diff --git a/messages/it.json b/messages/it.json index e252fde9e..f4c0652b1 100644 --- a/messages/it.json +++ b/messages/it.json @@ -1642,7 +1642,7 @@ "regenerateAnalysis": "Rigenera la valutazione", "regenerateSuccess": "Valutazione rigenerata", "warmStarted": "Le valutazioni vengono preparate in background", - "narrativeTitle": "Il tuo periodo in sintesi", + "narrativeTitle": "Periodo in sintesi", "narrativeWeek": "Questa settimana", "narrativeMonth": "Questo mese", "narrativePreparing": "Preparazione del riepilogo…", diff --git a/messages/pl.json b/messages/pl.json index 645e40fd7..474e3ccac 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -1642,7 +1642,7 @@ "regenerateAnalysis": "Uruchom ocenę ponownie", "regenerateSuccess": "Ocena wygenerowana ponownie", "warmStarted": "Oceny są przygotowywane w tle", - "narrativeTitle": "Twój okres w skrócie", + "narrativeTitle": "Okres w skrócie", "narrativeWeek": "Ten tydzień", "narrativeMonth": "Ten miesiąc", "narrativePreparing": "Przygotowywanie podsumowania…", diff --git a/src/components/insights/__tests__/coincident-deviation-card.test.tsx b/src/components/insights/__tests__/coincident-deviation-card.test.tsx index d4fb07e8e..f964d32ad 100644 --- a/src/components/insights/__tests__/coincident-deviation-card.test.tsx +++ b/src/components/insights/__tests__/coincident-deviation-card.test.tsx @@ -115,18 +115,14 @@ describe("", () => { expect(fired).toContain("min-h-48"); }); - it("renders the insufficient (building baselines) state, never an alarm", () => { + it("renders nothing in the insufficient (building baselines) state", () => { mock(insufficient()); const html = render(); - expect(html).toContain('data-state="insufficient"'); - expect(html).toContain('data-slot="coincident-building"'); - expect(html).toContain('data-slot="coverage-meter"'); - // Calm, not amber/red. - expect(html).not.toContain("border-warning"); - expect(html).not.toContain("text-destructive"); + // No signal yet → the section stays absent rather than an empty husk. + expect(html).toBe(""); }); - it("renders the all-clear state with the count checked", () => { + it("renders nothing in the all-clear state (no signal to show)", () => { mock( ok({ fired: false, @@ -136,12 +132,8 @@ describe("", () => { }), ); const html = render(); - expect(html).toContain('data-state="all-clear"'); - expect(html).toContain("All your vitals are within their personal range"); - expect(html).toContain("text-success"); - // No alarm tone. - expect(html).not.toContain("border-warning"); - expect(html).not.toContain("text-destructive"); + // Every vital inside its personal range → hide the whole card. + expect(html).toBe(""); }); it("renders the watch state for a single out-of-band vital, not the fired tone", () => { diff --git a/src/components/insights/coincident-deviation-card.tsx b/src/components/insights/coincident-deviation-card.tsx index cd427fffa..e7b4ff005 100644 --- a/src/components/insights/coincident-deviation-card.tsx +++ b/src/components/insights/coincident-deviation-card.tsx @@ -1,6 +1,6 @@ "use client"; -import { CheckCircle2, AlertTriangle, Activity } from "lucide-react"; +import { AlertTriangle, Activity } from "lucide-react"; import { useTranslations } from "@/lib/i18n/context"; import { cn } from "@/lib/utils"; @@ -17,22 +17,20 @@ import type { DerivedProvenance } from "@/lib/insights/derived/types"; /** * v1.10.3 — "Today's signal" headline card. * - * Promotes the COINCIDENT_DEVIATION flag from one buried below-the-fold vitals - * tile (painted only when it fired) to a dedicated, always-mounted card at the - * top of the Insights overview, matching the always-present pattern Apple - * Vitals / WHOOP Health Monitor / Oura Symptom Radar use. The signal is the - * daily headline read, not an alarm: an "all clear" day is itself the - * reassuring product. + * Surfaces the COINCIDENT_DEVIATION flag at the top of the Insights overview as + * a calm awareness read drawn off the SINGLE existing `Derived` payload — no new + * engine math, no new route, no schema change. * - * It renders four calm states off the SINGLE existing `Derived` payload — no - * new engine math, no new route, no schema change: - * - insufficient (< 2 banded vitals) → "building your baselines" + coverage. - * - all-clear (ok, !fired, 0 contributing) → a green check + the count - * checked. - * - watch (ok, !fired, 1 contributing) → a calm, non-alert line naming the - * one vital. - * - fired (ok, fired, ≥ 2 contributing) → an amber awareness card naming the - * vitals + the load-bearing "possible factors — never a cause" line. + * The card renders ONLY when there is an actual signal to show. A day where + * every vital sits inside its personal range (all-clear), or an account still + * building its baselines (insufficient), produces no card at all — the section + * stays absent rather than surfacing an empty header. The two states that DO + * render: + * - watch (ok, 1 contributing vital, or a thin-history multi-signal flag) → + * a calm, non-alert line naming the vital. + * - fired (ok, fired, ≥ 2 contributing, with enough history) → an amber + * awareness card naming the vitals + the load-bearing "possible factors — + * never a cause" line. * * Restraint guarantees: at most the `warning` (amber) band — never * `destructive`/red, never a score, never a chart (so no Recharts), never a @@ -167,22 +165,10 @@ export function CoincidentDeviationCard({ ); } - // Building the baselines — fewer than two banded vitals. Calm, never an - // alarm, never blank. + // Building the baselines — fewer than two banded vitals. There is no signal + // to show, so the section stays absent rather than rendering an empty husk. if (data.status === "insufficient") { - return ( -
- -

- {t("insights.derived.coincident.building")} -

- -
-
- ); + return null; } const v = data.value!; @@ -208,32 +194,10 @@ export function CoincidentDeviationCard({ ? "fired" : "watch"; + // All clear — every vital is inside its personal range, so there is no signal + // worth a card. Hide the whole section rather than surface an empty header. if (state === "all-clear") { - return ( -
- -
-
-
-
- ); + return null; } if (state === "watch") { diff --git a/src/components/insights/period-narrative-card.tsx b/src/components/insights/period-narrative-card.tsx index 98e3a46a2..7f2a53c15 100644 --- a/src/components/insights/period-narrative-card.tsx +++ b/src/components/insights/period-narrative-card.tsx @@ -159,9 +159,9 @@ export function PeriodNarrativeCard({ className={cn(SHELL, className)} >
- +

{t("insights.narrativeTitle")} - +

{narrative?.provenance ? ( ) : null}