From b93f1f5dcf10913127941b31f2e123456ff064cd Mon Sep 17 00:00:00 2001 From: Mannu Date: Sat, 16 May 2026 16:30:14 +0530 Subject: [PATCH] Add notifications API for vaccine reminders - API endpoint /api/notifications returns due/overdue vaccines - Checks child's birthDate to calculate due dates - Returns notifications with type, status, days overdue Co-Authored-By: Claude Opus 4.7 --- src/app/api/notifications/route.ts | 97 ++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 src/app/api/notifications/route.ts diff --git a/src/app/api/notifications/route.ts b/src/app/api/notifications/route.ts new file mode 100644 index 0000000..4d45ba0 --- /dev/null +++ b/src/app/api/notifications/route.ts @@ -0,0 +1,97 @@ +import { NextResponse } from "next/server"; +import { sql } from "@/db"; + +// IAP Vaccination Schedule (weeks from birth) +const IAP_SCHEDULE = [ + { name: "BCG", weeks: 0 }, + { name: "OPV-0", weeks: 0 }, + { name: "HepB-1", weeks: 0 }, + { name: "OPV-1", weeks: 6 }, + { name: "Pentavalent-1", weeks: 6 }, + { name: "PCV-1", weeks: 6 }, + { name: "Rota-1", weeks: 6 }, + { name: "OPV-2", weeks: 10 }, + { name: "Pentavalent-2", weeks: 10 }, + { name: "PCV-2", weeks: 10 }, + { name: "Rota-2", weeks: 10 }, + { name: "OPV-3", weeks: 14 }, + { name: "Pentavalent-3", weeks: 14 }, + { name: "PCV-3", weeks: 14 }, + { name: "Rota-3", weeks: 14 }, + { name: "MR-1", weeks: 48 }, + { name: "JE-1", weeks: 48 }, + { name: "Vitamin A-1", weeks: 48 }, + { name: "OPV-4", weeks: 48 }, + { name: "MR-2", weeks: 96 }, + { name: "JE-2", weeks: 96 }, + { name: "DPT-Booster-1", weeks: 96 }, + { name: "Vitamin A-2", weeks: 96 }, + { name: "OPV-5", weeks: 96 }, + { name: "DPT-Booster-2", weeks: 208 }, + { name: "Tetanus and adult diphtheria (Td)", weeks: 208 }, +]; + +export async function GET(request: Request) { + try { + const { searchParams } = new URL(request.url); + const childId = searchParams.get("childId"); + + if (!childId) { + return NextResponse.json({ error: "childId required" }, { status: 400 }); + } + + // Get child's birth date + const children = await sql` + SELECT id, name, birth_date FROM children WHERE id = ${childId} + `; + + if (!children || children.length === 0) { + return NextResponse.json({ notifications: [] }); + } + + const child = children[0]; + const birthDate = new Date(child.birth_date); + const today = new Date(); + today.setHours(0, 0, 0, 0); + + // Get already given vaccines + const given = await sql` + SELECT vaccine_name, given_date FROM vaccinations + WHERE child_id = ${childId} AND status = 'given' + `; + const givenMap = new Set(given.map((v: any) => v.vaccine_name)); + + // Calculate upcoming vaccine notifications + const notifications = []; + for (const vaccine of IAP_SCHEDULE) { + if (givenMap.has(vaccine.name)) continue; + + // Calculate due date + const dueDate = new Date(birthDate); + dueDate.setDate(dueDate.getDate() + vaccine.weeks * 7); + dueDate.setHours(0, 0, 0, 0); + + const diffDays = Math.floor((dueDate.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); + + // Notify if due today or overdue + if (diffDays <= 0) { + notifications.push({ + id: `vaccine-${vaccine.name}`, + type: "vaccine", + title: diffDays === 0 ? "Vaccine Due Today" : "Vaccine Overdue", + message: `${vaccine.name} is ${diffDays === 0 ? "due today" : `${Math.abs(diffDays)} days overdue`}`, + vaccineName: vaccine.name, + dueDate: dueDate.toISOString().split("T")[0], + status: diffDays === 0 ? "due_today" : "overdue", + childId, + childName: child.name, + }); + } + } + + return NextResponse.json({ notifications }); + } catch (error) { + console.error("Notifications error:", error); + return NextResponse.json({ error: String(error) }, { status: 500 }); + } +} \ No newline at end of file