import { z } from 'zod' import { TRPCError } from '@trpc/server' import { router, adminProcedure, protectedProcedure } from '../trpc' import { logAudit } from '../utils/audit' // ─── Shared zod schemas ────────────────────────────────────────────────────── const dietaryTags = z.array( z.enum(['VEGETARIAN', 'VEGAN', 'GLUTEN_FREE', 'PESCATARIAN']), ) const allergens = z.array( z.enum([ 'GLUTEN', 'CRUSTACEANS', 'EGGS', 'FISH', 'PEANUTS', 'SOYBEANS', 'MILK', 'TREE_NUTS', 'CELERY', 'MUSTARD', 'SESAME', 'SULPHITES', 'LUPIN', 'MOLLUSCS', ]), ) // ─── Router ────────────────────────────────────────────────────────────────── export const lunchRouter = router({ /** * Get-or-create the LunchEvent for a program. Lazy creation mirrors * the hotel pattern: callers don't have to know whether the row * already exists. */ getEvent: adminProcedure .input(z.object({ programId: z.string() })) .query(async ({ ctx, input }) => { const existing = await ctx.prisma.lunchEvent.findUnique({ where: { programId: input.programId }, }) if (existing) return existing return ctx.prisma.lunchEvent.create({ data: { programId: input.programId } }) }), /** Patch any subset of LunchEvent config fields. Audit-logged. */ updateEvent: adminProcedure .input( z.object({ programId: z.string(), enabled: z.boolean().optional(), eventAt: z.date().nullable().optional(), endAt: z.date().nullable().optional(), venue: z.string().nullable().optional(), notes: z.string().nullable().optional(), changeCutoffHours: z.number().int().min(0).max(720).optional(), reminderHoursBeforeDeadline: z .number() .int() .min(0) .max(720) .nullable() .optional(), cronEnabled: z.boolean().optional(), extraRecipients: z.array(z.string().email()).optional(), }), ) .mutation(async ({ ctx, input }) => { const { programId, ...patch } = input // Lazy-create before patching so updateEvent doubles as "create + update" await ctx.prisma.lunchEvent.upsert({ where: { programId }, create: { programId }, update: {}, }) const updated = await ctx.prisma.lunchEvent.update({ where: { programId }, data: patch, }) await logAudit({ prisma: ctx.prisma, userId: ctx.user.id, action: 'LUNCH_EVENT_UPDATED', entityType: 'LunchEvent', entityId: updated.id, detailsJson: patch as Record, }) return updated }), })