mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 16:02:14 +00:00
improve flow viewer
This commit is contained in:
@@ -18,7 +18,8 @@
|
||||
| 'gif'
|
||||
| 'error'
|
||||
| 'approval'
|
||||
| undefined = inferResultKind(result)
|
||||
| undefined
|
||||
$: resultKind = inferResultKind(result)
|
||||
|
||||
let forceJson = false
|
||||
|
||||
@@ -79,7 +80,7 @@
|
||||
{#if result != undefined}
|
||||
{#if resultKind && resultKind != 'json'}
|
||||
<div class="mb-2 text-gray-500 text-sm bg-gray-50/20">
|
||||
as JSON <input type="checkbox" bind:checked={forceJson} /></div
|
||||
as JSON <input type="checkbox" bind:checked={forceJson} /></div
|
||||
>{/if}{#if typeof result == 'object' && Object.keys(result).length > 0}<div
|
||||
class="mb-2 text-sm text-gray-700"
|
||||
>The result keys are: <b>{truncate(Object.keys(result).join(', '), 50)}</b></div
|
||||
|
||||
@@ -15,21 +15,21 @@
|
||||
let stepDetail: FlowModule | undefined = undefined
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full">
|
||||
<div class="h-full w-full border border-gray-700" class:overflow-auto={overflowAuto}>
|
||||
<div class="grid grid-cols-3 w-full h-full">
|
||||
<div class="h-full col-span-2 w-full border border-gray-700" class:overflow-auto={overflowAuto}>
|
||||
<FlowGraph
|
||||
modules={flow?.value?.modules}
|
||||
failureModule={flow?.value?.failure_module}
|
||||
on:click={(e) => (stepDetail = e.detail)}
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full border-l border-r border-b border-gray-700 min-h-[150px] p-2">
|
||||
<div class="w-full border-r border-b border-t border-gray-700 min-h-[150px] p-2 overflow-auto">
|
||||
{#if stepDetail == undefined}
|
||||
<span class="font-black text-lg w-full my-4">
|
||||
<span>Click on a step to see its details</span>
|
||||
</span>
|
||||
{:else}
|
||||
<div class="font-black text-lg w-full mb-2 "
|
||||
<div class="font-black text-lg w-full mb-6"
|
||||
>Step {stepDetail.id ?? ''}<span class="ml-2 font-normal">{stepDetail.summary || ''}</span
|
||||
></div
|
||||
>
|
||||
@@ -37,7 +37,7 @@
|
||||
<div> An identity step return as output its input </div>
|
||||
{:else if stepDetail.value.type == 'rawscript'}
|
||||
<div class="text-2xs mb-4">
|
||||
<h3>Step Inputs</h3>
|
||||
<h3 class="mb-2">Step Inputs</h3>
|
||||
<InputTransformsViewer
|
||||
inputTransforms={stepDetail?.value?.input_transforms ??
|
||||
stepDetail?.input_transforms ??
|
||||
@@ -45,7 +45,7 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<h3>Code</h3>
|
||||
<h3 class="mb-2">Code</h3>
|
||||
<HighlightCode language={stepDetail.value.language} code={stepDetail.value.content} />
|
||||
{:else if stepDetail.value.type == 'script'}
|
||||
<div class="mb-4">
|
||||
@@ -59,7 +59,7 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-2xs mb-4">
|
||||
<h3>Step Inputs</h3>
|
||||
<h3 class="mb-2">Step Inputs</h3>
|
||||
<InputTransformsViewer
|
||||
inputTransforms={stepDetail?.value?.input_transforms ??
|
||||
stepDetail?.input_transforms ??
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
<script lang="ts">
|
||||
import type { CompletedJob } from '$lib/gen'
|
||||
|
||||
import DisplayResult from './DisplayResult.svelte'
|
||||
import LogViewer from './LogViewer.svelte'
|
||||
|
||||
export let result: any
|
||||
export let logs: string
|
||||
export let col: boolean = false
|
||||
export let noBorder = false
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-2 shadow border border-gray-800 h-full">
|
||||
<div
|
||||
class:border={!noBorder}
|
||||
class="grid {!col ? 'grid-cols-2' : 'grid-rows-2'} shadow border-gray-400 h-full"
|
||||
>
|
||||
<div class="bg-white max-h-80 h-full p-1 overflow-auto relative">
|
||||
<span class="text-gray-500">Result</span>
|
||||
<DisplayResult {result} />
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
/>
|
||||
<div class="h-full pt-4 grow">
|
||||
{#if jobId}
|
||||
<FlowStatusViewer bind:flowState={$flowStateStore} {jobId} bind:job />
|
||||
<FlowStatusViewer isRoot bind:flowState={$flowStateStore} {jobId} bind:job />
|
||||
{:else}
|
||||
<div class="italic text-gray-500 h-full grow"> Flow status will be displayed here </div>
|
||||
{/if}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { onDestroy } from 'svelte'
|
||||
import type { FlowState } from './flows/flowState'
|
||||
import { Button, Tab } from './common'
|
||||
import { Badge, Button, Tab } from './common'
|
||||
import DisplayResult from './DisplayResult.svelte'
|
||||
import Tabs from './common/tabs/Tabs.svelte'
|
||||
import { FlowGraph } from './graph'
|
||||
@@ -25,9 +25,17 @@
|
||||
}
|
||||
| undefined = undefined
|
||||
export let job: Job | undefined = undefined
|
||||
export let flowModuleStates: Record<string, FlowStatusModule.type> = {}
|
||||
export let flowModuleStates: Record<
|
||||
string,
|
||||
{ type: FlowStatusModule.type; logs?: string; result?: any }
|
||||
> = {}
|
||||
|
||||
let localFlowModuleStates: Record<string, FlowStatusModule.type> = {}
|
||||
let localFlowModuleStates: Record<
|
||||
string,
|
||||
{ type: FlowStatusModule.type; logs?: string; result?: any }
|
||||
> = {}
|
||||
|
||||
let selectedNode: string | undefined = undefined
|
||||
|
||||
let jobResults: any[] = []
|
||||
let jobFailures: boolean[] = []
|
||||
@@ -41,7 +49,7 @@
|
||||
Object.entries(localFlowModuleStates).forEach(([moduleId, state]) => {
|
||||
if (
|
||||
flowModuleStates[moduleId] !== state &&
|
||||
flowModuleStates[moduleId] !== FlowStatusModule.type.FAILURE
|
||||
flowModuleStates[moduleId]?.type !== FlowStatusModule.type.FAILURE
|
||||
) {
|
||||
flowModuleStates[moduleId] = state
|
||||
}
|
||||
@@ -63,14 +71,27 @@
|
||||
: []
|
||||
) ?? []
|
||||
|
||||
$: innerModules.forEach((module, i) => {
|
||||
if (
|
||||
module.type === FlowStatusModule.type.WAITING_FOR_EVENTS &&
|
||||
localFlowModuleStates?.[innerModules?.[i - 1]?.id ?? '']?.type ==
|
||||
FlowStatusModule.type.SUCCESS
|
||||
) {
|
||||
localFlowModuleStates[module.id ?? ''] = { type: module.type }
|
||||
}
|
||||
})
|
||||
|
||||
let errorCount = 0
|
||||
async function loadJobInProgress() {
|
||||
if (jobId != '00000000-0000-0000-0000-000000000000') {
|
||||
try {
|
||||
job = await JobService.getJob({
|
||||
const newJob = await JobService.getJob({
|
||||
workspace: $workspaceStore ?? '',
|
||||
id: jobId ?? ''
|
||||
})
|
||||
if (JSON.stringify(newJob) !== JSON.stringify(job)) {
|
||||
job = newJob
|
||||
}
|
||||
errorCount = 0
|
||||
} catch (e) {
|
||||
errorCount += 1
|
||||
@@ -186,11 +207,18 @@
|
||||
jobFailures[j] = e.detail.success === false
|
||||
}
|
||||
if (e.detail.type == 'QueuedJob') {
|
||||
localFlowModuleStates[flowJobIds.moduleId] = FlowStatusModule.type.IN_PROGRESS
|
||||
localFlowModuleStates[flowJobIds.moduleId] = {
|
||||
type: FlowStatusModule.type.IN_PROGRESS,
|
||||
logs: e.detail.logs
|
||||
}
|
||||
} else {
|
||||
localFlowModuleStates[flowJobIds.moduleId] = e.detail.success
|
||||
? FlowStatusModule.type.SUCCESS
|
||||
: FlowStatusModule.type.FAILURE
|
||||
localFlowModuleStates[flowJobIds.moduleId] = {
|
||||
type: e.detail.success
|
||||
? FlowStatusModule.type.SUCCESS
|
||||
: FlowStatusModule.type.FAILURE,
|
||||
logs: e.detail.logs,
|
||||
result: e.detail.result
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -242,11 +270,18 @@
|
||||
flowState[mod.id].previewArgs = e.detail.args
|
||||
}
|
||||
if (e.detail.type == 'QueuedJob') {
|
||||
localFlowModuleStates[mod.id] = FlowStatusModule.type.IN_PROGRESS
|
||||
localFlowModuleStates[mod.id] = {
|
||||
type: FlowStatusModule.type.IN_PROGRESS,
|
||||
logs: e.detail.logs
|
||||
}
|
||||
} else {
|
||||
localFlowModuleStates[mod.id] = e.detail.success
|
||||
? FlowStatusModule.type.SUCCESS
|
||||
: FlowStatusModule.type.FAILURE
|
||||
localFlowModuleStates[mod.id] = {
|
||||
type: e.detail.success
|
||||
? FlowStatusModule.type.SUCCESS
|
||||
: FlowStatusModule.type.FAILURE,
|
||||
logs: e.detail.logs,
|
||||
result: e.detail.result
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
@@ -270,14 +305,35 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if job.raw_flow && !isListJob}
|
||||
<div class="{selected != 'graph' ? 'hidden' : ''} lg:mx-40 mx-10 border border-gray-400 mt-4">
|
||||
<div class="{selected != 'graph' ? 'hidden' : ''} mx-10 mt-4">
|
||||
<div class="border" />
|
||||
<FlowGraph
|
||||
flowModuleStates={localFlowModuleStates}
|
||||
notSelectable
|
||||
modules={job.raw_flow?.modules ?? []}
|
||||
failureModule={job.raw_flow?.failure_module}
|
||||
/>
|
||||
<div class="grid grid-cols-3">
|
||||
<div class="col-span-2 bg-gray-50">
|
||||
<FlowGraph
|
||||
flowModuleStates={localFlowModuleStates}
|
||||
on:click={(e) => {
|
||||
if (e.detail.id) {
|
||||
selectedNode = e.detail.id
|
||||
}
|
||||
}}
|
||||
modules={job.raw_flow?.modules ?? []}
|
||||
failureModule={job.raw_flow?.failure_module}
|
||||
/>
|
||||
</div>
|
||||
<div class="border-l border-gray-400">
|
||||
{#if selectedNode}
|
||||
{#if localFlowModuleStates[selectedNode]}
|
||||
<Badge>{localFlowModuleStates[selectedNode].type}</Badge>
|
||||
<FlowJobResult
|
||||
noBorder
|
||||
col
|
||||
result={localFlowModuleStates[selectedNode].result ?? {}}
|
||||
logs={localFlowModuleStates[selectedNode].logs ?? ''}
|
||||
/>
|
||||
{/if}
|
||||
{:else}<p class="p-2 text-gray-600 italic">Select a node to see its details here</p>{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
|
||||
<div class="flex space-x-2 items-center">
|
||||
{#if path.startsWith('hub/')}
|
||||
<IconedResourceType name={path.split('/')[2]} silent={true} />
|
||||
<span>{path}</span>
|
||||
<div>
|
||||
<IconedResourceType width="20px" height="20px" name={path.split('/')[2]} silent={true} />
|
||||
</div>
|
||||
<span class="text-sm">{path}</span>
|
||||
{:else}
|
||||
<Badge color="blue">Workspace</Badge>
|
||||
<span>{path}</span>
|
||||
<span class="text-sm">{path}</span>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -43,21 +43,27 @@
|
||||
</Badge>
|
||||
</div>
|
||||
{:else if job && 'running' in job && job.running}
|
||||
<Badge large color="yellow">
|
||||
<Icon data={faCircle} scale={SMALL_ICON_SCALE} class="mr-2" />
|
||||
Running {#if job.flow_status}({job.flow_status?.step ?? ''} of {job.raw_flow?.modules?.length ??
|
||||
'?'}){/if}
|
||||
</Badge>
|
||||
<div>
|
||||
<Badge large color="yellow">
|
||||
<Icon data={faCircle} scale={SMALL_ICON_SCALE} class="mr-2" />
|
||||
Running {#if job.flow_status}({job.flow_status?.step ?? ''} of {job.raw_flow?.modules
|
||||
?.length ?? '?'}){/if}
|
||||
</Badge>
|
||||
</div>
|
||||
{:else if job && 'running' in job && 'scheduled_for' in job && job.scheduled_for && forLater(job.scheduled_for)}
|
||||
<Badge>
|
||||
<Icon data={faCalendar} scale={SMALL_ICON_SCALE} class="mr-2" />
|
||||
Scheduled for {displayDate(job.scheduled_for)}
|
||||
</Badge>
|
||||
<div>
|
||||
<Badge>
|
||||
<Icon data={faCalendar} scale={SMALL_ICON_SCALE} class="mr-2" />
|
||||
Scheduled for {displayDate(job.scheduled_for)}
|
||||
</Badge>
|
||||
</div>
|
||||
{:else if job && 'running' in job}
|
||||
<Badge>
|
||||
<Icon data={faClock} scale={SMALL_ICON_SCALE} class="mr-2" />
|
||||
Queued
|
||||
</Badge>
|
||||
<div>
|
||||
<Badge>
|
||||
<Icon data={faClock} scale={SMALL_ICON_SCALE} class="mr-2" />
|
||||
Queued
|
||||
</Badge>
|
||||
</div>
|
||||
{:else}
|
||||
<Icon class="text-gray-200" data={faCircle} scale={SMALL_ICON_SCALE} />
|
||||
{/if}
|
||||
|
||||
@@ -18,13 +18,14 @@
|
||||
import { defaultIfEmptyString, truncateRev } from '$lib/utils'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { numberToChars } from '../flows/utils'
|
||||
import type { FlowState } from '../flows/flowState'
|
||||
|
||||
export let modules: FlowModule[] | undefined = []
|
||||
export let failureModule: FlowModule | undefined = undefined
|
||||
export let minHeight: number = 0
|
||||
export let notSelectable = false
|
||||
export let flowModuleStates: Record<string, FlowStatusModule.type> | undefined = undefined
|
||||
export let flowModuleStates:
|
||||
| Record<string, { type: FlowStatusModule.type; logs?: string; result?: any }>
|
||||
| undefined = undefined
|
||||
|
||||
let selectedNode: string | undefined = undefined
|
||||
|
||||
@@ -147,6 +148,8 @@
|
||||
return 'rgb(248 113 113)'
|
||||
case FlowStatusModule.type.IN_PROGRESS:
|
||||
return 'rgb(253, 240, 176)'
|
||||
case FlowStatusModule.type.WAITING_FOR_EVENTS:
|
||||
return 'rgb(229, 176, 253)'
|
||||
default:
|
||||
return '#fff'
|
||||
}
|
||||
@@ -199,7 +202,7 @@
|
||||
bgColor:
|
||||
selectedNode == onClickDetail.id
|
||||
? '#f5f5f5'
|
||||
: getStateColor(flowModuleStates?.[onClickDetail.id]),
|
||||
: getStateColor(flowModuleStates?.[onClickDetail.id]?.type),
|
||||
parentIds,
|
||||
clickCallback: (node) => {
|
||||
if (!notSelectable) {
|
||||
@@ -391,7 +394,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div bind:clientWidth={width} class="w-full h-full overflow-hidden">
|
||||
<div bind:clientWidth={width} class="w-full h-full overflow-hidden relative">
|
||||
{#if width && height}
|
||||
<Svelvet {nodes} {width} {edges} {height} bgColor="rgb(249 250 251)" />
|
||||
{/if}
|
||||
|
||||
@@ -5,27 +5,16 @@
|
||||
export let job: QueuedJob | CompletedJob
|
||||
</script>
|
||||
|
||||
<div class="overflow-x-auto relative">
|
||||
<table class="text-sm text-left text-gray-500 dark:text-gray-400">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row" class="py-2 pr-6 font-bold text-gray-900 whitespace-nowrap dark:text-white">
|
||||
Status
|
||||
</th>
|
||||
<td class="py-2 "> <JobStatus {job} /></td>
|
||||
</tr>
|
||||
{#if job}
|
||||
<tr>
|
||||
<th scope="row" class="py-2 pr-6 font-bold text-gray-900 whitespace-nowrap ">
|
||||
Job Id
|
||||
</th>
|
||||
<td class="py-2">
|
||||
<a rel="noreferrer" target="_blank" href="/run/{job?.id}?workspace={job?.workspace_id}">
|
||||
{job?.id}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="grid grid-cols-2 gap-4 mb-1 text-gray-500 dark:text-gray-400">
|
||||
<JobStatus {job} />
|
||||
{#if job}
|
||||
<div
|
||||
><div class=" text-gray-900 whitespace-nowrap ">
|
||||
<span class="font-bold">Job Id</span>
|
||||
<a rel="noreferrer" target="_blank" href="/run/{job?.id}?workspace={job?.workspace_id}">
|
||||
{job?.id}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user