diff --git a/sites/mainweb/app/(portal)/admin/hackathons/page.tsx b/sites/mainweb/app/(portal)/admin/hackathons/page.tsx index b3ddf6e7..f5e374cc 100644 --- a/sites/mainweb/app/(portal)/admin/hackathons/page.tsx +++ b/sites/mainweb/app/(portal)/admin/hackathons/page.tsx @@ -80,6 +80,9 @@ export default function AdminHackathonsPage() { hackathonId={editingId} onClose={() => setEditingId(null)} onSaved={() => { + // The form seeds itself from getById; left cached, reopening it + // shows the old values and a second save writes them back. + utils.hackathon.getById.invalidate({ id: editingId }); setEditingId(null); utils.hackathon.listAll.invalidate(); }} diff --git a/sites/mainweb/app/(portal)/admin/judging/page.tsx b/sites/mainweb/app/(portal)/admin/judging/page.tsx index 3cbb0f8e..6cfde65c 100644 --- a/sites/mainweb/app/(portal)/admin/judging/page.tsx +++ b/sites/mainweb/app/(portal)/admin/judging/page.tsx @@ -101,7 +101,13 @@ export default function AdminResultsPage() { gen: number, message: string, ) => { - await utils.judge.getRankings.invalidate({ hackathonId }); + // The live board and table cards read what prepare just built; neither + // polls until judging is open. + await Promise.all([ + utils.judge.getRankings.invalidate({ hackathonId }), + utils.judge.liveProgress.invalidate(), + utils.judge.tableCards.invalidate(), + ]); if (!stillThisRun(hackathonId, gen)) return; await refetchJudgingStatus(); if (!stillThisRun(hackathonId, gen)) return; diff --git a/sites/mainweb/app/(portal)/judge/register/page.tsx b/sites/mainweb/app/(portal)/judge/register/page.tsx index 767d5aff..2e43bf85 100644 --- a/sites/mainweb/app/(portal)/judge/register/page.tsx +++ b/sites/mainweb/app/(portal)/judge/register/page.tsx @@ -35,8 +35,14 @@ export default function JudgeRegisterPage() { const [success, setSuccess] = useState(false); const { data: hackathons, isLoading } = trpc.hackathon.list.useQuery({}); + const utils = trpc.useUtils(); const registerMutation = trpc.judge.register.useMutation({ - onSuccess: () => setSuccess(true), + onSuccess: () => { + setSuccess(true); + // Or /judge still offers "Apply to Judge", which now throws "already + // applied". + void utils.judge.myApplications.invalidate(); + }, onError: (e) => setError(e.message), }); diff --git a/sites/mainweb/app/(portal)/submit/page.tsx b/sites/mainweb/app/(portal)/submit/page.tsx index 55d7631b..ca7a329e 100644 --- a/sites/mainweb/app/(portal)/submit/page.tsx +++ b/sites/mainweb/app/(portal)/submit/page.tsx @@ -119,8 +119,12 @@ function SubmitPortalContent() { // We get the specific registration / team context based on selected hackathon const currentReg = myRegs?.find((r) => r.hackathonId === selectedHackathonId); + // A withdrawn project stays as a draft row, so the row alone is not a + // submission. const hasSubmitted = - !!mySubmission.data || currentReg?.hasSubmittedProject || projectSubmitted; + (!!mySubmission.data && mySubmission.data.status !== "draft") || + currentReg?.hasSubmittedProject || + projectSubmitted; // Mutations // Joining, creating or leaving a team changes which project this participant diff --git a/sites/mainweb/components/admin/hackathons/AttendeesTab.tsx b/sites/mainweb/components/admin/hackathons/AttendeesTab.tsx index 6656b5f9..7e0fc10b 100644 --- a/sites/mainweb/components/admin/hackathons/AttendeesTab.tsx +++ b/sites/mainweb/components/admin/hackathons/AttendeesTab.tsx @@ -117,6 +117,7 @@ export function AttendeesTab({ utils.hackathon.analytics.invalidate({ hackathonId }); utils.hackathon.getById.invalidate({ id: hackathonId }); utils.hackathon.listAll.invalidate(); + utils.hackathon.waveStatus.invalidate({ hackathonId }); }, // Same reasoning as the batch mutation below. This is the action an // organiser repeats 2000 times; a rate-limit or NOT_FOUND leaves the badge @@ -134,6 +135,7 @@ export function AttendeesTab({ utils.hackathon.analytics.invalidate({ hackathonId }); utils.hackathon.getById.invalidate({ id: hackathonId }); utils.hackathon.listAll.invalidate(); + utils.hackathon.waveStatus.invalidate({ hackathonId }); setSelectedIds(new Set()); setBulkError(null); }, @@ -323,6 +325,7 @@ export function AttendeesTab({ utils.hackathon.analytics.invalidate({ hackathonId }); utils.hackathon.getById.invalidate({ id: hackathonId }); utils.hackathon.listAll.invalidate(); + utils.hackathon.waveStatus.invalidate({ hackathonId }); }; const toggleSelect = (id: string) => {