Reopen rounds, file type buttons, checklist live-update
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m1s
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m1s
- Add reopenRound() to round engine (CLOSED → ACTIVE) with auto-pause of subsequent active rounds - Add reopen endpoint to roundEngine router and UI button on round detail page - Replace free-text MIME type input with toggle-only badge buttons in file requirements editor - Enable refetchOnWindowFocus and shorter polling intervals for readiness checklist queries Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -76,6 +76,7 @@ import {
|
||||
Plus,
|
||||
Trash2,
|
||||
ArrowRight,
|
||||
RotateCcw,
|
||||
X,
|
||||
} from 'lucide-react'
|
||||
import { RoundConfigForm } from '@/components/admin/competition/round-config-form'
|
||||
@@ -159,22 +160,22 @@ export default function RoundDetailPage() {
|
||||
// ── Core data queries ──────────────────────────────────────────────────
|
||||
const { data: round, isLoading } = trpc.round.getById.useQuery(
|
||||
{ id: roundId },
|
||||
{ refetchInterval: 30_000 },
|
||||
{ refetchInterval: 15_000, refetchOnWindowFocus: true },
|
||||
)
|
||||
const { data: projectStates } = trpc.roundEngine.getProjectStates.useQuery(
|
||||
{ roundId },
|
||||
{ refetchInterval: 15_000 },
|
||||
{ refetchInterval: 10_000, refetchOnWindowFocus: true },
|
||||
)
|
||||
|
||||
const competitionId = round?.competitionId ?? ''
|
||||
|
||||
const { data: juryGroups } = trpc.juryGroup.list.useQuery(
|
||||
{ competitionId },
|
||||
{ enabled: !!competitionId, refetchInterval: 30_000 },
|
||||
{ enabled: !!competitionId, refetchInterval: 30_000, refetchOnWindowFocus: true },
|
||||
)
|
||||
const { data: fileRequirements } = trpc.file.listRequirements.useQuery(
|
||||
{ roundId },
|
||||
{ refetchInterval: 30_000 },
|
||||
{ refetchInterval: 15_000, refetchOnWindowFocus: true },
|
||||
)
|
||||
|
||||
// Fetch awards linked to this round
|
||||
@@ -223,6 +224,18 @@ export default function RoundDetailPage() {
|
||||
onError: (err) => toast.error(err.message),
|
||||
})
|
||||
|
||||
const reopenMutation = trpc.roundEngine.reopen.useMutation({
|
||||
onSuccess: (data) => {
|
||||
utils.round.getById.invalidate({ id: roundId })
|
||||
utils.roundEngine.getProjectStates.invalidate({ roundId })
|
||||
const msg = data.pausedRounds?.length
|
||||
? `Round reopened. Paused: ${data.pausedRounds.join(', ')}`
|
||||
: 'Round reopened'
|
||||
toast.success(msg)
|
||||
},
|
||||
onError: (err) => toast.error(err.message),
|
||||
})
|
||||
|
||||
const archiveMutation = trpc.roundEngine.archive.useMutation({
|
||||
onSuccess: () => {
|
||||
utils.round.getById.invalidate({ id: roundId })
|
||||
@@ -268,7 +281,7 @@ export default function RoundDetailPage() {
|
||||
},
|
||||
})
|
||||
|
||||
const isTransitioning = activateMutation.isPending || closeMutation.isPending || archiveMutation.isPending
|
||||
const isTransitioning = activateMutation.isPending || closeMutation.isPending || reopenMutation.isPending || archiveMutation.isPending
|
||||
|
||||
const handleConfigChange = useCallback((newConfig: Record<string, unknown>) => {
|
||||
setConfig(newConfig)
|
||||
@@ -437,11 +450,11 @@ export default function RoundDetailPage() {
|
||||
{status === 'ROUND_CLOSED' && (
|
||||
<>
|
||||
<DropdownMenuItem
|
||||
onClick={() => activateMutation.mutate({ roundId })}
|
||||
onClick={() => reopenMutation.mutate({ roundId })}
|
||||
disabled={isTransitioning}
|
||||
>
|
||||
<Play className="h-4 w-4 mr-2 text-emerald-600" />
|
||||
Reactivate Round
|
||||
Reopen Round
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
@@ -775,6 +788,36 @@ export default function RoundDetailPage() {
|
||||
</AlertDialog>
|
||||
)}
|
||||
|
||||
{status === 'ROUND_CLOSED' && (
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<button className="flex items-start gap-3 p-4 rounded-lg border hover:bg-muted/50 transition-colors text-left border-amber-200 bg-amber-50/50">
|
||||
<RotateCcw className="h-5 w-5 text-amber-600 mt-0.5 shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-medium">Reopen Round</p>
|
||||
<p className="text-xs text-muted-foreground mt-0.5">
|
||||
Reactivate this round. Any subsequent active rounds will be paused.
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Reopen this round?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
The round will become active again. Any rounds after this one that are currently active will be paused (closed) automatically.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||
<AlertDialogAction onClick={() => reopenMutation.mutate({ roundId })}>
|
||||
Reopen
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
)}
|
||||
|
||||
{/* Assign projects */}
|
||||
<Link href={poolLink}>
|
||||
<button className="flex items-start gap-3 p-4 rounded-lg border hover:bg-muted/50 transition-colors text-left w-full">
|
||||
|
||||
Reference in New Issue
Block a user