diff --git a/frontend/src/lib/components/SavedInputs.svelte b/frontend/src/lib/components/SavedInputs.svelte
index 4aa1a7dad4..6275e95917 100644
--- a/frontend/src/lib/components/SavedInputs.svelte
+++ b/frontend/src/lib/components/SavedInputs.svelte
@@ -186,6 +186,7 @@
label={null}
folder={null}
concurrencyKey={null}
+ tag={null}
success="running"
argFilter={undefined}
bind:loading
diff --git a/frontend/src/lib/components/runs/JobLoader.svelte b/frontend/src/lib/components/runs/JobLoader.svelte
index dc1cf7d228..463f1ca7b7 100644
--- a/frontend/src/lib/components/runs/JobLoader.svelte
+++ b/frontend/src/lib/components/runs/JobLoader.svelte
@@ -36,6 +36,7 @@
export let completedJobs: CompletedJob[] | undefined = undefined
export let externalJobs: Job[] | undefined = undefined
export let concurrencyKey: string | null
+ export let tag: string | null
export let extendedJobs: ExtendedJobs | undefined = undefined
export let argError = ''
export let resultError = ''
@@ -58,6 +59,7 @@
isSkipped != undefined &&
jobKinds &&
concurrencyKey &&
+ tag &&
lookback &&
user &&
folder &&
@@ -143,6 +145,7 @@
? true
: undefined,
label: label === null || label === '' ? undefined : label,
+ tag: tag === null || tag === '' ? undefined : tag,
isNotSchedule: showSchedules == false ? true : undefined,
scheduledForBeforeNow: showFutureJobs == false ? true : undefined,
args:
@@ -190,6 +193,7 @@
isSkipped: isSkipped ? undefined : false,
isFlowStep: jobKindsCat != 'all' ? false : undefined,
label: label === null || label === '' ? undefined : label,
+ tag: tag === null || tag === '' ? undefined : tag,
isNotSchedule: showSchedules == false ? true : undefined,
scheduledForBeforeNow: showFutureJobs == false ? true : undefined,
args:
diff --git a/frontend/src/lib/components/runs/RunsFilter.svelte b/frontend/src/lib/components/runs/RunsFilter.svelte
index cca0369f7b..095467b69c 100644
--- a/frontend/src/lib/components/runs/RunsFilter.svelte
+++ b/frontend/src/lib/components/runs/RunsFilter.svelte
@@ -18,6 +18,7 @@
export let path: string | null = null
export let label: string | null = null
export let concurrencyKey: string | null = null
+ export let tag: string | null = null
export let success: 'running' | 'success' | 'failure' | undefined = undefined
export let isSkipped: boolean | undefined = undefined
export let argFilter: string
@@ -37,11 +38,12 @@
$: displayedLabel = label
$: displayedConcurrencyKey = concurrencyKey
+ $: displayedTag = tag
let copyArgFilter = argFilter
let copyResultFilter = resultFilter
- export let filterBy: 'path' | 'user' | 'folder' | 'label' | 'concurrencyKey' = 'path'
+ export let filterBy: 'path' | 'user' | 'folder' | 'label' | 'concurrencyKey' | 'tag' = 'path'
const dispatch = createEventDispatcher()
@@ -63,11 +65,15 @@
} else if (concurrencyKey !== null && concurrencyKey !== '' && filterBy !== 'concurrencyKey') {
manuallySet = true
filterBy = 'concurrencyKey'
+ } else if (tag !== null && tag !== '' && filterBy !== 'tag') {
+ manuallySet = true
+ filterBy = 'tag'
}
}
let labelTimeout: NodeJS.Timeout | undefined = undefined
let concurrencyKeyTimeout: NodeJS.Timeout | undefined = undefined
+ let tagTimeout: NodeJS.Timeout | undefined = undefined
@@ -94,6 +100,7 @@
folder = null
label = null
concurrencyKey = null
+ tag = null
} else {
manuallySet = false
}
@@ -105,7 +112,8 @@
@@ -295,6 +303,39 @@
/>
{/key}
+ {:else if filterBy === 'tag'}
+ {#key tag}
+
+ {#if tag}
+
+ {/if}
+ Tag
+
+ {
+ if (tagTimeout) {
+ clearTimeout(tagTimeout)
+ }
+
+ tagTimeout = setTimeout(() => {
+ tag = displayedTag
+ }, 1000)
+ }}
+ />
+
+ {/key}
{/if}
@@ -383,6 +424,8 @@
user = null
folder = null
label = null
+ concurrencyKey = null
+ tag = null
} else {
manuallySet = false
}
@@ -391,6 +434,9 @@
+
+
+
@@ -415,10 +461,10 @@
items={usernames}
value={user}
bind:selectedItem={user}
- inputClassName="!h-[32px] py-1 !text-xs !w-64"
+ inputClassName="!h-[32px] py-1 !text-xs !w-80"
hideArrow
className={user ? '!font-bold' : ''}
- dropdownClassName="!font-normal !w-64 !max-w-64"
+ dropdownClassName="!font-normal !w-80 !max-w-80"
/>
@@ -445,10 +491,10 @@
items={folders}
value={folder}
bind:selectedItem={folder}
- inputClassName="!h-[32px] py-1 !text-xs !w-64"
+ inputClassName="!h-[32px] py-1 !text-xs !w-80"
hideArrow
className={folder ? '!font-bold' : ''}
- dropdownClassName="!font-normal !w-64 !max-w-64"
+ dropdownClassName="!font-normal !w-80 !max-w-80"
/>
@@ -483,6 +529,107 @@
{/key}
+ {:else if filterBy === 'tag'}
+ {#key tag}
+
+ {/key}
+ {:else if filterBy === 'label'}
+ {#key label}
+
+ {/key}
+ {:else if filterBy === 'concurrencyKey'}
+ {#key concurrencyKey}
+
+ {/key}
{/if}
+ {#if queryParseErrors.length > 0}
+
+
+
+ Some of your search terms have been ignored because one or more parse errors:
+
+ {#each queryParseErrors as msg}
+ - - {msg}
+ {/each}
+
+
+
+ {/if}
{#if tab === 'default' || tab === 'switch-mode'}
{@const items = (itemMap[tab] ?? []).filter((e) => defaultMenuItems.includes(e))}
{#if items.length > 0}
-
+
{#each items as el}
{/each}
@@ -549,6 +578,7 @@
el.path +
(el.starred ? ' ★' : '')}
icon={iconForWindmillItem(el.type)}
+ bind:mouseMoved
/>
{/each}
{/if}
@@ -600,6 +630,7 @@
hovered={selectedItem && r?.document.id[0] === selectedItem?.document.id[0]}
icon={r?.icon}
containerClass="rounded-md px-2 py-1 my-2"
+ bind:mouseMoved
>
dispatch('hover')}
+ on:mouseenter={() => {
+ if (mouseMoved) {
+ dispatch('hover')
+ }
+ mouseMoved=false
+ }}
class={twMerge(
`rounded-md w-full transition-all cursor-pointer ${
hovered ? 'bg-surface-hover' : ''
diff --git a/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte
index 6078c7d4ad..864ecd8fe9 100644
--- a/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte
+++ b/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte
@@ -49,6 +49,7 @@
let folder: string | null = $page.url.searchParams.get('folder')
let label: string | null = $page.url.searchParams.get('label')
let concurrencyKey: string | null = $page.url.searchParams.get('concurrency_key')
+ let tag: string | null = $page.url.searchParams.get('tag')
// Rest of filters handled by RunsFilter
let success: 'running' | 'success' | 'failure' | undefined = ($page.url.searchParams.get(
'success'
@@ -125,6 +126,7 @@
schedulePath ||
jobKindsCat ||
concurrencyKey ||
+ tag ||
graph ||
minTs ||
maxTs ||
@@ -218,6 +220,12 @@
searchParams.delete('concurrency_key')
}
+ if (tag) {
+ searchParams.set('tag', tag)
+ } else {
+ searchParams.delete('tag')
+ }
+
if (label) {
searchParams.set('label', label)
} else {
@@ -287,6 +295,7 @@
folder = null
label = null
concurrencyKey = null
+ tag = null
}
function filterByUser(e: CustomEvent
) {
@@ -295,6 +304,7 @@
user = e.detail
label = null
concurrencyKey = null
+ tag = null
}
function filterByFolder(e: CustomEvent) {
@@ -303,6 +313,7 @@
folder = e.detail
label = null
concurrencyKey = null
+ tag = null
}
function filterByLabel(e: CustomEvent) {
@@ -311,6 +322,7 @@
folder = null
label = e.detail
concurrencyKey = null
+ tag = null
}
function filterByConcurrencyKey(e: CustomEvent) {
@@ -319,6 +331,16 @@
folder = null
label = null
concurrencyKey = e.detail
+ tag = null
+ }
+
+ function filterByTag(e: CustomEvent) {
+ path = null
+ user = null
+ folder = null
+ label = null
+ concurrencyKey = null
+ tag = e.detail
}
let calendarChangeTimeout: NodeJS.Timeout | undefined = undefined
@@ -369,7 +391,8 @@
? resultFilter
: undefined,
allWorkspaces: allWorkspaces ? true : undefined,
- concurrencyKey: concurrencyKey ?? undefined
+ concurrencyKey: concurrencyKey ?? undefined,
+ tag: tag ?? undefined
}
selectedFiltersString = JSON.stringify(selectedFilters, null, 4)
@@ -395,7 +418,7 @@
}
const warnJobLimitMsg =
- 'The exact number of concurrent job at the beginning of the time range may be incorrect as only the last 1000 jobs are taken into account: a job that was started earlier than this limit will not be taken into account'
+ 'The exact number of concurrent jobs at the beginning of the time range may be incorrect as only the last 1000 jobs are taken into account: a job that was started earlier than this limit will not be taken into account'
$: warnJobLimit =
graph === 'ConcurrencyChart' &&
@@ -430,6 +453,7 @@
{concurrencyKey}
{argError}
{resultError}
+ {tag}
bind:loading
bind:this={jobLoader}
lookback={graphIsRunsChart ? 0 : lookback}
@@ -521,6 +545,7 @@
bind:folder
bind:label
bind:concurrencyKey
+ bind:tag
bind:path
bind:success
bind:argFilter
@@ -815,6 +840,7 @@
on:filterByFolder={filterByFolder}
on:filterByLabel={filterByLabel}
on:filterByConcurrencyKey={filterByConcurrencyKey}
+ on:filterByTag={filterByTag}
/>
{:else}
@@ -872,6 +898,9 @@
bind:folder
bind:path
bind:user
+ bind:label
+ bind:concurrencyKey
+ bind:tag
bind:success
bind:argFilter
bind:resultFilter
@@ -1158,6 +1187,7 @@
on:filterByFolder={filterByFolder}
on:filterByLabel={filterByLabel}
on:filterByConcurrencyKey={filterByConcurrencyKey}
+ on:filterByTag={filterByTag}
/>