fix(queue): allow deleting failed jobs from queue progress UI (#8478)

## Summary
Failed jobs could not be removed from the Media Assets queue progress
panel because `useJobActions` only supported cancel for pending/running
jobs.

## Changes
- Add `deleteAction`, `canDeleteJob`, `runDeleteJob` to `useJobActions`
composable
- Export `removeFailedJob` from `useJobMenu` with optional task
parameter
- Update `ActiveMediaAssetCard.vue` to show delete button on failed jobs

## Testing
1. Queue a workflow that will fail (e.g., missing model)
2. Open Media Assets panel
3. Hover over the failed job card → delete button (circle-minus icon)
appears
4. Click delete → job is removed from queue


┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-8478-fix-queue-allow-deleting-failed-jobs-from-queue-progress-UI-2f86d73d3650810ba3aaf6cf38703bf5)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Amp <amp@ampcode.com>
This commit is contained in:
Christian Byrne
2026-02-20 16:49:36 -08:00
committed by GitHub
parent b3aed9afd0
commit b27eb5861a
4 changed files with 158 additions and 17 deletions

View File

@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n'
import { useErrorHandling } from '@/composables/useErrorHandling'
import type { JobListItem } from '@/composables/queue/useJobList'
import { useJobMenu } from '@/composables/queue/useJobMenu'
import type { TaskItemImpl } from '@/stores/queueStore'
import { isActiveJobState } from '@/utils/queueUtil'
export type JobAction = {
@@ -18,7 +19,7 @@ export function useJobActions(
) {
const { t } = useI18n()
const { wrapWithErrorHandlingAsync } = useErrorHandling()
const { cancelJob } = useJobMenu()
const { cancelJob, removeFailedJob } = useJobMenu()
const cancelAction: JobAction = {
icon: 'icon-[lucide--x]',
@@ -26,6 +27,12 @@ export function useJobActions(
variant: 'destructive'
}
const deleteAction: JobAction = {
icon: 'icon-[lucide--circle-minus]',
label: t('queue.jobMenu.removeJob'),
variant: 'destructive'
}
const jobRef = computed(() => toValue(job) ?? null)
const canCancelJob = computed(() => {
@@ -37,6 +44,15 @@ export function useJobActions(
return currentJob.showClear !== false && isActiveJobState(currentJob.state)
})
const canDeleteJob = computed(() => {
const currentJob = jobRef.value
if (!currentJob) {
return false
}
return currentJob.state === 'failed'
})
const runCancelJob = wrapWithErrorHandlingAsync(async () => {
const currentJob = jobRef.value
if (!currentJob) {
@@ -46,9 +62,22 @@ export function useJobActions(
await cancelJob(currentJob)
})
const runDeleteJob = wrapWithErrorHandlingAsync(async () => {
const currentJob = jobRef.value
const task = currentJob?.taskRef as TaskItemImpl | undefined
if (!task) {
return
}
await removeFailedJob(task)
})
return {
cancelAction,
canCancelJob,
runCancelJob
runCancelJob,
deleteAction,
canDeleteJob,
runDeleteJob
}
}

View File

@@ -217,10 +217,11 @@ export function useJobMenu(
}
}
const removeFailedJob = async () => {
const task = currentMenuItem()?.taskRef as TaskItemImpl | undefined
if (!task) return
await queueStore.delete(task)
const removeFailedJob = async (task?: TaskItemImpl | null) => {
const target =
task ?? (currentMenuItem()?.taskRef as TaskItemImpl | undefined)
if (!target) return
await queueStore.delete(target)
}
const jobMenuOpenWorkflowLabel = computed(() =>
@@ -369,6 +370,7 @@ export function useJobMenu(
jobMenuEntries,
openJobWorkflow,
copyJobId,
cancelJob
cancelJob,
removeFailedJob
}
}