feat(workspace): mentor + applicant message previews (§F.2)

mentor.getRecentMessages: last N unread messages from teams across all
of a mentor's assignments. Drives a Recent Messages card on /mentor.

applicant.getMentorConversationPreview: last 3 messages + unread count
for a given project. Drives a 'Conversation with [Mentor]' card on
/applicant — auto-hides when no mentor is assigned.

Both procedures use the existing MentorMessage(projectId, createdAt)
composite index — no new index needed.

Plan: docs/superpowers/plans/2026-04-28-pr6-multi-role-and-workspace-previews.md
This commit is contained in:
Matt
2026-04-28 16:14:11 +02:00
parent 70a9752d73
commit 26ff8ed111
7 changed files with 352 additions and 0 deletions

View File

@@ -2659,4 +2659,50 @@ export const applicantRouter = router({
return { success: true, roundId: activePrs.roundId, roundName: activePrs.round.name }
}),
/**
* Last N messages + unread count for the applicant's project mentor workspace.
* Drives the 'Conversation with [Mentor]' card on /applicant.
*/
getMentorConversationPreview: protectedProcedure
.input(z.object({ projectId: z.string(), limit: z.number().min(1).max(10).default(3) }))
.query(async ({ ctx, input }) => {
const teamMembership = await ctx.prisma.teamMember.findFirst({
where: { projectId: input.projectId, userId: ctx.user.id },
select: { id: true },
})
if (!teamMembership) {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'Not a team member of this project',
})
}
const assignment = await ctx.prisma.mentorAssignment.findUnique({
where: { projectId: input.projectId },
include: { mentor: { select: { id: true, name: true, email: true } } },
})
const [messages, unreadCount] = await Promise.all([
ctx.prisma.mentorMessage.findMany({
where: { projectId: input.projectId },
include: { sender: { select: { id: true, name: true, email: true } } },
orderBy: { createdAt: 'desc' },
take: input.limit,
}),
ctx.prisma.mentorMessage.count({
where: {
projectId: input.projectId,
senderId: { not: ctx.user.id },
isRead: false,
},
}),
])
return {
mentor: assignment?.mentor ?? null,
messages: messages.reverse(),
unreadCount,
}
}),
})