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:
@@ -33,7 +33,7 @@ import {
|
||||
} from '@/components/forms/evaluation-form-builder'
|
||||
import { RoundTypeSettings } from '@/components/forms/round-type-settings'
|
||||
import { ArrowLeft, Loader2, AlertCircle, AlertTriangle } from 'lucide-react'
|
||||
import { format } from 'date-fns'
|
||||
import { DateTimePicker } from '@/components/ui/datetime-picker'
|
||||
|
||||
interface PageProps {
|
||||
params: Promise<{ id: string }>
|
||||
@@ -43,13 +43,13 @@ const updateRoundSchema = z
|
||||
.object({
|
||||
name: z.string().min(1, 'Name is required').max(255),
|
||||
requiredReviews: z.number().int().min(1).max(10),
|
||||
votingStartAt: z.string().optional(),
|
||||
votingEndAt: z.string().optional(),
|
||||
votingStartAt: z.date().nullable().optional(),
|
||||
votingEndAt: z.date().nullable().optional(),
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
if (data.votingStartAt && data.votingEndAt) {
|
||||
return new Date(data.votingEndAt) > new Date(data.votingStartAt)
|
||||
return data.votingEndAt > data.votingStartAt
|
||||
}
|
||||
return true
|
||||
},
|
||||
@@ -61,25 +61,19 @@ const updateRoundSchema = z
|
||||
|
||||
type UpdateRoundForm = z.infer<typeof updateRoundSchema>
|
||||
|
||||
// Convert ISO date to datetime-local format
|
||||
function toDatetimeLocal(date: Date | string | null | undefined): string {
|
||||
if (!date) return ''
|
||||
const d = new Date(date)
|
||||
// Format: YYYY-MM-DDTHH:mm
|
||||
return format(d, "yyyy-MM-dd'T'HH:mm")
|
||||
}
|
||||
|
||||
function EditRoundContent({ roundId }: { roundId: string }) {
|
||||
const router = useRouter()
|
||||
const [criteria, setCriteria] = useState<Criterion[]>([])
|
||||
const [criteriaInitialized, setCriteriaInitialized] = useState(false)
|
||||
const [formInitialized, setFormInitialized] = useState(false)
|
||||
const [roundType, setRoundType] = useState<'FILTERING' | 'EVALUATION' | 'LIVE_EVENT'>('EVALUATION')
|
||||
const [roundSettings, setRoundSettings] = useState<Record<string, unknown>>({})
|
||||
|
||||
// Fetch round data
|
||||
const { data: round, isLoading: loadingRound } = trpc.round.get.useQuery({
|
||||
id: roundId,
|
||||
})
|
||||
// Fetch round data - disable refetch on focus to prevent overwriting user's edits
|
||||
const { data: round, isLoading: loadingRound } = trpc.round.get.useQuery(
|
||||
{ id: roundId },
|
||||
{ refetchOnWindowFocus: false }
|
||||
)
|
||||
|
||||
// Fetch evaluation form
|
||||
const { data: evaluationForm, isLoading: loadingForm } =
|
||||
@@ -110,25 +104,26 @@ function EditRoundContent({ roundId }: { roundId: string }) {
|
||||
defaultValues: {
|
||||
name: '',
|
||||
requiredReviews: 3,
|
||||
votingStartAt: '',
|
||||
votingEndAt: '',
|
||||
votingStartAt: null,
|
||||
votingEndAt: null,
|
||||
},
|
||||
})
|
||||
|
||||
// Update form when round data loads
|
||||
// Update form when round data loads - only initialize once
|
||||
useEffect(() => {
|
||||
if (round) {
|
||||
if (round && !formInitialized) {
|
||||
form.reset({
|
||||
name: round.name,
|
||||
requiredReviews: round.requiredReviews,
|
||||
votingStartAt: toDatetimeLocal(round.votingStartAt),
|
||||
votingEndAt: toDatetimeLocal(round.votingEndAt),
|
||||
votingStartAt: round.votingStartAt ? new Date(round.votingStartAt) : null,
|
||||
votingEndAt: round.votingEndAt ? new Date(round.votingEndAt) : null,
|
||||
})
|
||||
// Set round type and settings
|
||||
setRoundType((round.roundType as typeof roundType) || 'EVALUATION')
|
||||
setRoundSettings((round.settingsJson as Record<string, unknown>) || {})
|
||||
setFormInitialized(true)
|
||||
}
|
||||
}, [round, form])
|
||||
}, [round, form, formInitialized])
|
||||
|
||||
// Initialize criteria from evaluation form
|
||||
useEffect(() => {
|
||||
@@ -151,8 +146,8 @@ function EditRoundContent({ roundId }: { roundId: string }) {
|
||||
requiredReviews: data.requiredReviews,
|
||||
roundType,
|
||||
settingsJson: roundSettings,
|
||||
votingStartAt: data.votingStartAt ? new Date(data.votingStartAt) : null,
|
||||
votingEndAt: data.votingEndAt ? new Date(data.votingEndAt) : null,
|
||||
votingStartAt: data.votingStartAt ?? null,
|
||||
votingEndAt: data.votingEndAt ?? null,
|
||||
})
|
||||
|
||||
// Update evaluation form if criteria changed and no evaluations exist
|
||||
@@ -303,7 +298,11 @@ function EditRoundContent({ roundId }: { roundId: string }) {
|
||||
<FormItem>
|
||||
<FormLabel>Start Date & Time</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="datetime-local" {...field} />
|
||||
<DateTimePicker
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Select start date & time"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -317,7 +316,11 @@ function EditRoundContent({ roundId }: { roundId: string }) {
|
||||
<FormItem>
|
||||
<FormLabel>End Date & Time</FormLabel>
|
||||
<FormControl>
|
||||
<Input type="datetime-local" {...field} />
|
||||
<DateTimePicker
|
||||
value={field.value}
|
||||
onChange={field.onChange}
|
||||
placeholder="Select end date & time"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -326,7 +329,7 @@ function EditRoundContent({ roundId }: { roundId: string }) {
|
||||
</div>
|
||||
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Leave empty to disable the voting window enforcement.
|
||||
Leave empty to disable the voting window enforcement. Past dates are allowed.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user