Fix multi-click submit bug and add draft submit indicator on juror dashboard
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m24s

- Initialize slider values to midpoint so visual matches stored value
  (root cause: sliders appeared filled but criteriaValues was undefined)
- Use mutateAsync for submit to properly await and prevent double-clicks
- Add startMutation.isPending to submit button disabled state
- Add error toast in evaluation-form.tsx catch block (was silent)
- Show "Ready to submit" badge and "Review & Submit" button for drafts
  on juror dashboard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 17:29:13 +01:00
parent ab2c73bad2
commit 3bc6552f47
3 changed files with 51 additions and 12 deletions

View File

@@ -173,6 +173,24 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
}
})
// Initialize numeric criteria with midpoint values so slider visual matches stored value.
const criteriaInitializedRef = useRef(false)
useEffect(() => {
if (criteriaInitializedRef.current || criteria.length === 0) return
if (existingEvaluation?.criterionScoresJson) return
criteriaInitializedRef.current = true
const defaults: Record<string, number | boolean | string> = {}
for (const c of criteria) {
if (c.type === 'numeric') {
defaults[c.id] = Math.ceil((c.minScore + c.maxScore) / 2)
}
}
if (Object.keys(defaults).length > 0) {
setCriteriaValues((prev) => ({ ...defaults, ...prev }))
}
}, [criteria, existingEvaluation?.criterionScoresJson])
// Build current form data for autosave
const buildSavePayload = useCallback(() => {
return {
@@ -370,13 +388,17 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
}
}
submitMutation.mutate({
id: evaluationId,
criterionScoresJson: scoringMode === 'criteria' ? criteriaValues : {},
globalScore: scoringMode === 'global' ? parseInt(globalScore, 10) : computedGlobalScore,
binaryDecision: scoringMode === 'binary' ? binaryDecision === 'accept' : true,
feedbackText: feedbackText || 'No feedback provided',
})
try {
await submitMutation.mutateAsync({
id: evaluationId,
criterionScoresJson: scoringMode === 'criteria' ? criteriaValues : {},
globalScore: scoringMode === 'global' ? parseInt(globalScore, 10) : computedGlobalScore,
binaryDecision: scoringMode === 'binary' ? binaryDecision === 'accept' : true,
feedbackText: feedbackText || 'No feedback provided',
})
} catch {
// Error toast already handled by onError callback
}
}
if (!round || !project) {
@@ -840,7 +862,7 @@ export default function JuryEvaluatePage({ params: paramsPromise }: PageProps) {
</Button>
<Button
onClick={handleSubmit}
disabled={submitMutation.isPending || autosaveMutation.isPending}
disabled={submitMutation.isPending || autosaveMutation.isPending || startMutation.isPending}
className="bg-brand-blue hover:bg-brand-blue-light"
>
<Send className="mr-2 h-4 w-4" />