fix(charts): split mood-chart empty-state copy on raw entry count

Mirrors the health-chart fix. The mood-analytics endpoint already
exposes `entries[].samples` per calendar day, so summing it yields
the true raw mood-entry count for the window. When chartData
collapses to < 3 daily buckets but the raw count is ≥ 3, paint the
new "More distinct days needed" copy instead of the misleading "log
at least 3 entries" hint.

Pin both branches with an SSR test that mocks the analytics fetch.
This commit is contained in:
Marc-André Bombeck committed 2026-05-21 21:45:32 +02:00
1 parent f68b8c422a
commit ceabcf8a47
2 files changed
+127 -5

No files matched your search

@@ -0,0 +1,94 @@
import { describe, it, expect, vi, beforeEach } from "vitest";
import { renderToStaticMarkup } from "react-dom/server";
/**
* v1.4.43 W2-CHART-GATE — mood-chart empty-state copy split.
*
* `chartData` collapses entries to one point per calendar day, so a
* user with many mood entries on < 3 distinct days saw the legacy
* "log more entries" copy despite having logged plenty. The gate now
* keys on `rawCount = Σ entries[].samples` so the second message
* paints when the user only needs more spread across days.
*/
vi.mock("@/hooks/use-auth", () => ({
useAuth: () => ({
isAuthenticated: true,
user: null,
isLoading: false,
}),
}));
describe("<MoodChart> — empty-state gate by raw count", () => {
beforeEach(() => {
vi.resetModules();
});
it("renders need-more-days copy when chartData.length < 3 but rawCount >= 3", async () => {
const data = {
entries: [
{ date: "2026-05-19", score: 4, samples: 25 },
{ date: "2026-05-20", score: 5, samples: 25 },
],
trend: null,
};
vi.doMock("@tanstack/react-query", () => ({
useQuery: () => ({ data, isLoading: false }),
useQueryClient: () => ({
cancelQueries: () => Promise.resolve(),
getQueryData: () => undefined,
setQueryData: () => undefined,
invalidateQueries: () => Promise.resolve(),
}),
useMutation: () => ({ mutate: () => undefined, isPending: false }),
}));
const { I18nProvider } = await import("@/lib/i18n/context");
const { MoodChart } = await import("../mood-chart");
const html = renderToStaticMarkup(
<I18nProvider initialLocale="de">
<MoodChart />
</I18nProvider>,
);
expect(html).toContain("Mehr Messtage erforderlich");
expect(html).not.toContain("Erfasse mindestens 3 Einträge");
vi.doUnmock("@tanstack/react-query");
});
it("renders no-data copy when rawCount < 3", async () => {
const data = {
entries: [{ date: "2026-05-20", score: 4, samples: 1 }],
trend: null,
};
vi.doMock("@tanstack/react-query", () => ({
useQuery: () => ({ data, isLoading: false }),
useQueryClient: () => ({
cancelQueries: () => Promise.resolve(),
getQueryData: () => undefined,
setQueryData: () => undefined,
invalidateQueries: () => Promise.resolve(),
}),
useMutation: () => ({ mutate: () => undefined, isPending: false }),
}));
const { I18nProvider } = await import("@/lib/i18n/context");
const { MoodChart } = await import("../mood-chart");
const html = renderToStaticMarkup(
<I18nProvider initialLocale="de">
<MoodChart />
</I18nProvider>,
);
expect(html).toContain("Erfasse mindestens 3 Einträge");
expect(html).not.toContain("Mehr Messtage erforderlich");
vi.doUnmock("@tanstack/react-query");
});
});
+33 -5
View File
@@ -340,6 +340,20 @@ export function MoodChart({
refetchOnWindowFocus: false,
});
// v1.4.43 W2-CHART-GATE — raw mood-entry count across every day in
// the window. `entries[].samples` carries the per-day raw count
// straight from the analytics endpoint; summing it produces the
// total entries the user logged, independent of how many calendar
// days they hit. Used by the empty-state gate to distinguish "few
// entries" from "few days".
const rawCount = useMemo<number>(() => {
if (!data?.entries?.length) return 0;
return data.entries.reduce(
(acc, entry) => acc + (Number.isFinite(entry.samples) ? entry.samples : 0),
0,
);
}, [data]);
const chartData = useMemo((): ChartDataPoint[] | undefined => {
if (!data?.entries?.length) return undefined;
@@ -682,11 +696,25 @@ export function MoodChart({
// shared CHART_HEIGHT_PX so the empty state preserves the
// trend-row rhythm (v1.4.27 — was 280, now 240 to match every
// other dashboard chart card).
<ChartEmptyState
title={t("charts.emptyStateTitle")}
description={t("charts.emptyStateDescription")}
height={CHART_HEIGHT_PX}
/>
//
// v1.4.43 W2-CHART-GATE — split the copy on the raw mood-entry
// count. A user with 50 mood entries across 2 calendar days
// sees `chartData.length = 2` (daily-aggregated) but the raw
// count (sum of `samples`) is 50. Surface "need more days"
// in that scenario so the call-to-action matches reality.
rawCount >= 3 ? (
<ChartEmptyState
title={t("charts.needMoreDistinctDaysTitle")}
description={t("charts.needMoreDistinctDaysDescription")}
height={CHART_HEIGHT_PX}
/>
) : (
<ChartEmptyState
title={t("charts.emptyStateTitle")}
description={t("charts.emptyStateDescription")}
height={CHART_HEIGHT_PX}
/>
)
) : (
<div
className={`${