feat(sleep): place sleep-debt and rhythm cards side by side when both present

When both the sleep-debt and chronotype cards carry a settled readout, render
them two-up on large screens instead of stacked; they are compact and read
better paired. A lone data-bearing card — or either card in its learning /
partial state — keeps full width so it never leaves a half-width orphan. The
grid collapses to one column below the lg breakpoint and uses items-start so a
toggled-open chronotype card does not stretch its neighbour.
This commit is contained in:
Marc-André Bombeck committed 2026-06-14 23:02:37 +02:00
1 parent 94fd837d48
commit 3594b549d9
2 files changed
+51 -1

No files matched your search

@@ -102,6 +102,41 @@ describe("<SleepRhythmSection>", () => {
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(<SleepRhythmSection enabled />);
// 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(<SleepRhythmSection enabled />);
expect(html).toContain("grid-cols-1");
expect(html).not.toContain("lg:grid-cols-2");
});
it("renders nothing when disabled", () => {
rhythmMock.mockReturnValue({
data: undefined,
@@ -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 (
<div className="space-y-4">
<div
className={cn(
"grid gap-4",
bothHaveData ? "lg:grid-cols-2 lg:items-start" : "grid-cols-1",
)}
>
<SleepDebtCard debt={data.sleepDebt} />
<ChronotypeCard chronotype={data.chronotype} />
</div>