import { z } from 'zod' import { TRPCError } from '@trpc/server' import { router, adminProcedure, protectedProcedure } from '../trpc' import { activateRound, closeRound, archiveRound, transitionProject, batchTransitionProjects, getProjectRoundStates, getProjectRoundState, } from '../services/round-engine' const projectRoundStateEnum = z.enum([ 'PENDING', 'IN_PROGRESS', 'PASSED', 'REJECTED', 'COMPLETED', 'WITHDRAWN', ]) export const roundEngineRouter = router({ /** * Activate a round: ROUND_DRAFT → ROUND_ACTIVE */ activate: adminProcedure .input(z.object({ roundId: z.string() })) .mutation(async ({ ctx, input }) => { const result = await activateRound(input.roundId, ctx.user.id, ctx.prisma) if (!result.success) { throw new TRPCError({ code: 'BAD_REQUEST', message: result.errors?.join('; ') ?? 'Failed to activate round', }) } return result }), /** * Close a round: ROUND_ACTIVE → ROUND_CLOSED */ close: adminProcedure .input(z.object({ roundId: z.string() })) .mutation(async ({ ctx, input }) => { const result = await closeRound(input.roundId, ctx.user.id, ctx.prisma) if (!result.success) { throw new TRPCError({ code: 'BAD_REQUEST', message: result.errors?.join('; ') ?? 'Failed to close round', }) } return result }), /** * Archive a round: ROUND_CLOSED → ROUND_ARCHIVED */ archive: adminProcedure .input(z.object({ roundId: z.string() })) .mutation(async ({ ctx, input }) => { const result = await archiveRound(input.roundId, ctx.user.id, ctx.prisma) if (!result.success) { throw new TRPCError({ code: 'BAD_REQUEST', message: result.errors?.join('; ') ?? 'Failed to archive round', }) } return result }), /** * Transition a single project within a round */ transitionProject: adminProcedure .input( z.object({ projectId: z.string(), roundId: z.string(), newState: projectRoundStateEnum, }) ) .mutation(async ({ ctx, input }) => { const result = await transitionProject( input.projectId, input.roundId, input.newState, ctx.user.id, ctx.prisma, ) if (!result.success) { throw new TRPCError({ code: 'BAD_REQUEST', message: result.errors?.join('; ') ?? 'Failed to transition project', }) } return result }), /** * Batch transition multiple projects within a round */ batchTransition: adminProcedure .input( z.object({ projectIds: z.array(z.string()).min(1), roundId: z.string(), newState: projectRoundStateEnum, }) ) .mutation(async ({ ctx, input }) => { return batchTransitionProjects( input.projectIds, input.roundId, input.newState, ctx.user.id, ctx.prisma, ) }), /** * Get all project round states for a round */ getProjectStates: protectedProcedure .input(z.object({ roundId: z.string() })) .query(async ({ ctx, input }) => { return getProjectRoundStates(input.roundId, ctx.prisma) }), /** * Get a single project's state within a round */ getProjectState: protectedProcedure .input( z.object({ projectId: z.string(), roundId: z.string(), }) ) .query(async ({ ctx, input }) => { return getProjectRoundState(input.projectId, input.roundId, ctx.prisma) }), })