mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-21 00:02:23 +00:00
feat(frontend): show runs using a time chart
This commit is contained in:
@@ -15,9 +15,12 @@
|
||||
LogarithmicScale
|
||||
} from 'chart.js'
|
||||
import type { CompletedJob } from '$lib/gen'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let jobs: CompletedJob[] | undefined = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
$: success = jobs?.filter((x) => x.success)
|
||||
$: failed = jobs?.filter((x) => !x.success)
|
||||
ChartJS.register(
|
||||
@@ -54,13 +57,19 @@
|
||||
const zoomOptions = {
|
||||
pan: {
|
||||
enabled: true,
|
||||
modifierKey: 'ctrl' as 'ctrl'
|
||||
modifierKey: 'ctrl' as 'ctrl',
|
||||
onPan: ({ chart }) => {
|
||||
console.log('XXX')
|
||||
}
|
||||
},
|
||||
zoom: {
|
||||
drag: {
|
||||
enabled: true
|
||||
},
|
||||
mode: 'x' as 'x'
|
||||
mode: 'x' as 'x',
|
||||
onZoom: ({ chart }) => {
|
||||
dispatch('zoom', { min: new Date(chart.scales.x.min), max: new Date(chart.scales.x.max) })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { goto } from '$app/navigation'
|
||||
import { page } from '$app/stores'
|
||||
import type { Job } from '$lib/gen'
|
||||
import {
|
||||
displayDate,
|
||||
@@ -15,13 +17,14 @@
|
||||
faFastForward,
|
||||
faHourglassHalf,
|
||||
faRobot,
|
||||
faSearch,
|
||||
faTimes,
|
||||
faUser,
|
||||
faWind
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { check } from 'svelte-awesome/icons'
|
||||
import { Badge } from '../common'
|
||||
import { Badge, Button } from '../common'
|
||||
|
||||
const SMALL_ICON_SCALE = 0.7
|
||||
|
||||
@@ -94,6 +97,12 @@
|
||||
{:else if 'job_kind' in job && job.job_kind == 'identity'}
|
||||
<a href="/run/{job.id}">no op</a>
|
||||
{/if}
|
||||
<button
|
||||
class="ml-1"
|
||||
on:click={() => {
|
||||
goto(`/runs/${job.script_path}?${$page.url.searchParams.toString()}`)
|
||||
}}><Badge><Icon scale={0.7} data={faSearch} /></Badge></button
|
||||
>
|
||||
</div>
|
||||
<div class="whitespace-nowrap">
|
||||
{#if 'job_kind' in job}<a href="/run/{job.id}"
|
||||
|
||||
@@ -18,10 +18,12 @@
|
||||
import Tabs from '$lib/components/common/tabs/Tabs.svelte'
|
||||
import Tab from '$lib/components/common/tabs/Tab.svelte'
|
||||
import JobDetail from '$lib/components/jobs/JobDetail.svelte'
|
||||
import { Skeleton } from '$lib/components/common'
|
||||
import { Button, Skeleton } from '$lib/components/common'
|
||||
import { goto } from '$app/navigation'
|
||||
import PageHeader from '$lib/components/PageHeader.svelte'
|
||||
import RunChart from '$lib/components/RunChart.svelte'
|
||||
import { faSearch, faSearchMinus } from '@fortawesome/free-solid-svg-icons'
|
||||
import Icon from 'svelte-awesome'
|
||||
|
||||
let jobs: Job[] | undefined
|
||||
let error: Error | undefined
|
||||
@@ -118,7 +120,7 @@
|
||||
date.setSeconds(date.getSeconds() + 1)
|
||||
ts = date.toISOString()
|
||||
}
|
||||
const newJobs = await fetchJobs(undefined, ts)
|
||||
const newJobs = await fetchJobs(maxTs, minTs ?? ts)
|
||||
if (newJobs && newJobs.length > 0) {
|
||||
const oldJobs = jobs.map((x) => x.id)
|
||||
jobs = newJobs.filter((x) => !oldJobs.includes(x.id)).concat(jobs)
|
||||
@@ -133,6 +135,7 @@
|
||||
$: {
|
||||
if ($workspaceStore) {
|
||||
loadJobs(createdBefore)
|
||||
path // trigger on path change
|
||||
success && isSkipped && jobKinds
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId)
|
||||
@@ -154,6 +157,10 @@
|
||||
clearInterval(intervalId)
|
||||
}
|
||||
})
|
||||
let searchPath = ''
|
||||
$: searchPath = path
|
||||
let minTs = undefined
|
||||
let maxTs = undefined
|
||||
</script>
|
||||
|
||||
<CenteredPage>
|
||||
@@ -162,8 +169,25 @@
|
||||
tooltip="All past and schedule executions of scripts and flows, including previews.
|
||||
You only see your own runs or runs of groups you belong to unless you are an admin."
|
||||
/>
|
||||
<div class="flex flex-row gap-x-2">
|
||||
<input placeholder="Search jobs at a given path" type="text" bind:value={searchPath} />
|
||||
<Button
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
goto('/runs?' + $page.url.searchParams.toString())
|
||||
}}
|
||||
size="xs"><Icon data={faSearchMinus} /></Button
|
||||
>
|
||||
<Button
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
goto('/runs?' + $page.url.searchParams.toString())
|
||||
}}
|
||||
size="xs"><Icon data={faSearch} /></Button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="max-w-7x mt-6">
|
||||
<div class="max-w-7x mt-2">
|
||||
<div class="flex flex-row space-x-4">
|
||||
<select
|
||||
bind:value={success}
|
||||
@@ -199,7 +223,33 @@
|
||||
</Tabs>
|
||||
</div>
|
||||
<div class="border mb-4">
|
||||
<RunChart jobs={completedJobs} />
|
||||
<RunChart
|
||||
jobs={completedJobs}
|
||||
on:zoom={async (e) => {
|
||||
minTs = e.detail.min.toISOString()
|
||||
maxTs = e.detail.max.toISOString()
|
||||
jobs = await fetchJobs(maxTs, minTs)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row gap-x-2 w-full mb-2">
|
||||
<div class="relative"
|
||||
><span class="text-xs absolute -top-4">min datetime</span>
|
||||
<input type="text" value={minTs ?? 'zoom x axis to set min'} disabled />
|
||||
</div>
|
||||
<div class="relative"
|
||||
><span class="text-xs absolute -top-4">max datetime</span>
|
||||
<input type="text" value={maxTs ?? 'zoom x axis to set max'} disabled />
|
||||
</div>
|
||||
<Button
|
||||
variant="border"
|
||||
on:click={async () => {
|
||||
minTs = undefined
|
||||
maxTs = undefined
|
||||
jobs = await fetchJobs(maxTs, minTs)
|
||||
}}
|
||||
size="xs"><Icon data={faSearchMinus} /></Button
|
||||
>
|
||||
</div>
|
||||
<Skeleton loading={!jobs} layout={[[6], 1, [6], 1, [6], 1, [6], 1, [6]]} />
|
||||
{#if jobs}
|
||||
|
||||
Reference in New Issue
Block a user