feat: lunch picker on attending-members card + admin slide-over

LunchPickForm shared between applicant dashboard rows (member-self /
team-lead context) and the admin manifest's edit-pencil slide-over.
Adds lunch.getMemberPick read for the per-row hydration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-04-29 02:49:08 +02:00
parent ec24d404c5
commit df95867465
5 changed files with 350 additions and 30 deletions

View File

@@ -2727,7 +2727,9 @@ export const applicantRouter = router({
},
finalistConfirmation: {
include: {
attendingMembers: { select: { userId: true, needsVisa: true } },
attendingMembers: {
select: { id: true, userId: true, needsVisa: true },
},
},
},
},
@@ -2757,6 +2759,7 @@ export const applicantRouter = router({
project: {
id: project.id,
title: project.title,
programId: project.program.id,
teamMembers: project.teamMembers.map((tm) => ({
userId: tm.userId,
role: tm.role,

View File

@@ -227,6 +227,46 @@ export const lunchRouter = router({
return { ok: true as const }
}),
// ─── Single-row pick read (used by per-row picker UI) ────────────────────
/**
* Read the current MemberLunchPick for an AttendingMember plus the dishes
* for the parent event. Permission: any user with a TeamMember row on the
* project, OR the AttendingMember.userId itself, OR admin.
*/
getMemberPick: protectedProcedure
.input(z.object({ attendingMemberId: z.string() }))
.query(async ({ ctx, input }) => {
const am = await ctx.prisma.attendingMember.findUnique({
where: { id: input.attendingMemberId },
include: {
confirmation: {
select: {
project: {
select: {
id: true,
programId: true,
teamMembers: { select: { userId: true } },
},
},
},
},
lunchPick: true,
},
})
if (!am) throw new TRPCError({ code: 'NOT_FOUND' })
const userId = ctx.user.id
const role = ctx.user.role
const isAdmin = role === 'SUPER_ADMIN' || role === 'PROGRAM_ADMIN'
const isOnTeam = am.confirmation.project.teamMembers.some(
(tm) => tm.userId === userId,
)
if (!isAdmin && !isOnTeam && am.userId !== userId) {
throw new TRPCError({ code: 'FORBIDDEN' })
}
return { pick: am.lunchPick }
}),
// ─── Manifest + CSV export ───────────────────────────────────────────────
getManifest: adminProcedure