Fix voting gate to use round status, make eval doc uploads toggleable
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m26s
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m26s
Voting check now uses round.status === ROUND_ACTIVE instead of requiring windowOpenAt/windowCloseAt date range, fixing manual open/reopen scenarios. Added requireDocumentUpload toggle (default off) to evaluation round config so rounds reusing prior-round documents don't need file requirements. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -468,16 +468,18 @@ export default function RoundDetailPage() {
|
||||
action: undefined as Route | undefined,
|
||||
actionLabel: undefined as string | undefined,
|
||||
},
|
||||
{
|
||||
label: 'File requirements set',
|
||||
ready: (fileRequirements?.length ?? 0) > 0,
|
||||
detail:
|
||||
(fileRequirements?.length ?? 0) > 0
|
||||
? `${fileRequirements?.length} requirement(s)`
|
||||
: 'No file requirements \u2014 configure in Config tab',
|
||||
action: undefined as Route | undefined,
|
||||
actionLabel: undefined as string | undefined,
|
||||
},
|
||||
...((isEvaluation && !(config.requireDocumentUpload as boolean))
|
||||
? []
|
||||
: [{
|
||||
label: 'File requirements set',
|
||||
ready: (fileRequirements?.length ?? 0) > 0,
|
||||
detail:
|
||||
(fileRequirements?.length ?? 0) > 0
|
||||
? `${fileRequirements?.length} requirement(s)`
|
||||
: 'No file requirements \u2014 configure in Config tab',
|
||||
action: undefined as Route | undefined,
|
||||
actionLabel: undefined as string | undefined,
|
||||
}]),
|
||||
]
|
||||
const readyCount = readinessItems.filter((i) => i.ready).length
|
||||
|
||||
@@ -1738,25 +1740,27 @@ export default function RoundDetailPage() {
|
||||
{/* Evaluation Criteria Editor (EVALUATION rounds only) */}
|
||||
{isEvaluation && <EvaluationCriteriaEditor roundId={roundId} />}
|
||||
|
||||
{/* Document Requirements */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Document Requirements</CardTitle>
|
||||
<CardDescription>
|
||||
Files applicants must submit for this round
|
||||
{round.windowCloseAt && (
|
||||
<> — due by {new Date(round.windowCloseAt).toLocaleDateString()}</>
|
||||
)}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<FileRequirementsEditor
|
||||
roundId={roundId}
|
||||
windowOpenAt={round.windowOpenAt}
|
||||
windowCloseAt={round.windowCloseAt}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{/* Document Requirements — hidden for EVALUATION rounds unless requireDocumentUpload is on */}
|
||||
{(!isEvaluation || !!(config.requireDocumentUpload as boolean)) && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">Document Requirements</CardTitle>
|
||||
<CardDescription>
|
||||
Files applicants must submit for this round
|
||||
{round.windowCloseAt && (
|
||||
<> — due by {new Date(round.windowCloseAt).toLocaleDateString()}</>
|
||||
)}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<FileRequirementsEditor
|
||||
roundId={roundId}
|
||||
windowOpenAt={round.windowOpenAt}
|
||||
windowCloseAt={round.windowCloseAt}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
{/* ═══════════ AWARDS TAB ═══════════ */}
|
||||
|
||||
@@ -299,14 +299,10 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
)
|
||||
}
|
||||
|
||||
// Check if round is active and voting window is open
|
||||
const now = new Date()
|
||||
// Check if round is active — round status is the primary gate for evaluations
|
||||
const isRoundActive = round.status === 'ROUND_ACTIVE'
|
||||
const isWindowOpen = isRoundActive &&
|
||||
round.windowOpenAt && round.windowCloseAt &&
|
||||
new Date(round.windowOpenAt) <= now && new Date(round.windowCloseAt) >= now
|
||||
|
||||
if (!isWindowOpen) {
|
||||
if (!isRoundActive) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-4">
|
||||
@@ -325,9 +321,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold">Evaluation Not Available</h2>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
{!isRoundActive
|
||||
? 'This round is not currently active. Evaluations can only be submitted during an active round.'
|
||||
: 'The voting window for this round is not currently open. Please check back when the window opens.'}
|
||||
This round is not currently active. Evaluations can only be submitted during an active round.
|
||||
</p>
|
||||
<Button variant="outline" size="sm" className="mt-4" asChild>
|
||||
<Link href={`/jury/competitions/${roundId}/projects/${projectId}` as Route}>
|
||||
|
||||
@@ -27,11 +27,8 @@ export default function JuryProjectDetailPage() {
|
||||
{ enabled: !!roundId }
|
||||
)
|
||||
|
||||
// Determine if voting is currently open
|
||||
const now = new Date()
|
||||
const isVotingOpen = round?.status === 'ROUND_ACTIVE' &&
|
||||
round?.windowOpenAt && round?.windowCloseAt &&
|
||||
new Date(round.windowOpenAt) <= now && new Date(round.windowCloseAt) >= now
|
||||
// Round status is the primary gate for evaluations
|
||||
const isVotingOpen = round?.status === 'ROUND_ACTIVE'
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
|
||||
@@ -357,12 +357,7 @@ async function JuryDashboardContent() {
|
||||
const evaluation = assignment.evaluation
|
||||
const isCompleted = evaluation?.status === 'SUBMITTED'
|
||||
const isDraft = evaluation?.status === 'DRAFT'
|
||||
const isVotingOpen =
|
||||
assignment.round.status === 'ROUND_ACTIVE' &&
|
||||
assignment.round.windowOpenAt &&
|
||||
assignment.round.windowCloseAt &&
|
||||
new Date(assignment.round.windowOpenAt) <= now &&
|
||||
new Date(assignment.round.windowCloseAt) >= now
|
||||
const isVotingOpen = assignment.round.status === 'ROUND_ACTIVE'
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -143,6 +143,18 @@ export function EvaluationConfig({ config, onChange }: EvaluationConfigProps) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label htmlFor="requireDocumentUpload">Require Document Upload</Label>
|
||||
<p className="text-xs text-muted-foreground">Applicants must upload documents for this evaluation round (disable if documents were uploaded in a previous round)</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="requireDocumentUpload"
|
||||
checked={(config.requireDocumentUpload as boolean) ?? false}
|
||||
onCheckedChange={(v) => update('requireDocumentUpload', v)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label htmlFor="peerReviewEnabled">Peer Review</Label>
|
||||
|
||||
@@ -83,6 +83,8 @@ export const EvaluationConfigSchema = z.object({
|
||||
feedbackMinLength: z.number().int().nonnegative().default(0),
|
||||
requireAllCriteriaScored: z.boolean().default(true),
|
||||
|
||||
requireDocumentUpload: z.boolean().default(false),
|
||||
|
||||
coiRequired: z.boolean().default(true),
|
||||
|
||||
peerReviewEnabled: z.boolean().default(false),
|
||||
|
||||
Reference in New Issue
Block a user