144 lines
3.5 KiB
TypeScript
144 lines
3.5 KiB
TypeScript
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)
|
|
}),
|
|
})
|