import { z } from 'zod' import { router, protectedProcedure, adminProcedure } from '../trpc' export const programRouter = router({ /** * List all programs with optional filtering */ list: protectedProcedure .input( z.object({ status: z.enum(['DRAFT', 'ACTIVE', 'ARCHIVED']).optional(), includeRounds: z.boolean().optional(), }).optional() ) .query(async ({ ctx, input }) => { return ctx.prisma.program.findMany({ where: input?.status ? { status: input.status } : undefined, orderBy: { year: 'desc' }, include: { _count: { select: { rounds: true }, }, rounds: input?.includeRounds ? { orderBy: { createdAt: 'asc' }, } : false, }, }) }), /** * Get a single program with its rounds */ get: protectedProcedure .input(z.object({ id: z.string() })) .query(async ({ ctx, input }) => { return ctx.prisma.program.findUniqueOrThrow({ where: { id: input.id }, include: { rounds: { orderBy: { createdAt: 'asc' }, include: { _count: { select: { projects: true, assignments: true }, }, }, }, }, }) }), /** * Create a new program (admin only) */ create: adminProcedure .input( z.object({ name: z.string().min(1).max(255), year: z.number().int().min(2020).max(2100), description: z.string().optional(), }) ) .mutation(async ({ ctx, input }) => { const program = await ctx.prisma.program.create({ data: input, }) // Audit log await ctx.prisma.auditLog.create({ data: { userId: ctx.user.id, action: 'CREATE', entityType: 'Program', entityId: program.id, detailsJson: input, ipAddress: ctx.ip, userAgent: ctx.userAgent, }, }) return program }), /** * Update a program (admin only) */ update: adminProcedure .input( z.object({ id: z.string(), name: z.string().min(1).max(255).optional(), status: z.enum(['DRAFT', 'ACTIVE', 'ARCHIVED']).optional(), description: z.string().optional(), }) ) .mutation(async ({ ctx, input }) => { const { id, ...data } = input const program = await ctx.prisma.program.update({ where: { id }, data, }) // Audit log await ctx.prisma.auditLog.create({ data: { userId: ctx.user.id, action: 'UPDATE', entityType: 'Program', entityId: id, detailsJson: data, ipAddress: ctx.ip, userAgent: ctx.userAgent, }, }) return program }), /** * Delete a program (admin only) * Note: This will cascade delete all rounds, projects, etc. */ delete: adminProcedure .input(z.object({ id: z.string() })) .mutation(async ({ ctx, input }) => { const program = await ctx.prisma.program.delete({ where: { id: input.id }, }) // Audit log await ctx.prisma.auditLog.create({ data: { userId: ctx.user.id, action: 'DELETE', entityType: 'Program', entityId: input.id, detailsJson: { name: program.name, year: program.year }, ipAddress: ctx.ip, userAgent: ctx.userAgent, }, }) return program }), })