Add AI eligibility toggle and include-submitted filter for awards
- Add useAiEligibility boolean to SpecialAward schema (default true) - Toggle on creation form lets admins disable AI for feeling-based awards - Detail page shows "Load All Projects" when AI is off vs "Run AI Eligibility" - Include Submitted toggle lets admins include SUBMITTED-status projects - Fix perPage: 200 → 100 to match user.list validation max - Fix edition display on award detail page - Add migration for new column Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,7 +60,7 @@ export const specialAwardRouter = router({
|
||||
select: { id: true, title: true, teamName: true },
|
||||
},
|
||||
program: {
|
||||
select: { id: true, name: true },
|
||||
select: { id: true, name: true, year: true },
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -85,6 +85,7 @@ export const specialAwardRouter = router({
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
criteriaText: z.string().optional(),
|
||||
useAiEligibility: z.boolean().optional(),
|
||||
scoringMode: z.enum(['PICK_WINNER', 'RANKED', 'SCORED']),
|
||||
maxRankedPicks: z.number().int().min(1).max(20).optional(),
|
||||
autoTagRulesJson: z.record(z.unknown()).optional(),
|
||||
@@ -102,6 +103,7 @@ export const specialAwardRouter = router({
|
||||
name: input.name,
|
||||
description: input.description,
|
||||
criteriaText: input.criteriaText,
|
||||
useAiEligibility: input.useAiEligibility ?? true,
|
||||
scoringMode: input.scoringMode,
|
||||
maxRankedPicks: input.maxRankedPicks,
|
||||
autoTagRulesJson: input.autoTagRulesJson as Prisma.InputJsonValue ?? undefined,
|
||||
@@ -130,6 +132,7 @@ export const specialAwardRouter = router({
|
||||
name: z.string().min(1).optional(),
|
||||
description: z.string().optional(),
|
||||
criteriaText: z.string().optional(),
|
||||
useAiEligibility: z.boolean().optional(),
|
||||
scoringMode: z.enum(['PICK_WINNER', 'RANKED', 'SCORED']).optional(),
|
||||
maxRankedPicks: z.number().int().min(1).max(20).optional(),
|
||||
autoTagRulesJson: z.record(z.unknown()).optional(),
|
||||
@@ -220,18 +223,24 @@ export const specialAwardRouter = router({
|
||||
* Run auto-tag + AI eligibility
|
||||
*/
|
||||
runEligibility: adminProcedure
|
||||
.input(z.object({ awardId: z.string() }))
|
||||
.input(z.object({
|
||||
awardId: z.string(),
|
||||
includeSubmitted: z.boolean().optional(),
|
||||
}))
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const award = await ctx.prisma.specialAward.findUniqueOrThrow({
|
||||
where: { id: input.awardId },
|
||||
include: { program: true },
|
||||
})
|
||||
|
||||
// Get all projects in the program's rounds
|
||||
// Get projects in the program's rounds
|
||||
const statusFilter = input.includeSubmitted
|
||||
? (['SUBMITTED', 'ELIGIBLE', 'ASSIGNED', 'SEMIFINALIST', 'FINALIST'] as const)
|
||||
: (['ELIGIBLE', 'ASSIGNED', 'SEMIFINALIST', 'FINALIST'] as const)
|
||||
const projects = await ctx.prisma.project.findMany({
|
||||
where: {
|
||||
round: { programId: award.programId },
|
||||
status: { in: ['ELIGIBLE', 'ASSIGNED', 'SEMIFINALIST', 'FINALIST'] },
|
||||
status: { in: [...statusFilter] },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
@@ -259,9 +268,9 @@ export const specialAwardRouter = router({
|
||||
autoResults = applyAutoTagRules(autoTagRules, projects)
|
||||
}
|
||||
|
||||
// Phase 2: AI interpretation (if criteria text exists)
|
||||
// Phase 2: AI interpretation (if criteria text exists AND AI eligibility is enabled)
|
||||
let aiResults: Map<string, { eligible: boolean; confidence: number; reasoning: string }> | undefined
|
||||
if (award.criteriaText) {
|
||||
if (award.criteriaText && award.useAiEligibility) {
|
||||
const aiEvals = await aiInterpretCriteria(award.criteriaText, projects)
|
||||
aiResults = new Map(
|
||||
aiEvals.map((e) => [
|
||||
|
||||
Reference in New Issue
Block a user