Fix evaluation double-click submit: autosave was blocking the submit button
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m35s
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m35s
Root cause: submit button was disabled when autosaveMutation.isPending was true. If the 3-second autosave timer fired while the user clicked Submit, the click was silently swallowed. User had to wait for autosave to finish, then click again. Fixes: - Remove autosaveMutation.isPending from submit button disabled state - Cancel pending autosave timer when submit starts (prevents race condition) - Add isSubmittingRef guard to prevent autosave from firing during submit - Reset submitting flag on validation failure or submit error Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
const isDirtyRef = useRef(false)
|
||||
const evaluationIdRef = useRef<string | null>(null)
|
||||
const isSubmittedRef = useRef(false)
|
||||
const isSubmittingRef = useRef(false)
|
||||
const autosaveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
||||
const [lastSavedAt, setLastSavedAt] = useState<Date | null>(null)
|
||||
|
||||
@@ -203,7 +204,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
|
||||
// Perform autosave
|
||||
const performAutosave = useCallback(async () => {
|
||||
if (!isDirtyRef.current || isSubmittedRef.current) return
|
||||
if (!isDirtyRef.current || isSubmittedRef.current || isSubmittingRef.current) return
|
||||
if (existingEvaluation?.status === 'SUBMITTED') return
|
||||
|
||||
let evalId = evaluationIdRef.current
|
||||
@@ -311,8 +312,16 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
// Cancel any pending autosave to avoid race conditions
|
||||
if (autosaveTimerRef.current) {
|
||||
clearTimeout(autosaveTimerRef.current)
|
||||
autosaveTimerRef.current = null
|
||||
}
|
||||
isSubmittingRef.current = true
|
||||
|
||||
if (!myAssignment) {
|
||||
toast.error('Assignment not found')
|
||||
isSubmittingRef.current = false
|
||||
return
|
||||
}
|
||||
|
||||
@@ -325,14 +334,17 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
const val = criteriaValues[c.id]
|
||||
if (c.type === 'numeric' && (val === undefined || val === null)) {
|
||||
toast.error(`Please score "${c.label}"`)
|
||||
isSubmittingRef.current = false
|
||||
return
|
||||
}
|
||||
if (c.type === 'boolean' && val === undefined) {
|
||||
toast.error(`Please answer "${c.label}"`)
|
||||
isSubmittingRef.current = false
|
||||
return
|
||||
}
|
||||
if (c.type === 'text' && (!val || (typeof val === 'string' && !val.trim()))) {
|
||||
toast.error(`Please fill in "${c.label}"`)
|
||||
isSubmittingRef.current = false
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -342,6 +354,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
const score = parseInt(globalScore, 10)
|
||||
if (isNaN(score) || score < 1 || score > 10) {
|
||||
toast.error('Please enter a valid score between 1 and 10')
|
||||
isSubmittingRef.current = false
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -349,6 +362,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
if (scoringMode === 'binary') {
|
||||
if (!binaryDecision) {
|
||||
toast.error('Please select accept or reject')
|
||||
isSubmittingRef.current = false
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -356,6 +370,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
if (requireFeedback) {
|
||||
if (!feedbackText.trim() || feedbackText.length < feedbackMinLength) {
|
||||
toast.error(`Please provide feedback (minimum ${feedbackMinLength} characters)`)
|
||||
isSubmittingRef.current = false
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -398,6 +413,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
})
|
||||
} catch {
|
||||
// Error toast already handled by onError callback
|
||||
isSubmittingRef.current = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,7 +878,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
disabled={submitMutation.isPending || autosaveMutation.isPending || startMutation.isPending}
|
||||
disabled={submitMutation.isPending || startMutation.isPending}
|
||||
className="bg-brand-blue hover:bg-brand-blue-light"
|
||||
>
|
||||
<Send className="mr-2 h-4 w-4" />
|
||||
|
||||
Reference in New Issue
Block a user