From 2688999389f07c60ac0cf160dd64cd64d2e4a023 Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Fri, 31 Jul 2026 12:35:21 +0100 Subject: [PATCH 1/2] fix: skip deleted projects --- .../checklist/ModerationChecklist.vue | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue b/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue index 3727b6197c..dd253ad1ef 100644 --- a/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue +++ b/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue @@ -764,11 +764,17 @@ function isEligibleQueueCandidate(result: QueueCandidateCheck | undefined): bool return !result.locked || !!result.expired || !!result.isOwnLock } +function isNotFoundError(error: unknown): boolean { + if (!error || typeof error !== 'object') return false + const fetchError = error as { statusCode?: number; response?: { status?: number } } + return fetchError.statusCode === 404 || fetchError.response?.status === 404 +} + function notifySkippedQueueProjects(count: number) { if (count <= 0) return addNotification({ title: 'Skipped projects', - text: `Skipped ${count} project(s) already moderated or locked by others.`, + text: `Skipped ${count} project(s) already moderated, deleted, or locked by others.`, type: 'info', autoCloseMs: 2000, }) @@ -799,10 +805,10 @@ async function batchCheckQueueCandidates( projectIds.map(async (id) => { const [lockResponse, projectData] = await Promise.all([ moderationQueue.checkLock(id), - useBaseFetch(`project/${id}`, { method: 'GET' }).catch(() => null), + useBaseFetch(`project/${id}`, { method: 'GET' }), ]) - const status = (projectData as { status?: string } | null)?.status + const status = (projectData as { status?: string }).status return { id, @@ -812,7 +818,7 @@ async function batchCheckQueueCandidates( slug: (projectData as { slug?: string } | null)?.slug, projectType: (projectData as { project_type?: string } | null)?.project_type, status, - isProcessing: projectData === null ? true : status === 'processing', + isProcessing: status === 'processing', } }), ) @@ -820,6 +826,8 @@ async function batchCheckQueueCandidates( checks.forEach((result, index) => { if (result.status === 'fulfilled') { results.set(result.value.id, result.value) + } else if (isNotFoundError(result.reason)) { + results.set(projectIds[index], { locked: false, isProcessing: false }) } else { results.set(projectIds[index], { locked: false, isProcessing: true }) } @@ -966,7 +974,7 @@ async function skipToNextProject() { debug('[skipToNextProject] No eligible projects in queue') addNotification({ title: 'No projects available', - text: 'All remaining projects are already moderated or locked by others.', + text: 'All remaining projects are already moderated, deleted, or locked by others.', type: 'warning', }) } @@ -1720,7 +1728,7 @@ async function endChecklist(status?: string) { ) addNotification({ title: 'No projects available', - text: 'All remaining projects are already moderated or locked by others.', + text: 'All remaining projects are already moderated, deleted, or locked by others.', type: 'warning', }) } From ef3a3bed581df72ca6e62e8d912e760f6257a7ad Mon Sep 17 00:00:00 2001 From: "Calum H. (IMB11)" Date: Fri, 31 Jul 2026 12:40:57 +0100 Subject: [PATCH 2/2] fix: remove unnesecary blocker --- .../checklist/ModerationChecklist.vue | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue b/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue index dd253ad1ef..5944d22d13 100644 --- a/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue +++ b/apps/frontend/src/components/ui/moderation/checklist/ModerationChecklist.vue @@ -1644,9 +1644,15 @@ async function sendMessage(status: ProjectStatus) { } } - await refreshModerationCaches(threadId) - const willHaveNext = await moderationQueue.completeCurrentProject(projectId, 'completed') + // Set both states together - hasNextProject MUST be set before done + // to avoid the race condition where done=true renders with hasNextProject=false + hasNextProject.value = willHaveNext + done.value = true + clearGeneratedMessageState() + await nextTick() + + await refreshModerationCaches(threadId) await Promise.race([ moderationQueue.releaseLock(projectId), @@ -1656,16 +1662,9 @@ async function sendMessage(status: ProjectStatus) { if (projectFixChanges?.slug) { const urlType = getProjectTypeForUrlShorthand(projectV2.value.project_type, [], tags.value) localStorage.setItem('moderation-checklist-finished', projectId) - clearGeneratedMessageState() await navigateTo(`/${urlType}/${projectFixChanges.slug}/moderation`, { replace: true }) return } - - // Set both states together - hasNextProject MUST be set before done - // to avoid the race condition where done=true renders with hasNextProject=false - hasNextProject.value = willHaveNext - done.value = true - clearGeneratedMessageState() } catch (error) { console.error('Error submitting moderation:', error) addNotification({