Fix award source round dropdown — auto-resolve competitionId from program
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m19s

Awards created from /admin/awards/new only sent programId, leaving
competitionId null. The edit page's source round dropdown was empty
because it depended on competitionId to fetch competition rounds.

- create mutation: auto-resolve competitionId from program's latest competition
- update mutation: backfill competitionId on save if missing
- get query: backfill competitionId on read for legacy awards
- edit page: use award.competition.rounds directly instead of separate query

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-17 21:00:20 +01:00
parent 619206c03f
commit 70d24036f9
2 changed files with 55 additions and 15 deletions

View File

@@ -38,12 +38,8 @@ export default function EditAwardPage({
const utils = trpc.useUtils()
const { data: award, isLoading } = trpc.specialAward.get.useQuery({ id: awardId })
// Fetch competition rounds for source round selector
const competitionId = award?.competitionId
const { data: competition } = trpc.competition.getById.useQuery(
{ id: competitionId! },
{ enabled: !!competitionId }
)
// Rounds come from the award's included competition relation
const competitionRounds = award?.competition?.rounds ?? []
const updateAward = trpc.specialAward.update.useMutation({
onSuccess: () => {
@@ -266,13 +262,11 @@ export default function EditAwardPage({
</SelectTrigger>
<SelectContent>
<SelectItem value="none">No source round</SelectItem>
{competition?.rounds
?.sort((a, b) => a.sortOrder - b.sortOrder)
.map((round) => (
<SelectItem key={round.id} value={round.id}>
{round.name} ({round.roundType})
</SelectItem>
))}
{competitionRounds.map((round) => (
<SelectItem key={round.id} value={round.id}>
{round.name} ({round.roundType})
</SelectItem>
))}
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">

View File

@@ -70,12 +70,30 @@ export const specialAwardRouter = router({
},
})
// Auto-resolve competition if missing (legacy awards created without competitionId)
let { competition } = award
if (!competition && award.programId) {
const comp = await ctx.prisma.competition.findFirst({
where: { programId: award.programId },
orderBy: { createdAt: 'desc' },
select: { id: true, name: true, rounds: { select: { id: true, name: true, roundType: true, sortOrder: true }, orderBy: { sortOrder: 'asc' as const } } },
})
if (comp) {
competition = comp
// Backfill competitionId on the award
await ctx.prisma.specialAward.update({
where: { id: input.id },
data: { competitionId: comp.id },
})
}
}
// Count eligible projects
const eligibleCount = await ctx.prisma.awardEligibility.count({
where: { awardId: input.id, eligible: true },
})
return { ...award, eligibleCount }
return { ...award, competition, eligibleCount }
}),
// ─── Admin Mutations ────────────────────────────────────────────────────
@@ -100,6 +118,17 @@ export const specialAwardRouter = router({
})
)
.mutation(async ({ ctx, input }) => {
// Auto-resolve competitionId from program if not provided
let competitionId = input.competitionId
if (!competitionId) {
const comp = await ctx.prisma.competition.findFirst({
where: { programId: input.programId },
orderBy: { createdAt: 'desc' },
select: { id: true },
})
competitionId = comp?.id ?? undefined
}
const maxOrder = await ctx.prisma.specialAward.aggregate({
where: { programId: input.programId },
_max: { sortOrder: true },
@@ -114,7 +143,7 @@ export const specialAwardRouter = router({
useAiEligibility: input.useAiEligibility ?? true,
scoringMode: input.scoringMode,
maxRankedPicks: input.maxRankedPicks,
competitionId: input.competitionId,
competitionId,
evaluationRoundId: input.evaluationRoundId,
juryGroupId: input.juryGroupId,
eligibilityMode: input.eligibilityMode,
@@ -160,6 +189,23 @@ export const specialAwardRouter = router({
)
.mutation(async ({ ctx, input }) => {
const { id, ...rest } = input
// Auto-resolve competitionId if missing on existing award
if (rest.competitionId === undefined) {
const existing = await ctx.prisma.specialAward.findUnique({
where: { id },
select: { competitionId: true, programId: true },
})
if (existing && !existing.competitionId) {
const comp = await ctx.prisma.competition.findFirst({
where: { programId: existing.programId },
orderBy: { createdAt: 'desc' },
select: { id: true },
})
if (comp) rest.competitionId = comp.id
}
}
const award = await ctx.prisma.specialAward.update({
where: { id },
data: rest,