mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
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:
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");
|
||||
});
|
||||
});
|
||||
@@ -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={`${
|
||||
|
||||
Reference in new issue
Block a user