fix: filter applicant portal rounds by award track membership
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled

Projects in SEPARATE_POOL awards now only see their award rounds (not main
pool rounds) across all applicant queries: openRounds, deadlines, document
completeness, nav flags, and evaluations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 00:24:33 +01:00
parent 84d90e1978
commit d183d98d9a

View File

@@ -1301,22 +1301,52 @@ export const applicantRouter = router({
} }
const programId = project.programId const programId = project.programId
const openRounds = programId let openRounds: Array<{ id: string; name: string; slug: string | null; roundType: string; windowCloseAt: Date | null }> = []
? await ctx.prisma.round.findMany({ if (programId) {
where: { const allActiveRounds = await ctx.prisma.round.findMany({
competition: { programId }, where: {
status: 'ROUND_ACTIVE', competition: { programId },
}, status: 'ROUND_ACTIVE',
orderBy: { sortOrder: 'asc' }, },
select: { orderBy: { sortOrder: 'asc' },
id: true, select: {
name: true, id: true,
slug: true, name: true,
roundType: true, slug: true,
windowCloseAt: true, roundType: true,
}, windowCloseAt: true,
specialAwardId: true,
specialAward: { select: { name: true } },
},
})
// Filter rounds based on award track: only show rounds the project is actually in
const projectRoundIds = new Set(
(await ctx.prisma.projectRoundState.findMany({
where: { projectId: project.id },
select: { roundId: true },
})).map((prs) => prs.roundId)
)
const isInAwardTrack = allActiveRounds.some(
(r) => r.specialAwardId && projectRoundIds.has(r.id)
)
openRounds = allActiveRounds
.filter((r) => {
// Award round project isn't in → hide
if (r.specialAwardId && !projectRoundIds.has(r.id)) return false
// Main round when project is in award track and has no state in this round → hide
if (!r.specialAwardId && isInAwardTrack && !projectRoundIds.has(r.id)) return false
return true
}) })
: [] .map((r) => ({
id: r.id,
name: r.specialAward ? `${r.specialAward.name}: ${r.name}` : r.name,
slug: r.slug,
roundType: r.roundType,
windowCloseAt: r.windowCloseAt,
}))
}
// Determine user's role in the project // Determine user's role in the project
const userMembership = project.teamMembers.find((tm) => tm.userId === ctx.user.id) const userMembership = project.teamMembers.find((tm) => tm.userId === ctx.user.id)
@@ -1391,13 +1421,22 @@ export const applicantRouter = router({
const hasMentor = !!project.mentorAssignment const hasMentor = !!project.mentorAssignment
// Check if there are EVALUATION rounds (CLOSED/ARCHIVED) with applicantVisibility.enabled // Check if there are EVALUATION rounds (CLOSED/ARCHIVED) with applicantVisibility.enabled
// Only consider rounds the project actually participated in (award track filtering)
let hasEvaluationRounds = false let hasEvaluationRounds = false
if (project.programId) { if (project.programId) {
const projectRoundIds = new Set(
(await ctx.prisma.projectRoundState.findMany({
where: { projectId: project.id },
select: { roundId: true },
})).map((prs) => prs.roundId)
)
const closedEvalRounds = await ctx.prisma.round.findMany({ const closedEvalRounds = await ctx.prisma.round.findMany({
where: { where: {
competition: { programId: project.programId }, competition: { programId: project.programId },
roundType: 'EVALUATION', roundType: 'EVALUATION',
status: { in: ['ROUND_CLOSED', 'ROUND_ARCHIVED'] }, status: { in: ['ROUND_CLOSED', 'ROUND_ARCHIVED'] },
...(projectRoundIds.size > 0 ? { id: { in: [...projectRoundIds] } } : {}),
}, },
select: { configJson: true }, select: { configJson: true },
}) })
@@ -1666,12 +1705,20 @@ export const applicantRouter = router({
if (!project?.programId) return [] if (!project?.programId) return []
// Get closed/archived EVALUATION rounds for this competition // Get closed/archived EVALUATION rounds — only ones this project participated in
const projectRoundIds = new Set(
(await ctx.prisma.projectRoundState.findMany({
where: { projectId: project.id },
select: { roundId: true },
})).map((prs) => prs.roundId)
)
const evalRounds = await ctx.prisma.round.findMany({ const evalRounds = await ctx.prisma.round.findMany({
where: { where: {
competition: { programId: project.programId }, competition: { programId: project.programId },
roundType: 'EVALUATION', roundType: 'EVALUATION',
status: { in: ['ROUND_CLOSED', 'ROUND_ARCHIVED'] }, status: { in: ['ROUND_CLOSED', 'ROUND_ARCHIVED'] },
...(projectRoundIds.size > 0 ? { id: { in: [...projectRoundIds] } } : {}),
}, },
select: { select: {
id: true, id: true,
@@ -1758,7 +1805,7 @@ export const applicantRouter = router({
{ teamMembers: { some: { userId: ctx.user.id } } }, { teamMembers: { some: { userId: ctx.user.id } } },
], ],
}, },
select: { programId: true }, select: { id: true, programId: true },
}) })
if (!project?.programId) return [] if (!project?.programId) return []
@@ -1774,14 +1821,33 @@ export const applicantRouter = router({
id: true, id: true,
name: true, name: true,
windowCloseAt: true, windowCloseAt: true,
specialAwardId: true,
specialAward: { select: { name: true } },
}, },
orderBy: { windowCloseAt: 'asc' }, orderBy: { windowCloseAt: 'asc' },
}) })
return rounds.map((r) => ({ // Filter by award track membership
roundName: r.name, const projectRoundIds = new Set(
windowCloseAt: r.windowCloseAt!, (await ctx.prisma.projectRoundState.findMany({
})) where: { projectId: project.id },
select: { roundId: true },
})).map((prs) => prs.roundId)
)
const isInAwardTrack = rounds.some(
(r) => r.specialAwardId && projectRoundIds.has(r.id)
)
return rounds
.filter((r) => {
if (r.specialAwardId && !projectRoundIds.has(r.id)) return false
if (!r.specialAwardId && isInAwardTrack && !projectRoundIds.has(r.id)) return false
return true
})
.map((r) => ({
roundName: r.specialAward ? `${r.specialAward.name}: ${r.name}` : r.name,
windowCloseAt: r.windowCloseAt!,
}))
}), }),
/** /**
@@ -1805,7 +1871,7 @@ export const applicantRouter = router({
if (!project?.programId) return [] if (!project?.programId) return []
// Find active rounds with file requirements // Find active rounds with file requirements
const rounds = await ctx.prisma.round.findMany({ const allRounds = await ctx.prisma.round.findMany({
where: { where: {
competition: { programId: project.programId }, competition: { programId: project.programId },
status: 'ROUND_ACTIVE', status: 'ROUND_ACTIVE',
@@ -1814,6 +1880,8 @@ export const applicantRouter = router({
select: { select: {
id: true, id: true,
name: true, name: true,
specialAwardId: true,
specialAward: { select: { name: true } },
fileRequirements: { fileRequirements: {
select: { id: true }, select: { id: true },
}, },
@@ -1821,6 +1889,22 @@ export const applicantRouter = router({
orderBy: { sortOrder: 'asc' }, orderBy: { sortOrder: 'asc' },
}) })
// Filter by award track membership
const projectRoundIds = new Set(
(await ctx.prisma.projectRoundState.findMany({
where: { projectId: project.id },
select: { roundId: true },
})).map((prs) => prs.roundId)
)
const isInAwardTrack = allRounds.some(
(r) => r.specialAwardId && projectRoundIds.has(r.id)
)
const rounds = allRounds.filter((r) => {
if (r.specialAwardId && !projectRoundIds.has(r.id)) return false
if (!r.specialAwardId && isInAwardTrack && !projectRoundIds.has(r.id)) return false
return true
})
const results: Array<{ roundId: string; roundName: string; required: number; uploaded: number }> = [] const results: Array<{ roundId: string; roundName: string; required: number; uploaded: number }> = []
for (const round of rounds) { for (const round of rounds) {
@@ -1836,7 +1920,7 @@ export const applicantRouter = router({
results.push({ results.push({
roundId: round.id, roundId: round.id,
roundName: round.name, roundName: round.specialAward ? `${round.specialAward.name}: ${round.name}` : round.name,
required: requirementIds.length, required: requirementIds.length,
uploaded, uploaded,
}) })