feat: add retry/delete for jobs
All checks were successful
Build & Push Docker Image / build-and-push (push) Successful in 41s
All checks were successful
Build & Push Docker Image / build-and-push (push) Successful in 41s
- db.ts: add resetJob() and deleteJob() statements + exports - pipeline.ts: export retryJob() — resets job state and re-runs pipeline - DELETE /api/jobs/[id]: hard-delete terminal jobs (done/failed/cancelled); keep cancel-only behavior for active jobs - POST /api/jobs/[id]/retry: new endpoint; validates failed/cancelled URL job, resets and re-runs via retryJob() - jobs/[id]/+page.svelte: wire Cancel/Retry/Delete buttons with fetch calls; fix hardcoded ACCENT → accent store - jobs/+page.svelte: per-row Retry+Delete icon buttons (visible on hover); fix hardcoded ACCENT → accent store Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -4,8 +4,9 @@
|
||||
import type { Job, Segment } from '$lib/types.js';
|
||||
import SourceIcon from '$lib/components/SourceIcon.svelte';
|
||||
import Waveform from '$lib/components/Waveform.svelte';
|
||||
import { accent } from '$lib/accent.js';
|
||||
|
||||
const ACCENT = '#cdf24e';
|
||||
const ACCENT = $derived($accent.value);
|
||||
|
||||
const jobId = $derived($page.params.id);
|
||||
let job = $state<Job | null>(null);
|
||||
@@ -108,6 +109,36 @@
|
||||
|
||||
const formats = ['srt', 'txt', 'md', 'json'] as const;
|
||||
const isActive = $derived(!job || !['done', 'failed', 'cancelled'].includes(job.status));
|
||||
const isTerminal = $derived(job !== null && ['done', 'failed', 'cancelled'].includes(job.status));
|
||||
const canRetry = $derived(
|
||||
job !== null &&
|
||||
['failed', 'cancelled'].includes(job.status) &&
|
||||
(job.source?.startsWith('http') ?? false)
|
||||
);
|
||||
|
||||
async function cancelJob() {
|
||||
if (!job) return;
|
||||
await fetch(`/api/jobs/${job.id}`, { method: 'DELETE' });
|
||||
await loadJob();
|
||||
}
|
||||
|
||||
async function deleteJob() {
|
||||
if (!job || !confirm(`Delete job "${job.title || job.id}"?`)) return;
|
||||
const res = await fetch(`/api/jobs/${job.id}`, { method: 'DELETE' });
|
||||
if (res.ok) window.location.href = '/';
|
||||
}
|
||||
|
||||
async function retryJobAction() {
|
||||
if (!job) return;
|
||||
const res = await fetch(`/api/jobs/${job.id}/retry`, { method: 'POST' });
|
||||
if (res.ok) {
|
||||
await loadJob();
|
||||
openStream();
|
||||
} else {
|
||||
const body = await res.json().catch(() => ({}));
|
||||
alert(body.message ?? 'Retry failed');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -143,11 +174,17 @@
|
||||
{#if job.meanVolume != null}· {job.meanVolume.toFixed(1)} dBFS{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-actions">
|
||||
{#if isActive}
|
||||
<form method="POST" action="/api/jobs/{job.id}?_method=DELETE">
|
||||
<button type="button" class="btn-cancel" aria-label="Cancel job">Cancel</button>
|
||||
</form>
|
||||
<button class="btn-job-action danger" onclick={cancelJob} aria-label="Cancel job">Cancel</button>
|
||||
{/if}
|
||||
{#if canRetry}
|
||||
<button class="btn-job-action accent" onclick={retryJobAction} aria-label="Retry job">↺ Retry</button>
|
||||
{/if}
|
||||
{#if isTerminal}
|
||||
<button class="btn-job-action danger" onclick={deleteJob} aria-label="Delete job">Delete</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Progress block ────────────────────────────────── -->
|
||||
@@ -377,20 +414,37 @@
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
.job-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.btn-job-action {
|
||||
padding: 8px 14px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(255, 90, 90, 0.3);
|
||||
background: rgba(255, 90, 90, 0.08);
|
||||
color: #ff8a8a;
|
||||
font-size: 12.5px;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.btn-cancel:hover {
|
||||
.btn-job-action.danger {
|
||||
border: 1px solid rgba(255, 90, 90, 0.3);
|
||||
background: rgba(255, 90, 90, 0.08);
|
||||
color: #ff8a8a;
|
||||
}
|
||||
.btn-job-action.danger:hover {
|
||||
background: rgba(255, 90, 90, 0.15);
|
||||
}
|
||||
.btn-job-action.accent {
|
||||
border: 1px solid color-mix(in oklab, var(--accent) 40%, transparent);
|
||||
background: color-mix(in oklab, var(--accent) 10%, transparent);
|
||||
color: var(--accent);
|
||||
}
|
||||
.btn-job-action.accent:hover {
|
||||
background: color-mix(in oklab, var(--accent) 18%, transparent);
|
||||
}
|
||||
|
||||
/* ── Progress card ──────────────────────────────────────── */
|
||||
.progress-card {
|
||||
|
||||
Reference in New Issue
Block a user