feat(finale): deliberation jury identity resolution, rankable projects, score-revision path, session sync

- submitVote resolves the caller's JuryGroupMember participant row server-side
  (was comparing JuryGroupMember id to User id — every juror got FORBIDDEN)
- getSessionWithVotes now includes category projects so the ranking form has
  data before finalize
- liveVoting.vote accepts any finale-ordered project (revision during
  deliberation); timed window still applies to the live project
- live.sendToScreens keeps LiveVotingSession.currentProjectId/status in sync

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-06-10 18:15:45 +02:00
parent 4e6904fa12
commit c9dc1bfabd
7 changed files with 229 additions and 18 deletions

View File

@@ -615,7 +615,7 @@ export async function getSessionWithVotes(
sessionId: string,
prisma: PrismaClient,
) {
return prisma.deliberationSession.findUnique({
const session = await prisma.deliberationSession.findUnique({
where: { id: sessionId },
include: {
votes: {
@@ -648,6 +648,23 @@ export async function getSessionWithVotes(
round: { select: { id: true, name: true, roundType: true } },
},
})
if (!session) return null
// Rankable projects: the round's projects in this session's category.
// (results are empty until finalize — the jury ranking form needs this list.)
const roundStates = await prisma.projectRoundState.findMany({
where: {
roundId: session.roundId,
project: { competitionCategory: session.category },
},
include: {
project: {
select: { id: true, title: true, teamName: true, competitionCategory: true },
},
},
})
return { ...session, projects: roundStates.map((rs) => rs.project) }
}
// ─── Internal Helpers ───────────────────────────────────────────────────────