Files
windmill/frontend/src/lib/components/HistoricInputs.svelte
T
Diego Imbertandclaude[bot] 9b28c85469 feat: Unified filters and new runs page (#8027)
* RunsPage redesign v0

* nit

* Remove manualdatepicker

* remove shadow

* ui nits

* nit scrollbar bg

* prettier cards

* nit

* Remove code

* command/meta multi select

* Shift select

* RightClickPopover

* nit

* Ctrl A

* nit card

* DropdownMenu

* nit

* count hint

* fix stuck keys

* opacity UX

* error toasts pickhubscript

* Improve UX

* fix undefined error

* keyboard nav

* nit batch rerun fixes

* nit fix scroll / height

* Batch reruns actions + nits

* nit

* Cancel selected jobs

* Cancel / re-run all filtered jobs

* Go to job / flow / script action

* nit

* add batch actions back

* nit

* nit

* bar on splitpane hover

* nit

* New Timeframe system

* reset btn

* nit fixes

* dead code

* nits

* typecheck

* naming clarity

* Update frontend/src/lib/components/RightClickPopover.svelte

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* unnecessary json stringify

* dedup 'the'

* Code deletion to prepare for changes

* filter types

* ui

* fix bug with maxTs

* stuck with melt

* GenericDropdown

* filters onclick

* iterate

* iter

* add all filters

* Descriptions

* focus position

* stash

* TaggedTextInput works much much better

* placeholder

* currentTag suggestion

* improve

* nit

* Keyboard nav

* buildRunsFilterSearchbarSchema

* nit naming

* assignObjInPlace

* Escaping + pretty dates

* nit empty

* fix cursor

* nit space

* Filter filtering

* escape pasted value

* nit

* escape spaces

* nit undefined

* add space at end if right arrow

* escape all spaces

* arrow skips escape chars

* escape \ too

* delete whole escaped characters

* double space to escape tag

* code refactor

* Ensure cursor visible

* fix keyboard nav

* safety

* filterSchemaRecToZodSchema

* URL Sync

* fix readonly

* fix typing

* start replacing old filter logic

* use new filter impl

* nit

* nit reactivity

* nit fix

* no more localStorage

* Add back status and kind toggles

* Nit fix

* style nit

* focus at end on click

* clearn btn + fixes

* fix broken date uri

* nit

* useSyncedTimeframe

* negative filter button

* negative filters helpers rust

* Negated filters backed

* nit

* highlight

* New useSearchParams

* Accept comma separated list

* nit allowNegative

* openapi update

* Fix trigger kind list/negation not working

* nit oipenpai

* Presets

* DebouncedTempValue

* remove presets from list when already applied

* UI nit improvements

* allowMultiple

* hint

* validateFilterInstance fn

* nit fix

* error highlights

* nit ux selecting negative list

* nit

* on clear btn

* SimpleEditor for JSON

* nit

* flop

* Pass presets as param

* nit delete

* preventCursorMoveOnNextSync

* responsive layout

* Escape \n

* Inline calendar input

* mm/dd or dd/mm depending on US or not

* onClickBehavior

* infiniteRange

* other nits

* Wiring with runs filter

* formatDateRange better

* inits on right page

* style

* min hour support

* Time input

* use our components

* Improve SKILL.md

* dd mm yyyy numeric input

* TimeframeSelect with new date picker

* fixes

* ensure date is in view when value changes externally

* fixes

* nit select all on focus

* select year + nits

* nit layout shift

* nit negative when starting with !

* nit

* SelectDropdown uses GenericDropdown now

* Fix blank select dropdown rendering bug

* icons

* Reset btn + shorter date range formatting

* overflow fix

* unnecessary absolute

* fix clear btn overlap

* Update routes for new filters (assets, schedule, resource, variables)

* update openapi

* Impl for other pages

* ui nits

* nit fixes

* Fix columns filter

* super nits

---------

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2026-02-23 17:53:09 +00:00

174 lines
4.5 KiB
Svelte

<script lang="ts">
import { type RunnableType } from '$lib/gen/index.js'
import { sendUserToast } from '$lib/utils.js'
import RunningJobSchemaPicker from '$lib/components/schema/RunningJobSchemaPicker.svelte'
import { createEventDispatcher, onDestroy } from 'svelte'
import { useJobsLoader, type UseJobLoaderArgs } from './runs/useJobsLoader.svelte'
import { DataTable } from '$lib/components/table'
import HistoricList from './HistoricList.svelte'
import { Loader2 } from 'lucide-svelte'
import { workspaceStore } from '$lib/stores'
interface Props {
runnableId?: string | undefined
runnableType?: RunnableType | undefined
selected?: string | undefined
showAuthor?: boolean
placement?: 'bottom-start' | 'top-start' | 'bottom-end' | 'top-end'
limitPayloadSize?: boolean
searchArgs?: Record<string, any> | undefined
}
let {
runnableId = undefined,
runnableType = undefined,
selected = $bindable(undefined),
showAuthor = false,
placement = 'top-end',
limitPayloadSize = false,
searchArgs = undefined
}: Props = $props()
let historicList: HistoricList | undefined = $state(undefined)
const dispatch = createEventDispatcher()
let hasMoreCurrentRuns = false
let page = $state(1)
async function handleSelected(data: any) {
if (selected === data.id) {
resetSelected(true)
return
}
selected = data.id
if (data.payloadData === 'WINDMILL_TOO_BIG') {
const fullPayload = await data.getFullPayload?.()
dispatch('select', { args: fullPayload, jobId: data.id })
} else {
dispatch('select', {
args: structuredClone($state.snapshot(data.payloadData)),
jobId: data.id
})
}
}
export function loading(): boolean {
return jobsLoader?.loading ?? false
}
onDestroy(() => {
resetSelected(false)
})
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape' && selected) {
resetSelected(true)
event.stopPropagation()
event.preventDefault()
}
}
function handleError(e: { type: string; error: any }) {
if (e.type === 'load') {
sendUserToast(`Failed to load input history: ${e.error}`, true)
}
}
let jobHovered: string | undefined = undefined
export function refresh(clearCurrentRuns: boolean = false) {
historicList?.refresh(clearCurrentRuns)
}
export function resetSelected(dispatchEvent?: boolean) {
selected = undefined
if (dispatchEvent) {
dispatch('select', undefined)
}
}
function getJobKinds(runnableType: RunnableType | undefined) {
if (runnableType === 'FlowPath') {
return 'flow,flowpreview'
} else if (runnableType === 'ScriptPath') {
return 'script,preview'
} else if (runnableType === 'ScriptHash') {
return 'script,preview'
}
return ''
}
let jobsLoader = useJobsLoader(
() =>
({
filters: {
show_skipped: false,
path: runnableId,
status: 'running',
arg: searchArgs ? JSON.stringify(searchArgs) : ''
},
perPage: 5,
jobKinds: getJobKinds(runnableType),
syncQueuedRunsCount: false,
refreshRate: 10000,
currentWorkspace: $workspaceStore ?? '',
skip: !runnableId
}) satisfies UseJobLoaderArgs
)
let jobs = $derived(jobsLoader?.jobs ?? [])
</script>
<svelte:window onkeydown={handleKeydown} />
<div class="h-full max-h-full min-h-0 w-full flex flex-col gap-2 relative">
<div class="grow-0" data-schema-picker>
<DataTable size="xs" bind:currentPage={page} hasMore={hasMoreCurrentRuns} tableFixed={true}>
{#if jobsLoader?.loading}
<div class="text-primary absolute top-2 right-2">
<Loader2 class="animate-spin" size={14} />
</div>
{/if}
{#if jobs.length > 0}
<colgroup>
<col class="w-8" />
<col class="w-16" />
<col />
</colgroup>
<tbody class="w-full overflow-y-auto">
{#each jobs as job (job.id)}
<RunningJobSchemaPicker
{job}
selected={selected === job.id}
hovering={jobHovered === job.id}
on:select={(e) => handleSelected(e.detail)}
/>
{/each}
{#if jobs.length == 5}
<tr class="text-left text-primary text-xs w-full">
<td class="w-full px-2" colspan="3">limited to 5 runs</td>
</tr>
{/if}
</tbody>
{:else}
<div class="text-center text-primary text-xs py-2">No job currently running</div>
{/if}
</DataTable>
</div>
<div class="min-h-0 grow" data-schema-picker>
<HistoricList
bind:this={historicList}
on:error={(e) => handleError(e.detail)}
on:select={(e) => handleSelected(e.detail)}
{runnableId}
{runnableType}
{selected}
{showAuthor}
{placement}
{limitPayloadSize}
{searchArgs}
/>
</div>
</div>