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} />