diff --git a/src/components/insights/sleep/__tests__/chronotype-section.test.tsx b/src/components/insights/sleep/__tests__/chronotype-section.test.tsx index 7d09a89d0..8d26be570 100644 --- a/src/components/insights/sleep/__tests__/chronotype-section.test.tsx +++ b/src/components/insights/sleep/__tests__/chronotype-section.test.tsx @@ -80,7 +80,7 @@ describe("", () => { expect(html).not.toContain('data-slot="chronotype-error"'); }); - it("renders the large band treatment + midpoint clock on a settled DTO", () => { + it("renders the band value + midpoint clock on a settled DTO", () => { rhythmMock.mockReturnValue({ data: READY_DTO, isLoading: false, @@ -90,8 +90,10 @@ describe("", () => { expect(html).toContain('data-slot="chronotype-band"'); expect(html).toContain("Evening type"); expect(html).toContain("05:41"); - // The section heading carries the chronotype title. - expect(html).toContain('data-slot="section-heading"'); + // v1.19.0 — the duplicate standalone section heading is gone; the single + // chronotype title now lives inside the card, uniform with the debt tile. + expect(html).not.toContain('data-slot="section-heading"'); + expect(html).toContain('data-slot="chronotype-card"'); }); it("renders nothing when disabled", () => { diff --git a/src/components/insights/sleep/__tests__/sleep-rhythm-cards.test.tsx b/src/components/insights/sleep/__tests__/sleep-rhythm-cards.test.tsx index c2c0bc53f..6dfe7a9ca 100644 --- a/src/components/insights/sleep/__tests__/sleep-rhythm-cards.test.tsx +++ b/src/components/insights/sleep/__tests__/sleep-rhythm-cards.test.tsx @@ -21,16 +21,16 @@ const PARTIAL_DEBT: SleepDebtDto = { debtMinutes: 0, needMinutes: 420, nightsCounted: 3, - windowNights: 14, - nightsUntilReady: 4, + windowNights: 5, + nightsUntilReady: 1, }; const READY_DEBT: SleepDebtDto = { state: "ready", - debtMinutes: 600, + debtMinutes: 60, needMinutes: 420, - nightsCounted: 10, - windowNights: 14, + nightsCounted: 5, + windowNights: 5, nightsUntilReady: 0, }; @@ -65,11 +65,11 @@ describe("", () => { expect(html).not.toContain("short"); }); - it("asserts the cumulative debt in the ready state", () => { + it("asserts the running debt balance in the ready state", () => { const html = render(); - // 600 min = 10h 0m short. - expect(html).toContain("10h 0m short"); - expect(html).toContain("14 nights"); + // 60 min = 1h 0m short — the small, recovering balance a normal sleeper sees. + expect(html).toContain("1h 0m short"); + expect(html).toContain("5 nights"); }); it("inherits the standard Card rhythm (no compact override)", () => { @@ -110,10 +110,11 @@ describe("", () => { expect(html).toContain("04:30"); }); - it("v1.18.7 W-D — renders the band as the large hero label, not a corner readout", () => { + it("v1.19.0 — renders the band as the bold primary value, uniform with the debt tile", () => { const html = render(); - // The reworked bottom treatment leads with a large band label + the - // natural-midpoint headline. The retired top-right corner readout is gone. + // The band is the primary value at the SAME text-2xl scale the debt tile + // uses for its hours figure; the natural-midpoint clock is the secondary + // line. The retired top-right corner readout is gone. expect(html).not.toContain('data-slot="chronotype-corner"'); expect(html).toContain('data-slot="chronotype-band"'); expect(html).toContain("Intermediate type"); diff --git a/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx b/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx index 2d254ed3e..d4af80009 100644 --- a/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx +++ b/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx @@ -42,10 +42,10 @@ function render(node: React.ReactNode, locale: "en" | "de" = "en") { const READY_DTO: SleepRhythmDto = { sleepDebt: { state: "ready", - debtMinutes: 600, + debtMinutes: 60, needMinutes: 420, - nightsCounted: 10, - windowNights: 14, + nightsCounted: 5, + windowNights: 5, nightsUntilReady: 0, }, chronotype: { @@ -97,8 +97,8 @@ describe("", () => { isError: false, }); const html = render(); - expect(html).toContain("10h 0m short"); - // The chronotype band lives in the separate bottom section now, never here. + expect(html).toContain("1h 0m short"); + // The chronotype band lives in the separate sibling tile, never here. expect(html).not.toContain("Intermediate type"); expect(html).not.toContain('data-slot="chronotype-band"'); }); diff --git a/src/components/insights/sleep/chronotype-card.tsx b/src/components/insights/sleep/chronotype-card.tsx index 4918e9813..03bb455e6 100644 --- a/src/components/insights/sleep/chronotype-card.tsx +++ b/src/components/insights/sleep/chronotype-card.tsx @@ -28,12 +28,16 @@ function hoursMinutes(totalMinutes: number): { /** * v1.17.0 — chronotype card (MCTQ MSF / MSFsc band + social jetlag). * - * v1.18.7 W-D — reworked from a small corner-label card into the PROMINENT - * bottom treatment of the Sleep view: a LARGE band label is the hero, with the - * natural sleep-midpoint clock beneath it, and an "advanced" disclosure that - * reveals social jetlag + the sleep-debt-corrected MSFsc. While `learning` it - * keeps the calm "still learning your rhythm — N of M nights" copy and asserts - * NO band. Warm, grounded copy; mobile-first. + * v1.19.0 — brought to the SAME tile shape as `` so the two sit + * uniform in the shared grid row: identical card chrome, an icon + `text-base` + * `CardTitle` header, then a `space-y-0.5` value block with a bold primary value + * (`text-2xl font-semibold`) and one secondary `text-xs muted` line. The primary + * value is the chronotype BAND (a label, the slot the debt tile fills with an + * hours figure); the concrete companion — the natural sleep midpoint clock — is + * the secondary line, so both tiles read "bold value + one grounded line" at the + * same scale. The "advanced" disclosure (social jetlag + sleep-debt-corrected + * MSFsc) stays. While `learning` it keeps the calm "still learning your rhythm — + * N of M nights" copy and asserts NO band. Warm, grounded copy; mobile-first. */ export function ChronotypeCard({ chronotype }: { chronotype: ChronotypeDto }) { const { t } = useTranslations(); @@ -78,15 +82,13 @@ export function ChronotypeCard({ chronotype }: { chronotype: ChronotypeDto }) { - {/* The band is the hero of the bottom treatment — a large label the - eye lands on first, with the natural sleep-midpoint clock as the - grounding caption beneath it. */} -
-

- {bandLabel} -

+ {/* Uniform with the sleep-debt tile: a bold primary value (the band) at + the same scale, with the concrete companion (natural sleep midpoint + clock) as the secondary muted line. */} +
+

{bandLabel}

{msfClock && ( -

+

{t("insights.sleep.chronotype.midpointHeadline", { clock: msfClock, })} diff --git a/src/components/insights/sleep/chronotype-section.tsx b/src/components/insights/sleep/chronotype-section.tsx index 39f577634..38d27879d 100644 --- a/src/components/insights/sleep/chronotype-section.tsx +++ b/src/components/insights/sleep/chronotype-section.tsx @@ -1,28 +1,28 @@ "use client"; -import { Clock } from "lucide-react"; - import { useAuth } from "@/hooks/use-auth"; import { Skeleton } from "@/components/ui/skeleton"; import { useTranslations } from "@/lib/i18n/context"; -import { SectionHeading } from "@/components/insights/section-heading"; import { useSleepRhythm } from "./use-sleep-rhythm"; import { ChronotypeCard } from "./chronotype-card"; /** - * v1.18.7 W-D — chronotype as the prominent bottom block of the Sleep view. + * v1.18.7 W-D — chronotype card on the Sleep view. * - * Split out of `` (which now owns only the sleep-debt - * headline) so the chronotype gets a full-width treatment at the end of the - * page: a large band label, the natural sleep-midpoint clock beneath it, and an - * expandable disclosure for social jetlag + MSFsc. Reads the SAME - * server-authoritative DTO via `useSleepRhythm`, so it shares the + * v1.19.0 — the standalone "Chronotyp" `` was removed: it + * duplicated the heading the `` already carries in its own + * `CardTitle`, so "Chronotype" appeared twice and the tile no longer matched + * its sibling (the sleep-debt tile, which has no section heading). The card now + * stands as a peer of `` inside the shared grid row — same card + * chrome, one heading each, rendered inside the tile. + * + * Reads the SAME server-authoritative DTO via `useSleepRhythm`, so it shares the * `["sleep-rhythm"]` cache the debt section already warmed — no extra * round-trip. Gated on `enabled` so a source-less account never fetches. * * States mirror the debt section: loading → skeleton, error → quiet inline - * notice, settled → the card (which carries its own calm "still learning" - * copy for the no-nights-yet case, since the route always returns a full DTO). + * notice, settled → the card (which carries its own calm "still learning" copy + * for the no-nights-yet case, since the route always returns a full DTO). */ export function ChronotypeSection({ enabled }: { enabled: boolean }) { const { isAuthenticated } = useAuth(); @@ -33,28 +33,26 @@ export function ChronotypeSection({ enabled }: { enabled: boolean }) { if (!enabled) return null; - return ( -

- + {t("insights.sleep.rhythm.loadError")} +
+ ); + } + + if (isLoading || !data) { + return ( + - {isError ? ( -
- {t("insights.sleep.rhythm.loadError")} -
- ) : isLoading || !data ? ( - - ) : ( - - )} - - ); + ); + } + + return ; }