feat: round finalization with ranking-based outcomes + award pool notifications
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m0s

- processRoundClose EVALUATION uses ranking scores + advanceMode config
  (threshold vs count) to auto-set proposedOutcome instead of defaulting all to PASSED
- Advancement emails generate invite tokens for passwordless users with
  "Create Your Account" CTA; rejection emails have no link
- Finalization UI shows account stats (invite vs dashboard link counts)
- Fixed getFinalizationSummary ranking query (was using non-existent rankingsJson)
- New award pool notification system: getAwardSelectionNotificationTemplate email,
  notifyEligibleProjects mutation with invite token generation,
  "Notify Pool" button on award detail page with custom message dialog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 19:14:41 +01:00
parent 7735f3ecdf
commit cfee3bc8a9
48 changed files with 5294 additions and 676 deletions

View File

@@ -0,0 +1,25 @@
import crypto from 'crypto'
import type { PrismaClient } from '@prisma/client'
const DEFAULT_INVITE_EXPIRY_HOURS = 72 // 3 days
export function generateInviteToken(): string {
return crypto.randomBytes(32).toString('hex')
}
export async function getInviteExpiryHours(prisma: PrismaClient): Promise<number> {
try {
const setting = await prisma.systemSettings.findUnique({
where: { key: 'invite_link_expiry_hours' },
select: { value: true },
})
const hours = setting?.value ? parseInt(setting.value, 10) : DEFAULT_INVITE_EXPIRY_HOURS
return isNaN(hours) || hours < 1 ? DEFAULT_INVITE_EXPIRY_HOURS : hours
} catch {
return DEFAULT_INVITE_EXPIRY_HOURS
}
}
export async function getInviteExpiryMs(prisma: PrismaClient): Promise<number> {
return (await getInviteExpiryHours(prisma)) * 60 * 60 * 1000
}