From ba789f979403e58d35f558758abe579da5e5d978 Mon Sep 17 00:00:00 2001 From: Daniel Volz Date: Sun, 10 May 2026 14:22:33 +0200 Subject: [PATCH] fix: harden dashboard takenBy rendering --- frontend/src/pages/DashboardPage.tsx | 13 ++++--- .../src/test/pages/DashboardPage.test.tsx | 36 +++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx index f9fd0a5..4e3a666 100644 --- a/frontend/src/pages/DashboardPage.tsx +++ b/frontend/src/pages/DashboardPage.tsx @@ -62,6 +62,11 @@ function findFocusTargetElement(doseId: string | null, medId: string | null): HT return null; } +function getDosePeople(takenBy: unknown): Array { + const takenByArray = Array.isArray(takenBy) ? takenBy : []; + return takenByArray.length > 0 ? takenByArray : [null]; +} + export function DashboardPage() { const { t, i18n } = useTranslation(); const { user } = useAuth(); @@ -1035,7 +1040,7 @@ export function DashboardPage() {
{item.doses.map((dose) => { // If no takenBy, show single checkbox; otherwise show one per person - const people = dose.takenBy.length > 0 ? dose.takenBy : [null]; + const people = getDosePeople(dose.takenBy); const allTaken = people.every((person) => isDoseTakenForDisplay(getDoseId(dose.id, person)) ); @@ -1358,7 +1363,7 @@ export function DashboardPage() {
{item.doses.map((dose) => { const isOverdue = dose.when < Date.now() && !isEmpty; - const people = dose.takenBy.length > 0 ? dose.takenBy : [null]; + const people = getDosePeople(dose.takenBy); const allTaken = people.every((person) => isDoseTakenForDisplay(getDoseId(dose.id, person)) ); @@ -1443,7 +1448,7 @@ export function DashboardPage() { const totalFutureDoses = futureDays.flatMap((d) => d.meds.flatMap((m) => m.doses.flatMap((dose) => - dose.takenBy.length > 0 ? dose.takenBy.map((p) => `${dose.id}-${p}`) : [dose.id] + getDosePeople(dose.takenBy).map((person) => (person ? `${dose.id}-${person}` : dose.id)) ) ) ); @@ -1623,7 +1628,7 @@ export function DashboardPage() {
{item.doses.map((dose) => { - const people = dose.takenBy.length > 0 ? dose.takenBy : [null]; + const people = getDosePeople(dose.takenBy); const allTaken = people.every((person) => isDoseTakenForDisplay(getDoseId(dose.id, person)) ); diff --git a/frontend/src/test/pages/DashboardPage.test.tsx b/frontend/src/test/pages/DashboardPage.test.tsx index c6e1098..5e4ae43 100644 --- a/frontend/src/test/pages/DashboardPage.test.tsx +++ b/frontend/src/test/pages/DashboardPage.test.tsx @@ -165,6 +165,7 @@ const createMockAppContext = (overrides = {}) => ({ todayDay: null, futureDays: [], takenDoses: new Set(), + skippedDoses: new Set(), dismissedDoses: new Set(), markDoseTaken: vi.fn(), undoDoseTaken: vi.fn(), @@ -385,6 +386,41 @@ describe("DashboardPage", () => { expect(cards.length).toBeGreaterThan(0); }); + it("renders today doses even when schedule data omits takenBy arrays", () => { + mockContextValue = createMockAppContext({ + todayDay: { + dateStr: "Today", + date: new Date(), + isPast: false, + meds: [ + { + medName: "Aspirin", + total: 1, + doses: [ + { + id: "dose-without-taken-by", + timeStr: "09:00", + when: Date.now() + 60_000, + usage: 1, + takenBy: undefined as unknown as string[], + }, + ], + lastWhen: Date.now() + 60_000, + }, + ], + }, + }); + + render( + + + + ); + + expect(screen.getByText(/dashboard\.schedules\.title/i)).toBeInTheDocument(); + expect(screen.getByText("09:00")).toBeInTheDocument(); + }); + it("renders schedule days selector", () => { render(