From fa9b9a08392d3fa478ae89fa0319de6c8bfde306 Mon Sep 17 00:00:00 2001 From: Ruben Fiszel Date: Tue, 13 Feb 2024 10:01:03 +0100 Subject: [PATCH] feat: improve runs page + add all workspaces to admins runs page --- ...acfc0ed4973309b4860e93882a37a21a0bd0.json} | 7 +- backend/windmill-api/openapi.yaml | 15 ++ backend/windmill-api/src/apps.rs | 2 +- backend/windmill-api/src/jobs.rs | 26 +- cli/sync.ts | 2 +- frontend/src/lib/components/RunChart.svelte | 60 ++++- .../src/lib/components/SavedInputs.svelte | 2 +- .../src/lib/components/TestJobLoader.svelte | 2 +- .../src/lib/components/runs/JobLoader.svelte | 246 +++++++----------- .../src/lib/components/runs/JobPreview.svelte | 14 +- .../components/runs/ManuelDatePicker.svelte | 78 ++++-- .../lib/components/runs/QueuePopover.svelte | 6 +- .../src/lib/components/runs/RunRow.svelte | 1 - .../src/lib/components/runs/RunsFilter.svelte | 12 + .../src/lib/components/runs/RunsQueue.svelte | 3 +- .../src/lib/components/runs/RunsTable.svelte | 89 ++++--- .../(logged)/runs/[...path]/+page.svelte | 207 +++++++++++++-- 17 files changed, 497 insertions(+), 275 deletions(-) rename backend/.sqlx/{query-9da1bf41c433c1f781bcc8ea5d86fd796b7ff92434f5ed4682c4445a02c2c720.json => query-8481d1bd91b7d23f946b4cf0d312acfc0ed4973309b4860e93882a37a21a0bd0.json} (62%) diff --git a/backend/.sqlx/query-9da1bf41c433c1f781bcc8ea5d86fd796b7ff92434f5ed4682c4445a02c2c720.json b/backend/.sqlx/query-8481d1bd91b7d23f946b4cf0d312acfc0ed4973309b4860e93882a37a21a0bd0.json similarity index 62% rename from backend/.sqlx/query-9da1bf41c433c1f781bcc8ea5d86fd796b7ff92434f5ed4682c4445a02c2c720.json rename to backend/.sqlx/query-8481d1bd91b7d23f946b4cf0d312acfc0ed4973309b4860e93882a37a21a0bd0.json index 51711c668c..2ec3af2410 100644 --- a/backend/.sqlx/query-9da1bf41c433c1f781bcc8ea5d86fd796b7ff92434f5ed4682c4445a02c2c720.json +++ b/backend/.sqlx/query-8481d1bd91b7d23f946b4cf0d312acfc0ed4973309b4860e93882a37a21a0bd0.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM queue WHERE workspace_id = $1 AND scheduled_for <= now() AND running = false", + "query": "SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now() AND running = false", "describe": { "columns": [ { @@ -11,12 +11,13 @@ ], "parameters": { "Left": [ - "Text" + "Text", + "Bool" ] }, "nullable": [ null ] }, - "hash": "9da1bf41c433c1f781bcc8ea5d86fd796b7ff92434f5ed4682c4445a02c2c720" + "hash": "8481d1bd91b7d23f946b4cf0d312acfc0ed4973309b4860e93882a37a21a0bd0" } diff --git a/backend/windmill-api/openapi.yaml b/backend/windmill-api/openapi.yaml index 3ac6bfbdcd..b6ac847ea1 100644 --- a/backend/windmill-api/openapi.yaml +++ b/backend/windmill-api/openapi.yaml @@ -4814,6 +4814,11 @@ paths: - $ref: "#/components/parameters/ArgsFilter" - $ref: "#/components/parameters/ResultFilter" - $ref: "#/components/parameters/Tag" + - name: all_workspaces + description: get jobs from all workspaces (only valid if request come from the `admins` workspace) + in: query + schema: + type: boolean responses: "200": description: All queued jobs @@ -4832,6 +4837,11 @@ paths: - job parameters: - $ref: "#/components/parameters/WorkspaceId" + - name: all_workspaces + description: get jobs from all workspaces (only valid if request come from the `admins` workspace) + in: query + schema: + type: boolean responses: "200": description: queue count @@ -4965,6 +4975,11 @@ paths: in: query schema: type: boolean + - name: all_workspaces + description: get jobs from all workspaces (only valid if request come from the `admins` workspace) + in: query + schema: + type: boolean responses: "200": description: All jobs diff --git a/backend/windmill-api/src/apps.rs b/backend/windmill-api/src/apps.rs index 0a95307816..98f41d7298 100644 --- a/backend/windmill-api/src/apps.rs +++ b/backend/windmill-api/src/apps.rs @@ -82,7 +82,7 @@ pub struct ListableApp { pub extra_perms: serde_json::Value, pub execution_mode: String, pub starred: bool, - pub edited_at: chrono::DateTime, + pub edited_at: Option>, pub has_draft: bool, #[serde(skip_serializing_if = "Option::is_none")] pub draft_only: Option, diff --git a/backend/windmill-api/src/jobs.rs b/backend/windmill-api/src/jobs.rs index b9c4b160cf..db0aa43cda 100644 --- a/backend/windmill-api/src/jobs.rs +++ b/backend/windmill-api/src/jobs.rs @@ -663,6 +663,7 @@ pub struct ListQueueQuery { pub args: Option, pub tag: Option, pub scheduled_for_before_now: Option, + pub all_workspaces: Option, } fn list_queue_jobs_query(w_id: &str, lq: &ListQueueQuery, fields: &[&str]) -> SqlBuilder { @@ -670,9 +671,12 @@ fn list_queue_jobs_query(w_id: &str, lq: &ListQueueQuery, fields: &[&str]) -> Sq .fields(fields) .order_by("created_at", lq.order_desc.unwrap_or(true)) .limit(1000) - .and_where_eq("workspace_id", "?".bind(&w_id)) .clone(); + if w_id != "admins" || !lq.all_workspaces.is_some_and(|x| x) { + sqlb.and_where_eq("workspace_id", "?".bind(&w_id)); + } + if let Some(ps) = &lq.script_path_start { sqlb.and_where_like_left("script_path", "?".bind(ps)); } @@ -765,6 +769,7 @@ struct ListableQueuedJob { pub suspend: Option, pub tag: String, pub priority: Option, + pub workspace_id: String, } async fn list_queue_jobs( @@ -794,6 +799,7 @@ async fn list_queue_jobs( "suspend", "tag", "priority", + "workspace_id", ], ) .sql()?; @@ -857,15 +863,22 @@ struct QueueStats { database_length: i64, } +#[derive(Deserialize)] +pub struct CountQueueJobsQuery { + all_workspaces: Option, +} + async fn count_queue_jobs( Extension(db): Extension, Path(w_id): Path, + Query(cq): Query, ) -> error::JsonResult { Ok(Json( sqlx::query_as!( QueueStats, - "SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM queue WHERE workspace_id = $1 AND scheduled_for <= now() AND running = false", - w_id + "SELECT coalesce(COUNT(*), 0) as \"database_length!\" FROM queue WHERE (workspace_id = $1 OR $2) AND scheduled_for <= now() AND running = false", + w_id, + w_id == "admins" && cq.all_workspaces.unwrap_or(false), ) .fetch_one(&db) .await?, @@ -972,6 +985,7 @@ async fn list_jobs( tag: lq.tag, schedule_path: lq.schedule_path, scheduled_for_before_now: lq.scheduled_for_before_now, + all_workspaces: lq.all_workspaces, }, &[ "'QueuedJob' as typ", @@ -3135,11 +3149,14 @@ fn list_completed_jobs_query( let mut sqlb = SqlBuilder::select_from("completed_job") .fields(fields) .order_by("created_at", lq.order_desc.unwrap_or(true)) - .and_where_eq("workspace_id", "?".bind(&w_id)) .offset(offset) .limit(per_page) .clone(); + if w_id != "admins" || !lq.all_workspaces.is_some_and(|x| x) { + sqlb.and_where_eq("workspace_id", "?".bind(&w_id)); + } + if let Some(p) = &lq.schedule_path { sqlb.and_where_eq("schedule_path", "?".bind(p)); } @@ -3229,6 +3246,7 @@ pub struct ListCompletedQuery { pub result: Option, pub tag: Option, pub scheduled_for_before_now: Option, + pub all_workspaces: Option, } async fn list_completed_jobs( diff --git a/cli/sync.ts b/cli/sync.ts index bcfb6bb527..fcd8f78eac 100644 --- a/cli/sync.ts +++ b/cli/sync.ts @@ -528,7 +528,7 @@ export async function ignoreF(wmillconf: { .join(", "); log.info( colors.gray( - `(Deprecated, use wmill.conf/includes instead) Using .wmillignore file (${condensed})` + `(Deprecated, use wmill.yaml/includes instead) Using .wmillignore file (${condensed})` ) ); ign = gitignore_parser.compile(ignoreContent); diff --git a/frontend/src/lib/components/RunChart.svelte b/frontend/src/lib/components/RunChart.svelte index e908b7219f..deaa0773a2 100644 --- a/frontend/src/lib/components/RunChart.svelte +++ b/frontend/src/lib/components/RunChart.svelte @@ -20,6 +20,8 @@ export let jobs: CompletedJob[] | undefined = [] export let maxIsNow: boolean = false + export let minTimeSet: string | undefined = undefined + export let maxTimeSet: string | undefined = undefined const dispatch = createEventDispatcher() @@ -100,25 +102,56 @@ let minTime = addSeconds(new Date(), -300) let maxTime = getDbClockNow() - $: computeMinMaxTime(jobs) + $: computeMinMaxTime(jobs, minTimeSet, maxTimeSet) - function computeMinMaxTime(jobs: CompletedJob[] | undefined) { - if (jobs == undefined || jobs?.length == 0) { - minTime = addSeconds(new Date(), -300) - maxTime = getDbClockNow() + function minJobTime(jobs: CompletedJob[]): Date { + let min: Date = new Date(jobs[0].started_at) + for (const job of jobs) { + if (new Date(job.started_at) < min) { + min = new Date(job.started_at) + } + } + return min + } + + function maxJobTime(jobs: CompletedJob[]): Date { + let max: Date = new Date(jobs[0].started_at) + for (const job of jobs) { + if (new Date(job.started_at) > max) { + max = new Date(job.started_at) + } + } + return max + } + function computeMinMaxTime( + jobs: CompletedJob[] | undefined, + minTimeSet: string | undefined, + maxTimeSet: string | undefined + ) { + let minTimeSetDate = minTimeSet ? new Date(minTimeSet) : undefined + let maxTimeSetDate = maxTimeSet ? new Date(maxTimeSet) : undefined + if (minTimeSetDate && maxTimeSetDate) { + minTime = minTimeSetDate + maxTime = maxTimeSetDate return } - const maxJob = maxIsNow ? getDbClockNow() : new Date(jobs?.[0].started_at) - const minJob = new Date(jobs?.[jobs?.length - 1].started_at) + if (jobs == undefined || jobs?.length == 0) { + minTime = minTimeSetDate ?? addSeconds(new Date(), -300) + maxTime = maxTimeSetDate ?? getDbClockNow() + return + } + + const maxJob = maxIsNow ? getDbClockNow() : maxJobTime(jobs) + const minJob = minJobTime(jobs) const diff = (maxJob.getTime() - minJob.getTime()) / 20000 - minTime = addSeconds(minJob, -diff) + minTime = minTimeSetDate ?? addSeconds(minJob, -diff) if (maxIsNow) { - maxTime = maxJob + maxTime = maxTimeSetDate ?? maxJob } else { - maxTime = addSeconds(maxJob, diff) + maxTime = maxTimeSetDate ?? addSeconds(maxJob, diff) } } @@ -168,6 +201,13 @@ } as any + + +
diff --git a/frontend/src/lib/components/SavedInputs.svelte b/frontend/src/lib/components/SavedInputs.svelte index 51dedbd920..b0b73b438b 100644 --- a/frontend/src/lib/components/SavedInputs.svelte +++ b/frontend/src/lib/components/SavedInputs.svelte @@ -153,9 +153,9 @@ success="running" argFilter={undefined} bind:loading - synUrl={false} syncQueuedRunsCount={false} refreshRate={10000} + computeMinAndMax={undefined} />
diff --git a/frontend/src/lib/components/TestJobLoader.svelte b/frontend/src/lib/components/TestJobLoader.svelte index b06e013656..746c8bb111 100644 --- a/frontend/src/lib/components/TestJobLoader.svelte +++ b/frontend/src/lib/components/TestJobLoader.svelte @@ -181,7 +181,7 @@ errorIteration += 1 if (errorIteration == 5) { notfound = true - await clearCurrentJob() + job = undefined } console.warn(err) } diff --git a/frontend/src/lib/components/runs/JobLoader.svelte b/frontend/src/lib/components/runs/JobLoader.svelte index d312f69440..ff703c0699 100644 --- a/frontend/src/lib/components/runs/JobLoader.svelte +++ b/frontend/src/lib/components/runs/JobLoader.svelte @@ -1,21 +1,11 @@ - - +
{#if job}
@@ -52,6 +53,11 @@ Mem: {job?.['mem_peak'] ? `${(job['mem_peak'] / 1024).toPrecision(4)}MB` : 'N/A'} {/if} + {#if workspace && $workspaceStore != workspace} + + {workspace} + + {/if}
- Results + {#if job?.type === Job.type.COMPLETED_JOB} + Results + {/if} {#if job && 'scheduled_for' in job && !job.running && job.scheduled_for && forLater(job.scheduled_for)}
diff --git a/frontend/src/lib/components/runs/ManuelDatePicker.svelte b/frontend/src/lib/components/runs/ManuelDatePicker.svelte index 56e308f053..991033a71c 100644 --- a/frontend/src/lib/components/runs/ManuelDatePicker.svelte +++ b/frontend/src/lib/components/runs/ManuelDatePicker.svelte @@ -8,61 +8,74 @@ export let loading: boolean = false export let selectedManualDate = 0 - const manualDates = [ + export function computeMinMax(): { minTs: string; maxTs: string } | undefined { + return manualDates[selectedManualDate].computeMinMax() + } + + const manualDates: { + label: string + computeMinMax: () => { minTs: string; maxTs: string } | undefined + }[] = [ { label: 'Last 1000 runs', - setMinMax: () => { - minTs = undefined - maxTs = undefined + computeMinMax: () => { + return undefined } }, { label: 'Within 30 seconds', - setMinMax: () => { - minTs = new Date(new Date().getTime() - 30 * 1000).toISOString() - maxTs = new Date().toISOString() + computeMinMax: () => { + let minTs = new Date(new Date().getTime() - 30 * 1000).toISOString() + let maxTs = new Date().toISOString() + return { minTs, maxTs } } }, { label: 'Within last minute', - setMinMax: () => { - minTs = new Date(new Date().getTime() - 60 * 1000).toISOString() - maxTs = new Date().toISOString() + computeMinMax: () => { + let minTs = new Date(new Date().getTime() - 60 * 1000).toISOString() + let maxTs = new Date().toISOString() + return { minTs, maxTs } } }, { label: 'Within last 5 minutes', - setMinMax: () => { - minTs = new Date(new Date().getTime() - 5 * 60 * 1000).toISOString() - maxTs = new Date().toISOString() + computeMinMax: () => { + let minTs = new Date(new Date().getTime() - 5 * 60 * 1000).toISOString() + let maxTs = new Date().toISOString() + return { minTs, maxTs } } }, { label: 'Within last 30 minutes', - setMinMax: () => { - minTs = new Date(new Date().getTime() - 30 * 60 * 1000).toISOString() - maxTs = new Date().toISOString() + computeMinMax: () => { + let minTs = new Date(new Date().getTime() - 30 * 60 * 1000).toISOString() + let maxTs = new Date().toISOString() + return { minTs, maxTs } } }, { label: 'Within last 24 hours', - setMinMax: () => { - minTs = new Date(new Date().getTime() - 24 * 60 * 60 * 1000).toISOString() - maxTs = new Date().toISOString() + computeMinMax: () => { + let minTs = new Date(new Date().getTime() - 24 * 60 * 60 * 1000).toISOString() + let maxTs = new Date().toISOString() + return { minTs, maxTs } } }, { label: 'Within last 7 days', - setMinMax: () => { - minTs = new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000).toISOString() - maxTs = new Date().toISOString() + computeMinMax: () => { + let minTs = new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000).toISOString() + let maxTs = new Date().toISOString() + return { minTs, maxTs } } }, { label: 'Within last month', - setMinMax: () => { - minTs = new Date(new Date().getTime() - 30 * 24 * 60 * 60 * 1000).toISOString() - maxTs = new Date().toISOString() + computeMinMax: () => { + let minTs = new Date(new Date().getTime() - 30 * 24 * 60 * 60 * 1000).toISOString() + let maxTs = new Date().toISOString() + return { minTs, maxTs } } } ] @@ -75,7 +88,11 @@ size="xs" wrapperClasses="border rounded-md" on:click={() => { - manualDates[selectedManualDate].setMinMax() + const ts = computeMinMax() + if (ts) { + minTs = ts.minTs + maxTs = ts.maxTs + } dispatch('loadJobs') }} dropdownItems={[ @@ -83,7 +100,14 @@ label: d.label, onClick: () => { selectedManualDate = i - d.setMinMax() + const ts = d.computeMinMax() + if (ts) { + minTs = ts.minTs + maxTs = ts.maxTs + } else { + minTs = undefined + maxTs = undefined + } dispatch('loadJobs') } })) diff --git a/frontend/src/lib/components/runs/QueuePopover.svelte b/frontend/src/lib/components/runs/QueuePopover.svelte index 70f63be854..797135da9f 100644 --- a/frontend/src/lib/components/runs/QueuePopover.svelte +++ b/frontend/src/lib/components/runs/QueuePopover.svelte @@ -6,6 +6,7 @@ import { displayDate } from '$lib/utils' let jobs: QueuedJob[] | undefined = undefined + export let allWorkspaces: boolean = false getQueuedJobs() async function getQueuedJobs() { @@ -13,7 +14,8 @@ workspace: $workspaceStore ?? '', scheduledForBeforeNow: true, suspended: false, - running: false + running: false, + allWorkspaces }) } @@ -35,7 +37,7 @@
{job.id} diff --git a/frontend/src/lib/components/runs/RunRow.svelte b/frontend/src/lib/components/runs/RunRow.svelte index 0ecd75f2bb..10175f226a 100644 --- a/frontend/src/lib/components/runs/RunRow.svelte +++ b/frontend/src/lib/components/runs/RunRow.svelte @@ -56,7 +56,6 @@ )} style="width: {containerWidth}px" on:click={() => { - selectedId = job.id dispatch('select') }} > diff --git a/frontend/src/lib/components/runs/RunsFilter.svelte b/frontend/src/lib/components/runs/RunsFilter.svelte index 6cfe0e4d63..9244b6cd9d 100644 --- a/frontend/src/lib/components/runs/RunsFilter.svelte +++ b/frontend/src/lib/components/runs/RunsFilter.svelte @@ -10,6 +10,7 @@ import Label from '../Label.svelte' import Section from '../Section.svelte' import CloseButton from '../common/CloseButton.svelte' + import { workspaceStore } from '$lib/stores' // Filters export let path: string | null = null @@ -29,6 +30,7 @@ export let paths: string[] = [] export let usernames: string[] = [] export let folders: string[] = [] + export let allWorkspaces = false let copyArgFilter = argFilter let copyResultFilter = resultFilter @@ -54,6 +56,16 @@
{#if !mobile}
+ {#if $workspaceStore == 'admins'} +
+ Workspaces + + + + +
+ {/if} +
Filter by | undefined = undefined + export let allWorkspaces: boolean = false
@@ -15,7 +16,7 @@ jobs - + {/if}
diff --git a/frontend/src/lib/components/runs/RunsTable.svelte b/frontend/src/lib/components/runs/RunsTable.svelte index 7f40de7062..d9ad1c1ff1 100644 --- a/frontend/src/lib/components/runs/RunsTable.svelte +++ b/frontend/src/lib/components/runs/RunsTable.svelte @@ -2,11 +2,12 @@ import type { Job } from '$lib/gen' import RunRow from './RunRow.svelte' import VirtualList from 'svelte-tiny-virtual-list' - import { onMount } from 'svelte' + import { createEventDispatcher, onMount } from 'svelte' //import InfiniteLoading from 'svelte-infinite-loading' - export let jobs: Job[] = [] + export let jobs: Job[] | undefined = undefined export let selectedId: string | undefined = undefined + export let selectedWorkspace: string | undefined = undefined // const loadMoreQuantity: number = 100 function getTime(job: Job): string | undefined { @@ -56,7 +57,7 @@ return sortedLogs } - $: groupedJobs = groupJobsByDay(jobs) + $: groupedJobs = jobs ? groupJobsByDay(jobs) : undefined type FlatJobs = | { @@ -81,14 +82,14 @@ return flatJobs } - $: flatJobs = flattenJobs(groupedJobs) + $: flatJobs = groupedJobs ? flattenJobs(groupedJobs) : undefined let stickyIndices: number[] = [] $: { stickyIndices = [] let index = 0 - for (const entry of flatJobs) { + for (const entry of flatJobs ?? []) { if (entry.type === 'date') { stickyIndices.push(index) } @@ -117,17 +118,29 @@ } */ - onMount(() => { + function computeHeight() { tableHeight = document.querySelector('#runs-table-wrapper')!.parentElement?.clientHeight ?? 0 + } + onMount(() => { + computeHeight() }) + const dispatch = createEventDispatcher() -
+ computeHeight()} /> + +
-
+
{jobs?.length == 1000 ? '1000+' : jobs ? jobs.length.toString() : '...'} jobs
Timestamp
Path
Triggered by
@@ -136,44 +149,48 @@
- {@const jobOrDate = flatJobs[index]} + {#if flatJobs} + {@const jobOrDate = flatJobs[index]} - {#if jobOrDate} - {#if jobOrDate?.type === 'date'} -
- {jobOrDate.date} -
+ {#if jobOrDate} + {#if jobOrDate?.type === 'date'} +
+ {jobOrDate.date} +
+ {:else} +
+ { + selectedWorkspace = jobOrDate.job.workspace_id + selectedId = jobOrDate.job.id + dispatch('select') + }} + on:filterByPath + on:filterByUser + on:filterByFolder + {containerWidth} + /> +
+ {/if} {:else} -
- -
+ {JSON.stringify(jobOrDate)} {/if} {:else} - {JSON.stringify(jobOrDate)} +
+
...
+
...
+
...
+
...
+
{/if}
-
{#if jobs?.length == 0} diff --git a/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte index 15ed825875..1f9bb77238 100644 --- a/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte @@ -1,5 +1,13 @@ @@ -129,7 +260,7 @@ on:confirmed={async () => { cancelAllJobs = false let uuids = await JobService.cancelAll({ workspace: $workspaceStore ?? '' }) - jobLoader?.loadJobs(true) + jobLoader?.loadJobs(minTs, maxTs, true, true) sendUserToast(`Canceled ${uuids.length} jobs`) }} on:canceled={() => { @@ -140,7 +271,7 @@ {#if selectedId} - + {/if} @@ -180,6 +311,7 @@ bind:resultError bind:jobKindsCat bind:hideSchedules + bind:allWorkspaces on:change={reloadLogsWithoutFilterError} {usernames} {folders} @@ -195,12 +327,13 @@ on:zoom={async (e) => { minTs = e.detail.min.toISOString() maxTs = e.detail.max.toISOString() + jobLoader?.loadJobs(minTs, maxTs, true) }} />
- + - + { + jobLoader?.loadJobs(minTs, maxTs, true, true) + }} + bind:minTs + bind:maxTs + bind:selectedManualDate + {loading} + bind:this={manualDatePicker} + /> { user = null folder = null @@ -289,7 +432,7 @@ {#if selectedId} - + {:else}
No job selected
{/if} @@ -329,6 +472,7 @@ bind:argError bind:resultError bind:hideSchedules + bind:allWorkspaces mobile={true} on:change={reloadLogsWithoutFilterError} /> @@ -341,13 +485,14 @@ on:zoom={async (e) => { minTs = e.detail.min.toISOString() maxTs = e.detail.max.toISOString() + jobLoader?.loadJobs(minTs, maxTs, true) }} />
{#if queue_count} - + {/if} - + { + jobLoader?.loadJobs(minTs, maxTs, true, true) + }} + bind:this={manualDatePicker} + bind:minTs + bind:maxTs + bind:selectedManualDate + {loading} + /> { runDrawer.openDrawer() }}