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,7 +468,9 @@ export default function RoundDetailPage() {
|
|||||||
action: undefined as Route | undefined,
|
action: undefined as Route | undefined,
|
||||||
actionLabel: undefined as string | undefined,
|
actionLabel: undefined as string | undefined,
|
||||||
},
|
},
|
||||||
{
|
...((isEvaluation && !(config.requireDocumentUpload as boolean))
|
||||||
|
? []
|
||||||
|
: [{
|
||||||
label: 'File requirements set',
|
label: 'File requirements set',
|
||||||
ready: (fileRequirements?.length ?? 0) > 0,
|
ready: (fileRequirements?.length ?? 0) > 0,
|
||||||
detail:
|
detail:
|
||||||
@@ -477,7 +479,7 @@ export default function RoundDetailPage() {
|
|||||||
: 'No file requirements \u2014 configure in Config tab',
|
: 'No file requirements \u2014 configure in Config tab',
|
||||||
action: undefined as Route | undefined,
|
action: undefined as Route | undefined,
|
||||||
actionLabel: undefined as string | undefined,
|
actionLabel: undefined as string | undefined,
|
||||||
},
|
}]),
|
||||||
]
|
]
|
||||||
const readyCount = readinessItems.filter((i) => i.ready).length
|
const readyCount = readinessItems.filter((i) => i.ready).length
|
||||||
|
|
||||||
@@ -1738,7 +1740,8 @@ export default function RoundDetailPage() {
|
|||||||
{/* Evaluation Criteria Editor (EVALUATION rounds only) */}
|
{/* Evaluation Criteria Editor (EVALUATION rounds only) */}
|
||||||
{isEvaluation && <EvaluationCriteriaEditor roundId={roundId} />}
|
{isEvaluation && <EvaluationCriteriaEditor roundId={roundId} />}
|
||||||
|
|
||||||
{/* Document Requirements */}
|
{/* Document Requirements — hidden for EVALUATION rounds unless requireDocumentUpload is on */}
|
||||||
|
{(!isEvaluation || !!(config.requireDocumentUpload as boolean)) && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-base">Document Requirements</CardTitle>
|
<CardTitle className="text-base">Document Requirements</CardTitle>
|
||||||
@@ -1757,6 +1760,7 @@ export default function RoundDetailPage() {
|
|||||||
/>
|
/>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
)}
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
{/* ═══════════ AWARDS TAB ═══════════ */}
|
{/* ═══════════ AWARDS TAB ═══════════ */}
|
||||||
|
|||||||
@@ -299,14 +299,10 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if round is active and voting window is open
|
// Check if round is active — round status is the primary gate for evaluations
|
||||||
const now = new Date()
|
|
||||||
const isRoundActive = round.status === 'ROUND_ACTIVE'
|
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 (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
@@ -325,9 +321,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
|||||||
<div>
|
<div>
|
||||||
<h2 className="text-lg font-semibold">Evaluation Not Available</h2>
|
<h2 className="text-lg font-semibold">Evaluation Not Available</h2>
|
||||||
<p className="text-sm text-muted-foreground mt-1">
|
<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.
|
||||||
? '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.'}
|
|
||||||
</p>
|
</p>
|
||||||
<Button variant="outline" size="sm" className="mt-4" asChild>
|
<Button variant="outline" size="sm" className="mt-4" asChild>
|
||||||
<Link href={`/jury/competitions/${roundId}/projects/${projectId}` as Route}>
|
<Link href={`/jury/competitions/${roundId}/projects/${projectId}` as Route}>
|
||||||
|
|||||||
@@ -27,11 +27,8 @@ export default function JuryProjectDetailPage() {
|
|||||||
{ enabled: !!roundId }
|
{ enabled: !!roundId }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Determine if voting is currently open
|
// Round status is the primary gate for evaluations
|
||||||
const now = new Date()
|
const isVotingOpen = round?.status === 'ROUND_ACTIVE'
|
||||||
const isVotingOpen = round?.status === 'ROUND_ACTIVE' &&
|
|
||||||
round?.windowOpenAt && round?.windowCloseAt &&
|
|
||||||
new Date(round.windowOpenAt) <= now && new Date(round.windowCloseAt) >= now
|
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -357,12 +357,7 @@ async function JuryDashboardContent() {
|
|||||||
const evaluation = assignment.evaluation
|
const evaluation = assignment.evaluation
|
||||||
const isCompleted = evaluation?.status === 'SUBMITTED'
|
const isCompleted = evaluation?.status === 'SUBMITTED'
|
||||||
const isDraft = evaluation?.status === 'DRAFT'
|
const isDraft = evaluation?.status === 'DRAFT'
|
||||||
const isVotingOpen =
|
const isVotingOpen = assignment.round.status === 'ROUND_ACTIVE'
|
||||||
assignment.round.status === 'ROUND_ACTIVE' &&
|
|
||||||
assignment.round.windowOpenAt &&
|
|
||||||
assignment.round.windowCloseAt &&
|
|
||||||
new Date(assignment.round.windowOpenAt) <= now &&
|
|
||||||
new Date(assignment.round.windowCloseAt) >= now
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|||||||
@@ -143,6 +143,18 @@ export function EvaluationConfig({ config, onChange }: EvaluationConfigProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</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 className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<Label htmlFor="peerReviewEnabled">Peer Review</Label>
|
<Label htmlFor="peerReviewEnabled">Peer Review</Label>
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ export const EvaluationConfigSchema = z.object({
|
|||||||
feedbackMinLength: z.number().int().nonnegative().default(0),
|
feedbackMinLength: z.number().int().nonnegative().default(0),
|
||||||
requireAllCriteriaScored: z.boolean().default(true),
|
requireAllCriteriaScored: z.boolean().default(true),
|
||||||
|
|
||||||
|
requireDocumentUpload: z.boolean().default(false),
|
||||||
|
|
||||||
coiRequired: z.boolean().default(true),
|
coiRequired: z.boolean().default(true),
|
||||||
|
|
||||||
peerReviewEnabled: z.boolean().default(false),
|
peerReviewEnabled: z.boolean().default(false),
|
||||||
|
|||||||
Reference in New Issue
Block a user