From 70d24036f9135c41375dfdcdebdcd1505da07d8b Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 17 Feb 2026 21:00:20 +0100 Subject: [PATCH] =?UTF-8?q?Fix=20award=20source=20round=20dropdown=20?= =?UTF-8?q?=E2=80=94=20auto-resolve=20competitionId=20from=20program?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- .../(admin)/admin/awards/[id]/edit/page.tsx | 20 +++----- src/server/routers/specialAward.ts | 50 ++++++++++++++++++- 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/app/(admin)/admin/awards/[id]/edit/page.tsx b/src/app/(admin)/admin/awards/[id]/edit/page.tsx index 6d6c579..d1eaa74 100644 --- a/src/app/(admin)/admin/awards/[id]/edit/page.tsx +++ b/src/app/(admin)/admin/awards/[id]/edit/page.tsx @@ -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({ No source round - {competition?.rounds - ?.sort((a, b) => a.sortOrder - b.sortOrder) - .map((round) => ( - - {round.name} ({round.roundType}) - - ))} + {competitionRounds.map((round) => ( + + {round.name} ({round.roundType}) + + ))}

diff --git a/src/server/routers/specialAward.ts b/src/server/routers/specialAward.ts index c822044..a45ca25 100644 --- a/src/server/routers/specialAward.ts +++ b/src/server/routers/specialAward.ts @@ -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,