Add background filtering jobs, improved date picker, AI reasoning display

- Implement background job system for AI filtering to avoid HTTP timeouts
- Add FilteringJob model to track progress of long-running filtering operations
- Add real-time progress polling for filtering operations on round details page
- Create custom DateTimePicker component with calendar popup (no year picker hassle)
- Fix round date persistence bug (refetchOnWindowFocus was resetting form state)
- Integrate filtering controls into round details page for filtering rounds
- Display AI reasoning for flagged/filtered projects in results table
- Add onboarding system scaffolding (schema, routes, basic UI)
- Allow setting round dates in the past for manual overrides

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-03 19:48:41 +01:00
parent 8be740a4fb
commit e2782b2b19
24 changed files with 3692 additions and 443 deletions

View File

@@ -167,6 +167,15 @@ enum FormFieldType {
INSTRUCTIONS
}
enum SpecialFieldType {
TEAM_MEMBERS // Team member repeater
COMPETITION_CATEGORY // Business Concept vs Startup
OCEAN_ISSUE // Ocean issue dropdown
FILE_UPLOAD // File upload
GDPR_CONSENT // GDPR consent checkbox
COUNTRY_SELECT // Country dropdown
}
// =============================================================================
// APPLICANT SYSTEM ENUMS
// =============================================================================
@@ -376,14 +385,16 @@ model Round {
updatedAt DateTime @updatedAt
// Relations
program Program @relation(fields: [programId], references: [id], onDelete: Cascade)
roundProjects RoundProject[]
assignments Assignment[]
evaluationForms EvaluationForm[]
gracePeriods GracePeriod[]
program Program @relation(fields: [programId], references: [id], onDelete: Cascade)
roundProjects RoundProject[]
assignments Assignment[]
evaluationForms EvaluationForm[]
gracePeriods GracePeriod[]
liveVotingSession LiveVotingSession?
filteringRules FilteringRule[]
filteringResults FilteringResult[]
filteringRules FilteringRule[]
filteringResults FilteringResult[]
filteringJobs FilteringJob[]
applicationForm ApplicationForm?
@@index([programId])
@@index([status])
@@ -858,22 +869,35 @@ model ApplicationForm {
confirmationMessage String? @db.Text
// Round linking (for onboarding forms that create projects)
roundId String? @unique
// Email settings
sendConfirmationEmail Boolean @default(true)
sendTeamInviteEmails Boolean @default(true)
confirmationEmailSubject String?
confirmationEmailBody String? @db.Text
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
program Program? @relation(fields: [programId], references: [id], onDelete: SetNull)
round Round? @relation(fields: [roundId], references: [id], onDelete: SetNull)
fields ApplicationFormField[]
steps OnboardingStep[]
submissions ApplicationFormSubmission[]
@@index([programId])
@@index([status])
@@index([isPublic])
@@index([roundId])
}
model ApplicationFormField {
id String @id @default(cuid())
formId String
stepId String? // Which step this field belongs to (for onboarding)
fieldType FormFieldType
name String // Internal name (e.g., "project_title")
label String // Display label (e.g., "Project Title")
@@ -889,6 +913,10 @@ model ApplicationFormField {
optionsJson Json? @db.JsonB // For select/radio: [{ value, label }]
conditionJson Json? @db.JsonB // Conditional logic: { fieldId, operator, value }
// Onboarding-specific fields
projectMapping String? // Maps to Project column: "title", "description", etc.
specialType SpecialFieldType? // Special handling for complex fields
sortOrder Int @default(0)
width String @default("full") // full, half
@@ -896,7 +924,30 @@ model ApplicationFormField {
updatedAt DateTime @updatedAt
// Relations
form ApplicationForm @relation(fields: [formId], references: [id], onDelete: Cascade)
form ApplicationForm @relation(fields: [formId], references: [id], onDelete: Cascade)
step OnboardingStep? @relation(fields: [stepId], references: [id], onDelete: SetNull)
@@index([formId])
@@index([stepId])
@@index([sortOrder])
}
model OnboardingStep {
id String @id @default(cuid())
formId String
name String // Internal identifier (e.g., "category", "contact")
title String // Display title (e.g., "Category", "Contact Information")
description String? @db.Text
sortOrder Int @default(0)
isOptional Boolean @default(false)
conditionJson Json? @db.JsonB // Conditional visibility: { fieldId, operator, value }
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
form ApplicationForm @relation(fields: [formId], references: [id], onDelete: Cascade)
fields ApplicationFormField[]
@@index([formId])
@@index([sortOrder])
@@ -1117,6 +1168,39 @@ model FilteringResult {
@@index([outcome])
}
// Tracks progress of long-running filtering jobs
model FilteringJob {
id String @id @default(cuid())
roundId String
status FilteringJobStatus @default(PENDING)
totalProjects Int @default(0)
totalBatches Int @default(0)
currentBatch Int @default(0)
processedCount Int @default(0)
passedCount Int @default(0)
filteredCount Int @default(0)
flaggedCount Int @default(0)
errorMessage String? @db.Text
startedAt DateTime?
completedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relations
round Round @relation(fields: [roundId], references: [id], onDelete: Cascade)
@@index([roundId])
@@index([status])
}
enum FilteringJobStatus {
PENDING
RUNNING
COMPLETED
FAILED
}
// =============================================================================
// SPECIAL AWARDS SYSTEM
// =============================================================================