fix: home "Vaccine Reminder" showing "undefined due today"

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 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-31 02:15:51 +05:30
parent 52a60a7cff
commit f5d21eea28
2 changed files with 5 additions and 7 deletions

View file

@ -125,7 +125,11 @@ export default function HomePage() {
window.addEventListener("online", handleOnline); window.addEventListener("online", handleOnline);
fetch(`/api/notifications?childId=${childId}`) fetch(`/api/notifications?childId=${childId}`)
.then(res => res.json()) .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); .catch(console.error);
return () => window.removeEventListener("online", handleOnline); return () => window.removeEventListener("online", handleOnline);
}, [childId]); }, [childId]);

View file

@ -83,12 +83,6 @@ export async function GET(request: NextRequest) {
const child = children[0]; const child = children[0];
const birthDate = new Date(child.birth_date as string); 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 ────────────────────────────────────────────── // ── 1. Vaccine notifications ──────────────────────────────────────────────
const given = await sql` const given = await sql`
SELECT vaccine_name FROM vaccinations SELECT vaccine_name FROM vaccinations