mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-06 16:02:23 +00:00
* fix: distinguish canceled jobs in runs
* fix: order status=failure|canceled by completed_at to use partial index
The new `status` query param replaced the legacy `success=false` filter on
the Runs page, but the ORDER BY switch in list_completed_jobs_query only
flipped to v2_job_completed.completed_at for success==Some(false). With
status=failure|canceled (and success=None), the query fell back to ordering
by v2_job.created_at, which the partial index
ix_v2_job_completed_failure_workspace (workspace_id, completed_at DESC WHERE
status IN ('failure','canceled')) cannot serve.
EXPLAIN ANALYZE on 500k rows (1% failure/canceled): ordering by completed_at
uses the partial index (~150 buffers, 0.3ms); ordering by created_at scans
the v2_job created_at index and probes/discards 99% of rows via the join
(~49k buffers, 31ms). Switch the ordering to completed_at for
failure/canceled so the partial index serves both filtering and ordering.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test: trim order-by regression test to the failure/canceled case
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix: only treat canceled as a terminal status icon for completed jobs
Guard the canceled branch in JobStatusIcon and getJobStatusKind with
`'success' in job` so a job that is still running while being canceled keeps
its running icon/favicon until it completes, instead of immediately showing
the gray Canceled state. Also clarify the openapi `status` param is an exact
match (status=success excludes skipped, unlike success=true).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
57 lines
2.3 KiB
TypeScript
57 lines
2.3 KiB
TypeScript
import type { Job } from '$lib/gen'
|
|
|
|
export type JobStatusKind = 'running' | 'success' | 'failure' | 'canceled'
|
|
|
|
const STATUS_COLORS: Record<JobStatusKind, string> = {
|
|
running: '#eab308',
|
|
success: '#22c55e',
|
|
failure: '#ef4444',
|
|
canceled: '#6b7280'
|
|
}
|
|
|
|
const DEFAULT_FAVICON = '/logo.svg'
|
|
|
|
// Inlined windmill logo polygons (fills inlined so the SVG is self-contained as a data URI).
|
|
const LOGO_POLYGONS =
|
|
'<polygon fill="#BCD4FC" points="134.78,14.22 114.31,48.21 101.33,69.75 158.22,69.75 177.97,36.95 191.67,14.22"/>' +
|
|
'<polygon fill="#3B82F6" points="227.55,69.75 186.61,69.75 101.33,69.75 129.78,119.02 158.16,119.02 228.61,119.02 256,119.02"/>' +
|
|
'<polygon fill="#3B82F6" points="136.93,132.47 116.46,167.93 73.82,241.78 130.71,241.78 144.9,217.2 180.13,156.18 193.82,132.46"/>' +
|
|
'<polygon fill="#3B82F6" points="121.7,131.95 101.23,96.49 58.59,22.63 30.15,71.91 44.34,96.49 79.57,157.5 93.26,181.22"/>' +
|
|
'<polygon fill="#BCD4FC" points="64.81,131.95 25.15,131.21 0,130.74 28.44,180.01 66.73,180.72 93.26,181.21"/>' +
|
|
'<polygon fill="#BCD4FC" points="165.38,181.74 184.58,216.46 196.75,238.47 225.19,189.2 206.66,155.69 193.83,132.46"/>'
|
|
|
|
function faviconLink(): HTMLLinkElement {
|
|
let link = document.querySelector<HTMLLinkElement>('link[rel="icon"]')
|
|
if (!link) {
|
|
link = document.createElement('link')
|
|
link.rel = 'icon'
|
|
document.head.appendChild(link)
|
|
}
|
|
return link
|
|
}
|
|
|
|
/** Maps a job to one of the favicon status colors, following the same
|
|
* discrimination as JobStatusIcon (`'success' in job` => completed). A job that
|
|
* is still running while being canceled stays 'running' until it completes. */
|
|
export function getJobStatusKind(job: Job | undefined): JobStatusKind | undefined {
|
|
if (!job) return undefined
|
|
if (job.canceled && 'success' in job) return 'canceled'
|
|
if ('success' in job) return job.success ? 'success' : 'failure'
|
|
return 'running'
|
|
}
|
|
|
|
export function setStatusFavicon(status: JobStatusKind): void {
|
|
const color = STATUS_COLORS[status]
|
|
const svg =
|
|
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">' +
|
|
`<g>${LOGO_POLYGONS}</g>` +
|
|
'<circle cx="196" cy="196" r="70" fill="#ffffff"/>' +
|
|
`<circle cx="196" cy="196" r="54" fill="${color}"/>` +
|
|
'</svg>'
|
|
faviconLink().href = `data:image/svg+xml,${encodeURIComponent(svg)}`
|
|
}
|
|
|
|
export function resetFavicon(): void {
|
|
faviconLink().href = DEFAULT_FAVICON
|
|
}
|