fix(lunch): reminder filter, recap failure surfacing, manual send-reminders

- Extract selectUnpickedAttendees helper with OR filter (is null OR pickedAt null)
  to fix cron missing attendees with no MemberLunchPick row at all
- Update cron route to use the helper
- sendRecap now throws TRPCError on email failure instead of silently stamping success
- Add lunch.sendReminders adminProcedure for manual on-demand reminder sends
- Add "Send reminders now" AlertDialog button to LunchRecapActions
- Tests: lunch-reminder-filter.test.ts (2 new), all 5 lunch test files pass (40 tests)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-06-04 16:24:01 +02:00
parent 884c96c710
commit 3f25ba112b
6 changed files with 269 additions and 13 deletions

View File

@@ -1,6 +1,7 @@
import { NextResponse, type NextRequest } from 'next/server'
import { prisma } from '@/lib/prisma'
import { sendLunchReminderEmail } from '@/lib/email'
import { selectUnpickedAttendees } from '@/server/services/lunch-reminders'
/**
* Cron: send a single reminder email per attending member who hasn't picked
@@ -35,16 +36,7 @@ export async function GET(request: NextRequest): Promise<NextResponse> {
)
if (now < reminderAt || now >= deadline) continue
const ams = await prisma.attendingMember.findMany({
where: {
confirmation: {
project: { programId: event.programId },
status: 'CONFIRMED',
},
lunchPick: { is: { pickedAt: null } },
},
include: { user: { select: { name: true, email: true } } },
})
const ams = await selectUnpickedAttendees(prisma, event)
for (const am of ams) {
if (!am.user.email) continue
try {