From f5d21eea289e1eb98649d3928beef04320a72893 Mon Sep 17 00:00:00 2001 From: Mannu Date: Sun, 31 May 2026 02:15:51 +0530 Subject: [PATCH] fix: home "Vaccine Reminder" showing "undefined due today" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The home banner read fields (.status, .vaccineName) that the DB-backed notifications API never returns — its DTO exposes {type, title, message, metadata,...}. So .vaccineName was undefined → "undefined due today", and .status was undefined → the ternary always fell to the "due today" branch even for overdue/nudge items. Two issues, both fixed: 1. Contract mismatch: render the real `message` field instead of the non-existent `.vaccineName`/`.status`. message is already correctly built server-side ("BCG is due today" / "BCG is 3 days overdue"). 2. Wrong source set: the banner consumed the FIRST notification of ANY type. Since the API also returns log/memory/garment nudges, a nudge could land at index 0 and render under the vaccine header. Now filtered to type starting with "vaccine_" before use. Also hardened the vaccine upsert: ON CONFLICT DO NOTHING -> DO UPDATE SET title/message/action_url/metadata, so future copy/day-count changes refresh existing rows instead of freezing the first version (preserves id/is_read). Co-Authored-By: Claude Opus 4.8 --- src/app/(app)/home/page.tsx | 6 +++++- src/app/api/notifications/route.ts | 6 ------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/app/(app)/home/page.tsx b/src/app/(app)/home/page.tsx index c189508..f6965b1 100644 --- a/src/app/(app)/home/page.tsx +++ b/src/app/(app)/home/page.tsx @@ -125,7 +125,11 @@ export default function HomePage() { window.addEventListener("online", handleOnline); fetch(`/api/notifications?childId=${childId}`) .then(res => res.json()) - .then(data => setVaccineReminders(data.notifications || [])) + // Only vaccine notifications belong in the "Vaccine Reminder" banner — + // the API also returns log/memory/garment nudges. + .then(data => setVaccineReminders( + (data.notifications || []).filter((n: { type?: string }) => n.type?.startsWith("vaccine_")) + )) .catch(console.error); return () => window.removeEventListener("online", handleOnline); }, [childId]); diff --git a/src/app/api/notifications/route.ts b/src/app/api/notifications/route.ts index dfb32cf..966fc63 100644 --- a/src/app/api/notifications/route.ts +++ b/src/app/api/notifications/route.ts @@ -83,12 +83,6 @@ export async function GET(request: NextRequest) { const child = children[0]; const birthDate = new Date(child.birth_date as string); - // Self-heal legacy data: an earlier generator stored "undefined" in the - // message ("undefined is N days overdue"). Those rows were frozen by - // ON CONFLICT DO NOTHING. Delete them so the upsert below regenerates them - // with the correct vaccine name. - await sql`DELETE FROM notifications WHERE family_id = ${familyId} AND message LIKE 'undefined%'`; - // ── 1. Vaccine notifications ────────────────────────────────────────────── const given = await sql` SELECT vaccine_name FROM vaccinations