- 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>
44 lines
1.4 KiB
TypeScript
44 lines
1.4 KiB
TypeScript
'use client'
|
|
|
|
import { useRef } from 'react'
|
|
import { trpc } from '@/lib/trpc/client'
|
|
import { Skeleton } from '@/components/ui/skeleton'
|
|
import { LunchEventConfig } from './lunch-event-config'
|
|
import { LunchDishes } from './lunch-dishes'
|
|
import { LunchManifest } from './lunch-manifest'
|
|
import { LunchExternals, type LunchExternalsHandle } from './lunch-externals'
|
|
import { LunchRecapActions } from './lunch-recap-actions'
|
|
|
|
export function LunchTab({ programId }: { programId: string }) {
|
|
const { data: event, isLoading } = trpc.lunch.getEvent.useQuery({ programId })
|
|
const externalsRef = useRef<LunchExternalsHandle>(null)
|
|
if (isLoading || !event) {
|
|
return <Skeleton className="h-48 w-full" />
|
|
}
|
|
return (
|
|
<div className="space-y-6">
|
|
<LunchEventConfig programId={programId} event={event} />
|
|
{event.enabled && (
|
|
<>
|
|
<LunchDishes programId={programId} lunchEventId={event.id} />
|
|
<LunchManifest
|
|
programId={programId}
|
|
onEditExternal={(id) => externalsRef.current?.openEditDialog(id)}
|
|
/>
|
|
<LunchExternals
|
|
ref={externalsRef}
|
|
programId={programId}
|
|
lunchEventId={event.id}
|
|
/>
|
|
<LunchRecapActions
|
|
programId={programId}
|
|
lunchEventId={event.id}
|
|
recapSentAt={event.recapSentAt}
|
|
extraRecipientCount={event.extraRecipients.length}
|
|
/>
|
|
</>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|