feat(final-docs): mentor.getProjectFinalDocuments procedure
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,7 @@ import {
|
|||||||
signMentorUploadToken,
|
signMentorUploadToken,
|
||||||
verifyMentorUploadToken,
|
verifyMentorUploadToken,
|
||||||
} from '@/lib/mentor-upload-token'
|
} from '@/lib/mentor-upload-token'
|
||||||
|
import { getFinalDocumentStatusForProject } from '../services/final-documents'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the project is enrolled in a MENTORING round that is still
|
* True if the project is enrolled in a MENTORING round that is still
|
||||||
@@ -215,6 +216,21 @@ async function assertProjectWorkspaceAccess(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const mentorRouter = router({
|
export const mentorRouter = router({
|
||||||
|
/** Grand-final document status for a project, for the mentor workspace panel. */
|
||||||
|
getProjectFinalDocuments: protectedProcedure
|
||||||
|
.input(z.object({ projectId: z.string() }))
|
||||||
|
.query(async ({ ctx, input }) => {
|
||||||
|
const isAdmin = ['SUPER_ADMIN', 'PROGRAM_ADMIN'].includes(ctx.user.role)
|
||||||
|
if (!isAdmin) {
|
||||||
|
const [mentorAssignment, teamMembership] = await Promise.all([
|
||||||
|
ctx.prisma.mentorAssignment.findFirst({ where: { mentorId: ctx.user.id, projectId: input.projectId }, select: { id: true } }),
|
||||||
|
ctx.prisma.teamMember.findFirst({ where: { userId: ctx.user.id, projectId: input.projectId }, select: { id: true } }),
|
||||||
|
])
|
||||||
|
if (!mentorAssignment && !teamMembership) throw new TRPCError({ code: 'FORBIDDEN', message: 'No access to this project' })
|
||||||
|
}
|
||||||
|
return getFinalDocumentStatusForProject(ctx.prisma, input.projectId)
|
||||||
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get AI-suggested mentor matches for a project
|
* Get AI-suggested mentor matches for a project
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from '@/server/services/final-documents'
|
} from '@/server/services/final-documents'
|
||||||
import * as applicantRouter from '@/server/routers/applicant'
|
import * as applicantRouter from '@/server/routers/applicant'
|
||||||
import * as finalistRouter from '@/server/routers/finalist'
|
import * as finalistRouter from '@/server/routers/finalist'
|
||||||
|
import * as mentorRouter from '@/server/routers/mentor'
|
||||||
import { createCaller } from '../setup'
|
import { createCaller } from '../setup'
|
||||||
|
|
||||||
const programIds: string[] = []
|
const programIds: string[] = []
|
||||||
@@ -209,3 +210,35 @@ describe('finalist.listReviewDocuments', () => {
|
|||||||
await expect(caller.listReviewDocuments({ programId: program.id })).rejects.toThrow(TRPCError)
|
await expect(caller.listReviewDocuments({ programId: program.id })).rejects.toThrow(TRPCError)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('mentor.getProjectFinalDocuments', () => {
|
||||||
|
const localPrograms: string[] = []
|
||||||
|
const localUsers: string[] = []
|
||||||
|
afterAll(async () => { for (const id of localPrograms) await cleanupTestData(id, localUsers) })
|
||||||
|
|
||||||
|
it('returns status for a project the mentor is assigned to', async () => {
|
||||||
|
const program = await createTestProgram(); localPrograms.push(program.id)
|
||||||
|
const comp = await createTestCompetition(program.id, { status: 'ACTIVE' })
|
||||||
|
const round = await createTestRound(comp.id, { roundType: 'LIVE_FINAL', status: 'ROUND_ACTIVE', sortOrder: 6, windowCloseAt: new Date(Date.now() + 86_400_000) })
|
||||||
|
await prisma.fileRequirement.create({ data: { id: uid('req'), roundId: round.id, name: 'Executive Summary', acceptedMimeTypes: ['application/pdf'], isRequired: true, sortOrder: 1 } })
|
||||||
|
const project = await createTestProject(program.id)
|
||||||
|
await createTestProjectRoundState(project.id, round.id)
|
||||||
|
const mentor = await createTestUser('MENTOR'); localUsers.push(mentor.id)
|
||||||
|
await prisma.mentorAssignment.create({ data: { projectId: project.id, mentorId: mentor.id } })
|
||||||
|
|
||||||
|
const caller = createCaller(mentorRouter.mentorRouter, mentor)
|
||||||
|
const status = await caller.getProjectFinalDocuments({ projectId: project.id })
|
||||||
|
expect(status?.roundId).toBe(round.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('forbids a mentor not assigned to the project', async () => {
|
||||||
|
const program = await createTestProgram(); localPrograms.push(program.id)
|
||||||
|
const comp = await createTestCompetition(program.id, { status: 'ACTIVE' })
|
||||||
|
const round = await createTestRound(comp.id, { roundType: 'LIVE_FINAL', status: 'ROUND_ACTIVE', sortOrder: 6 })
|
||||||
|
const project = await createTestProject(program.id)
|
||||||
|
await createTestProjectRoundState(project.id, round.id)
|
||||||
|
const mentor = await createTestUser('MENTOR'); localUsers.push(mentor.id)
|
||||||
|
const caller = createCaller(mentorRouter.mentorRouter, mentor)
|
||||||
|
await expect(caller.getProjectFinalDocuments({ projectId: project.id })).rejects.toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user