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 696d6b981..639992829 100644 --- a/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx +++ b/src/components/insights/sleep/__tests__/sleep-rhythm-section.test.tsx @@ -102,6 +102,41 @@ describe("", () => { expect(html).toContain("md:py-6"); }); + it("places the two cards side by side when both carry settled data", () => { + rhythmMock.mockReturnValue({ + data: READY_DTO, + isLoading: false, + isError: false, + }); + const html = render(); + // Two-up on large screens, single column below. + expect(html).toContain("lg:grid-cols-2"); + }); + + it("keeps a lone data-bearing card full width when the other is still learning", () => { + rhythmMock.mockReturnValue({ + data: { + ...READY_DTO, + // Sleep-debt settled, chronotype still learning → no two-up grid. + chronotype: { + state: "learning", + msfMinutes: null, + msfScMinutes: null, + band: null, + socialJetlagMinutes: null, + freeNightsCounted: 2, + workNightsCounted: 5, + freeNightsUntilReady: 4, + }, + } satisfies SleepRhythmDto, + isLoading: false, + isError: false, + }); + const html = render(); + expect(html).toContain("grid-cols-1"); + expect(html).not.toContain("lg:grid-cols-2"); + }); + it("renders nothing when disabled", () => { rhythmMock.mockReturnValue({ data: undefined, diff --git a/src/components/insights/sleep/sleep-rhythm-section.tsx b/src/components/insights/sleep/sleep-rhythm-section.tsx index 68c578377..a88466965 100644 --- a/src/components/insights/sleep/sleep-rhythm-section.tsx +++ b/src/components/insights/sleep/sleep-rhythm-section.tsx @@ -2,6 +2,7 @@ import { useAuth } from "@/hooks/use-auth"; import { Skeleton } from "@/components/ui/skeleton"; +import { cn } from "@/lib/utils"; import { useTranslations } from "@/lib/i18n/context"; import { useSleepRhythm } from "./use-sleep-rhythm"; import { SleepDebtCard } from "./sleep-debt-card"; @@ -52,8 +53,22 @@ export function SleepRhythmSection({ enabled }: { enabled: boolean }) { ); } + // Both cards are compact; when each carries a settled (non-learning) readout + // they sit two-up. A lone card with data spans full width so it never leaves + // a half-width orphan with dead space beside it (mirrors the mood Einordnung + // tiles). The cards' own learning/partial states stay stacked full-width. + const debtHasData = data.sleepDebt.state === "ready"; + const chronotypeHasData = + data.chronotype.state === "ready" && data.chronotype.band != null; + const bothHaveData = debtHasData && chronotypeHasData; + return ( -
+