refactor(schema-cascade): rename Project.mentorAssignment → mentorAssignments

Schema dropped @unique on MentorAssignment.projectId in PR8 Task 1 →
back-relation becomes a list. Mechanical rename of Prisma queries and
consumer accessors. Legacy single-mentor callers use [0] with a TODO for
PR8 Task 8 to surface the full list. mentor-workspace.ts is left as Task 5.

- routers (mentor, project, applicant, finalist, round) and smart-assignment
  service: include/where/select keys renamed; `mentorAssignment: null` →
  `mentorAssignments: { none: {} }`; `{ isNot: null }` → `{ some: {} }`.
- UI consumers (mentor + applicant pages): `project.mentorAssignment` →
  `project.mentorAssignments[0]` with TODO markers.
- Tests: `findUnique({ projectId })` → `findFirst({ projectId })` since the
  composite key now requires both projectId+mentorId. MentorFile.create gains
  the new required projectId.
- Workspace endpoints in mentor.ts now guard null mentorAssignmentId until
  Task 5 re-scopes them to project.
- finalist.unconfirm now cascades to ALL active mentor assignments.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-05-22 16:37:37 +02:00
parent 9152ebb399
commit 66110598a0
11 changed files with 127 additions and 71 deletions

View File

@@ -1176,7 +1176,7 @@ export const applicantRouter = router({
],
},
include: {
mentorAssignment: { select: { mentorId: true } },
mentorAssignments: { select: { mentorId: true } },
},
})
@@ -1187,7 +1187,10 @@ export const applicantRouter = router({
})
}
if (!project.mentorAssignment) {
// TODO(PR8 Task 7): notify ALL assigned mentors. For now we notify the
// first one for legacy parity.
const primaryMentorAssignment = project.mentorAssignments[0] ?? null
if (!primaryMentorAssignment) {
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'No mentor assigned to this project',
@@ -1207,9 +1210,9 @@ export const applicantRouter = router({
},
})
// Notify the mentor
// Notify the (primary) mentor
await createNotification({
userId: project.mentorAssignment.mentorId,
userId: primaryMentorAssignment.mentorId,
type: 'MENTOR_MESSAGE',
title: 'New Message',
message: `${ctx.user.name || 'Team member'} sent a message about "${project.title}"`,
@@ -1313,7 +1316,7 @@ export const applicantRouter = router({
submittedBy: {
select: { id: true, name: true, email: true },
},
mentorAssignment: {
mentorAssignments: {
include: {
mentor: {
select: { id: true, name: true, email: true },
@@ -1523,7 +1526,7 @@ export const applicantRouter = router({
select: {
id: true,
programId: true,
mentorAssignment: { select: { id: true } },
mentorAssignments: { select: { id: true }, take: 1 },
},
})
@@ -1531,8 +1534,8 @@ export const applicantRouter = router({
return { hasMentor: false, hasEvaluationRounds: false }
}
// Check if mentor is assigned
const hasMentor = !!project.mentorAssignment
// Check if mentor is assigned (any active assignment counts)
const hasMentor = project.mentorAssignments.length > 0
// Check if feedback is available — first check admin settings, then fall back to per-round config
let hasEvaluationRounds = false
@@ -2689,8 +2692,12 @@ export const applicantRouter = router({
})
}
const assignment = await ctx.prisma.mentorAssignment.findUnique({
// TODO(PR8 Task 7): when multiple mentors are assigned, surface them all
// in the applicant message thread. For now we display the most recently
// assigned (non-dropped) mentor as the "primary".
const assignment = await ctx.prisma.mentorAssignment.findFirst({
where: { projectId: input.projectId },
orderBy: { assignedAt: 'desc' },
include: { mentor: { select: { id: true, name: true, email: true } } },
})