import { z } from 'zod' import { TRPCError } from '@trpc/server' import { CompetitionCategory } from '@prisma/client' import { router, adminProcedure } from '../trpc' import { logAudit } from '../utils/audit' export const finalistRouter = router({ /** * Set the finalist slot quota for a category in a program. Mutable mid-flight, * but blocked when reducing below the count of already-CONFIRMED finalists in * that category — admin must un-confirm a team first. */ setQuota: adminProcedure .input( z.object({ programId: z.string(), category: z.nativeEnum(CompetitionCategory), quota: z.number().int().min(0).max(100), }), ) .mutation(async ({ ctx, input }) => { const confirmedCount = await ctx.prisma.finalistConfirmation.count({ where: { project: { programId: input.programId }, category: input.category, status: 'CONFIRMED', }, }) if (input.quota < confirmedCount) { throw new TRPCError({ code: 'BAD_REQUEST', message: `Cannot reduce ${input.category} quota to ${input.quota} — ${confirmedCount} teams have already confirmed. Un-confirm one team first, then retry.`, }) } const quota = await ctx.prisma.finalistSlotQuota.upsert({ where: { programId_category: { programId: input.programId, category: input.category, }, }, create: { programId: input.programId, category: input.category, quota: input.quota, }, update: { quota: input.quota }, }) await logAudit({ prisma: ctx.prisma, userId: ctx.user.id, action: 'FINALIST_QUOTA_SET', entityType: 'FinalistSlotQuota', entityId: quota.id, detailsJson: { programId: input.programId, category: input.category, quota: input.quota, previousConfirmedCount: confirmedCount, }, }) return quota }), })