mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-19 16:02:14 +00:00
fix: improve flow status viewer to display the step details on third tab
This commit is contained in:
@@ -50,7 +50,7 @@
|
||||
noGraph ? 'border-0 w-max' : ''
|
||||
)}
|
||||
>
|
||||
<FlowGraphViewerStep {flow} {stepDetail} />
|
||||
<FlowGraphViewerStep schema={flow.schema} {stepDetail} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import HighlightCode from './HighlightCode.svelte'
|
||||
import InputTransformsViewer from './InputTransformsViewer.svelte'
|
||||
import IconedPath from './IconedPath.svelte'
|
||||
import type { FlowModule, FlowValue } from '$lib/gen'
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
import { Badge, Button, Drawer, DrawerContent } from './common'
|
||||
import { Highlight } from 'svelte-highlight'
|
||||
import ObjectViewer from './propertyPicker/ObjectViewer.svelte'
|
||||
@@ -17,12 +17,7 @@
|
||||
import FlowModuleScript from './flows/content/FlowModuleScript.svelte'
|
||||
import { Copy } from 'lucide-svelte'
|
||||
|
||||
export let flow: {
|
||||
summary: string
|
||||
description?: string
|
||||
value: FlowValue
|
||||
schema?: any
|
||||
}
|
||||
export let schema: any | undefined = undefined
|
||||
|
||||
export let stepDetail: FlowModule | string | undefined = undefined
|
||||
let codeViewer: Drawer
|
||||
@@ -42,7 +37,7 @@
|
||||
<IconedPath path={stepDetail?.value?.path ?? ''} />
|
||||
</a>
|
||||
</div>
|
||||
<div class="text-2xs mb-4">
|
||||
<div class="text-2xs mb-4 mt-2">
|
||||
<h3 class="mb-2">Step Inputs</h3>
|
||||
|
||||
<InputTransformsViewer inputTransforms={stepDetail?.value?.input_transforms ?? {}} />
|
||||
@@ -59,7 +54,7 @@
|
||||
</div>
|
||||
{/if}
|
||||
{:else if stepDetail.value.type == 'rawscript'}
|
||||
<div class="text-2xs mb-4">
|
||||
<div class="text-2xs mb-4 mt-2">
|
||||
<h3 class="mb-2">Step Inputs</h3>
|
||||
<InputTransformsViewer inputTransforms={stepDetail?.value?.input_transforms ?? {}} />
|
||||
</div>
|
||||
@@ -68,6 +63,16 @@
|
||||
<span class="!text-xs">
|
||||
<HighlightCode language={stepDetail.value.language} code={stepDetail.value.content} />
|
||||
</span>
|
||||
<h3 class="mb-2">Lockfile</h3>
|
||||
<div>
|
||||
{#if stepDetail.value.lock}
|
||||
<pre class="bg-surface-secondary text-sm p-2 h-full overflow-auto w-full"
|
||||
>{stepDetail.value.lock}</pre
|
||||
>
|
||||
{:else}
|
||||
<p class="bg-surface-secondary text-sm p-2"> There is no lock file for this script </p>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
</DrawerContent>
|
||||
@@ -79,11 +84,17 @@
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8">
|
||||
Click on a step to see its details
|
||||
</p>
|
||||
<h3 class="mb-2 font-semibold">Flow Inputs</h3>
|
||||
<SchemaViewer schema={flow?.schema} />
|
||||
{#if schema}
|
||||
<h3 class="mb-2 font-semibold">Flow Inputs</h3>
|
||||
<SchemaViewer {schema} />
|
||||
{/if}
|
||||
</div>
|
||||
{:else if stepDetail == 'Input'}
|
||||
<SchemaViewer schema={flow?.schema} />
|
||||
{#if schema}
|
||||
<SchemaViewer {schema} />
|
||||
{:else}
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8"> No input schema </p>
|
||||
{/if}
|
||||
{:else if stepDetail == 'Result'}
|
||||
<p class="font-medium text-secondary text-center pt-4 pb-8"> End of the flow </p>
|
||||
{:else if typeof stepDetail != 'string' && stepDetail.value}
|
||||
@@ -132,12 +143,12 @@
|
||||
</p>
|
||||
{:else if stepDetail.value.type == 'rawscript'}
|
||||
<div class="text-xs">
|
||||
<h3 class="mb-2 font-semibold">Step Inputs</h3>
|
||||
<h3 class="mb-2 font-semibold mt-2">Step Inputs</h3>
|
||||
<InputTransformsViewer inputTransforms={stepDetail?.value?.input_transforms ?? {}} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="mb-2 flex justify-between items-center">
|
||||
<div class="mb-2 mt-4 flex justify-between items-center">
|
||||
<h3 class="font-semibold">Code</h3>
|
||||
<Button size="xs2" color="light" variant="contained" on:click={codeViewer.openDrawer}>
|
||||
Expand
|
||||
@@ -150,10 +161,22 @@
|
||||
class="whitespace-pre-wrap"
|
||||
/>
|
||||
</div>
|
||||
<h3 class="mb-2 mt-4">Lockfile</h3>
|
||||
<div>
|
||||
{#if stepDetail.value.lock}
|
||||
<pre class="bg-surface-secondary text-xs p-2 h-full overflow-auto w-full"
|
||||
>{stepDetail.value.lock}</pre
|
||||
>
|
||||
{:else}
|
||||
<p class="bg-surface-secondary text-sm p-2">
|
||||
There is no lockfile for this inline script
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{:else if stepDetail.value.type == 'script'}
|
||||
<div class="text-2xs">
|
||||
<h3 class="mb-2 font-semibold">Step Inputs</h3>
|
||||
<h3 class="mb-2 font-semibold mt-2">Step Inputs</h3>
|
||||
<InputTransformsViewer inputTransforms={stepDetail?.value?.input_transforms ?? {}} />
|
||||
</div>
|
||||
{#if stepDetail.value.path.startsWith('hub/')}
|
||||
|
||||
@@ -64,9 +64,7 @@
|
||||
</Drawer>
|
||||
<div
|
||||
class:border={!noBorder}
|
||||
class="grid {!col
|
||||
? 'grid-cols-2'
|
||||
: 'grid-rows-2'} shadow border border-tertiary-inverse h-full max-h-[70vh]"
|
||||
class="grid {!col ? 'grid-cols-2' : 'grid-rows-2'} shadow border border-tertiary-inverse h-full"
|
||||
>
|
||||
<div class="bg-surface {col ? '' : 'max-h-80'} h-full p-1 overflow-auto relative">
|
||||
<span class="text-tertiary">Result</span>
|
||||
|
||||
@@ -308,7 +308,7 @@
|
||||
{/if}
|
||||
<FlowProgressBar {job} bind:reset={jobProgressReset} />
|
||||
</div>
|
||||
<div class="overflow-y-auto grow pr-4">
|
||||
<div class="overflow-y-auto grow flex flex-col pr-4">
|
||||
<div class="max-h-1/2 overflow-auto border-b">
|
||||
<SchemaForm
|
||||
noVariablePicker
|
||||
@@ -318,9 +318,10 @@
|
||||
bind:args={$previewArgs}
|
||||
/>
|
||||
</div>
|
||||
<div class="pt-4 grow">
|
||||
<div class="pt-4 flex flex-col grow">
|
||||
{#if jobId}
|
||||
<FlowStatusViewer
|
||||
wideResults
|
||||
{flowStateStore}
|
||||
{jobId}
|
||||
on:jobsLoaded={({ detail }) => {
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
export let selectedJobStep: string | undefined = undefined
|
||||
|
||||
export let isOwner = false
|
||||
export let wideResults = false
|
||||
|
||||
let lastJobId: string = jobId
|
||||
|
||||
@@ -57,4 +58,5 @@
|
||||
{jobId}
|
||||
{workspaceId}
|
||||
{isOwner}
|
||||
{wideResults}
|
||||
/>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
type FlowStatus,
|
||||
type CompletedJob,
|
||||
type QueuedJob,
|
||||
type FlowModuleValue
|
||||
type FlowModuleValue,
|
||||
type FlowModule
|
||||
} from '$lib/gen'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import FlowJobResult from './FlowJobResult.svelte'
|
||||
@@ -32,6 +33,7 @@
|
||||
import { dfs } from './flows/dfs'
|
||||
import { writable, type Writable } from 'svelte/store'
|
||||
import Alert from './common/alert/Alert.svelte'
|
||||
import FlowGraphViewerStep from './FlowGraphViewerStep.svelte'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
@@ -63,6 +65,8 @@
|
||||
export let childFlow: boolean = false
|
||||
export let reducedPolling = false
|
||||
|
||||
export let wideResults = false
|
||||
|
||||
let jobResults: any[] = []
|
||||
let jobFailures: boolean[] = []
|
||||
|
||||
@@ -423,7 +427,7 @@
|
||||
|
||||
let flowTimeline: FlowTimeline
|
||||
|
||||
let rightColumnSelect: 'timeline' | 'detail' = 'timeline'
|
||||
let rightColumnSelect: 'timeline' | 'node_status' | 'node_definition' | 'user_states' = 'timeline'
|
||||
|
||||
let slicedListJobIds: string[] = []
|
||||
|
||||
@@ -449,6 +453,8 @@
|
||||
jobResults = [...new Array(lenToAdd), ...jobResults]
|
||||
updateSlicedListJobIds()
|
||||
}
|
||||
|
||||
let stepDetail: FlowModule | string | undefined = undefined
|
||||
</script>
|
||||
|
||||
{#if notAnonynmous}
|
||||
@@ -456,7 +462,7 @@
|
||||
As a non logged in user, you can only see jobs ran by anonymous users like you
|
||||
</Alert>
|
||||
{:else if job}
|
||||
<div class="flow-root w-full space-y-4 max-w-7xl mx-auto">
|
||||
<div class="flow-root w-full space-y-4 {wideResults ? '' : 'max-w-7xl'} mx-auto">
|
||||
<!-- {#if innerModules.length > 0 && true}
|
||||
<h3 class="text-md leading-6 font-bold text-primay border-b pb-2">Flow result</h3>
|
||||
{:else}
|
||||
@@ -561,7 +567,7 @@
|
||||
{/if}
|
||||
{#if render}
|
||||
{#if innerModules.length > 0 && !isListJob}
|
||||
<Tabs class="mx-auto max-w-7xl" bind:selected>
|
||||
<Tabs class="mx-auto {wideResults ? '' : 'max-w-7xl'}" bind:selected>
|
||||
<Tab value="graph"><span class="font-semibold text-md">Graph</span></Tab>
|
||||
<Tab value="sequence"><span class="font-semibold">Details</span></Tab>
|
||||
</Tabs>
|
||||
@@ -754,8 +760,8 @@
|
||||
</div>
|
||||
{#if render}
|
||||
{#if job.raw_flow && !isListJob}
|
||||
<div class="{selected != 'graph' ? 'hidden' : ''} mt-4">
|
||||
<div class="grid grid-cols-3 border">
|
||||
<div class="{selected != 'graph' ? 'hidden' : ''} grow mt-4">
|
||||
<div class="grid grid-cols-3 border h-full">
|
||||
<div class="col-span-2 bg-surface-secondary">
|
||||
<div class="flex flex-col">
|
||||
{#each Object.values($retryStatus) as count}
|
||||
@@ -779,16 +785,22 @@
|
||||
success={jobId != undefined && isSuccess(job?.['success'])}
|
||||
flowModuleStates={$localModuleStates}
|
||||
on:select={(e) => {
|
||||
rightColumnSelect = 'detail'
|
||||
if (rightColumnSelect != 'node_definition') {
|
||||
rightColumnSelect = 'node_status'
|
||||
}
|
||||
if (typeof e.detail == 'string') {
|
||||
if (e.detail == 'Input') {
|
||||
selectedNode = 'start'
|
||||
stepDetail = undefined
|
||||
} else if (e.detail == 'Result') {
|
||||
selectedNode = 'end'
|
||||
stepDetail = 'end'
|
||||
} else {
|
||||
selectedNode = e.detail
|
||||
stepDetail = e.detail
|
||||
}
|
||||
} else {
|
||||
stepDetail = e.detail
|
||||
selectedNode = e.detail.id
|
||||
}
|
||||
}}
|
||||
@@ -801,7 +813,8 @@
|
||||
>
|
||||
<Tabs bind:selected={rightColumnSelect}>
|
||||
<Tab value="timeline"><span class="font-semibold text-md">Timeline</span></Tab>
|
||||
<Tab value="detail"><span class="font-semibold">Details</span></Tab>
|
||||
<Tab value="node_status"><span class="font-semibold">Node status</span></Tab>
|
||||
<Tab value="node_definition"><span class="font-semibold">Node definition</span></Tab>
|
||||
{#if Object.keys(job?.flow_status?.user_states ?? {}).length > 0}
|
||||
<Tab value="user_states"><span class="font-semibold">User States</span></Tab>
|
||||
{/if}
|
||||
@@ -815,8 +828,8 @@
|
||||
flowModules={dfs(job.raw_flow?.modules ?? [], (x) => x.id)}
|
||||
durationStatuses={localDurationStatuses}
|
||||
/>
|
||||
{:else if rightColumnSelect == 'detail'}
|
||||
<div class="pt-2 h-full">
|
||||
{:else if rightColumnSelect == 'node_status'}
|
||||
<div class="pt-2 min-h-[80vh]">
|
||||
{#if selectedNode}
|
||||
{@const node = $localModuleStates[selectedNode]}
|
||||
|
||||
@@ -887,6 +900,8 @@
|
||||
{:else}<p class="p-2 text-tertiary italic">Select a node to see its details here</p
|
||||
>{/if}
|
||||
</div>
|
||||
{:else if rightColumnSelect == 'node_definition'}
|
||||
<FlowGraphViewerStep {stepDetail} />
|
||||
{:else if rightColumnSelect == 'user_states'}
|
||||
<div class="p-2">
|
||||
<JobArgs argLabel="Key" args={job?.flow_status?.user_states ?? {}} />
|
||||
|
||||
@@ -7,19 +7,11 @@
|
||||
import FieldHeader from './FieldHeader.svelte'
|
||||
import { copyToClipboard } from '../utils'
|
||||
import FlowGraphViewer from './FlowGraphViewer.svelte'
|
||||
import { setContext } from 'svelte'
|
||||
import {
|
||||
SVELVET_CONTEXT_KEY,
|
||||
type SvelvetSettingsContext
|
||||
} from './graph/svelvet/container/models'
|
||||
|
||||
import { Clipboard } from 'lucide-svelte'
|
||||
import YAML from 'yaml'
|
||||
import { yaml } from 'svelte-highlight/languages'
|
||||
|
||||
setContext<SvelvetSettingsContext>(SVELVET_CONTEXT_KEY, {
|
||||
fullHeight: true
|
||||
})
|
||||
|
||||
export let flow: {
|
||||
summary: string
|
||||
description?: string
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
let code: string
|
||||
let language: SupportedLanguage
|
||||
let lock: string | undefined = undefined
|
||||
|
||||
let notFound = false
|
||||
async function loadCode(path: string, hash: string | undefined) {
|
||||
@@ -20,6 +21,7 @@
|
||||
: await getScriptByPath(path!)
|
||||
code = script.content
|
||||
language = script.language
|
||||
lock = script.lock
|
||||
} catch (e) {
|
||||
notFound = true
|
||||
console.error(e)
|
||||
@@ -35,4 +37,9 @@
|
||||
{:else}
|
||||
<HighlightCode {language} {code} />
|
||||
{/if}
|
||||
|
||||
{#if lock}
|
||||
<h3 class="mb-2 mt-6">Lock</h3>
|
||||
<pre class="bg-surface-secondary text-xs p-2 overflow-auto w-full">{lock}</pre>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export const SVELVET_CONTEXT_KEY = 'svelvet_settings_context' as const
|
||||
|
||||
export type SvelvetSettingsContext = {
|
||||
/** Sets the height of the canvas to `100%` instead of an arbitrary value in pixels */
|
||||
fullHeight: boolean
|
||||
}
|
||||
|
||||
@@ -6,12 +6,9 @@
|
||||
createStoreEmpty,
|
||||
populateSvelvetStoreFromUserInput
|
||||
} from '../../store/controllers/storeApi'
|
||||
import { afterUpdate, onMount, getContext } from 'svelte'
|
||||
import { afterUpdate, onMount } from 'svelte'
|
||||
import GraphView from './GraphView.svelte'
|
||||
import { sanitizeUserNodesAndEdges } from '../controllers/middleware'
|
||||
import { SVELVET_CONTEXT_KEY, type SvelvetSettingsContext } from '../models'
|
||||
|
||||
const settings = getContext<SvelvetSettingsContext | undefined>(SVELVET_CONTEXT_KEY)
|
||||
|
||||
export let nodes: UserNodeType[]
|
||||
export let edges: UserEdgeType[]
|
||||
@@ -31,7 +28,6 @@
|
||||
export let download: boolean = false
|
||||
export let dataflow: boolean = false
|
||||
export let nodeSelected: boolean = false
|
||||
const fullHeight = settings?.fullHeight ?? false
|
||||
// generates a unique string for each svelvet component's unique store instance
|
||||
// creates a store that uses the unique sting as the key to create and look up the corresponding store
|
||||
// this way we can have multiple Svelvet Components on the same page and prevent overlap of information
|
||||
@@ -105,7 +101,7 @@
|
||||
<!-- Now that a store has been created from the initial nodes and initial edges we drill props from the store down to the D3 GraphView along with the unique key -->
|
||||
<div
|
||||
class="Svelvet bg-surface-secondary"
|
||||
style={`width: ${width}px; height: ${fullHeight ? '100%' : height + 'px'};`}
|
||||
style={`width: ${width}px; min-height: 100%; height: ${height}px;`}
|
||||
>
|
||||
{#if error != ''}
|
||||
<div class="error text-red-600 center-center p-4">{error}</div>
|
||||
|
||||
@@ -118,9 +118,10 @@ export async function getScriptByPath(path: string): Promise<{
|
||||
tag: string | undefined
|
||||
concurrent_limit: number | undefined
|
||||
concurrency_time_window_s: number | undefined
|
||||
lock?: string
|
||||
}> {
|
||||
if (path.startsWith('hub/')) {
|
||||
const { content, language, schema } = await ScriptService.getHubScriptByPath({ path })
|
||||
const { content, language, schema, lockfile } = await ScriptService.getHubScriptByPath({ path })
|
||||
|
||||
return {
|
||||
content,
|
||||
@@ -129,7 +130,8 @@ export async function getScriptByPath(path: string): Promise<{
|
||||
description: '',
|
||||
tag: undefined,
|
||||
concurrent_limit: undefined,
|
||||
concurrency_time_window_s: undefined
|
||||
concurrency_time_window_s: undefined,
|
||||
lock: lockfile
|
||||
}
|
||||
} else {
|
||||
const script = await ScriptService.getScriptByPath({
|
||||
@@ -143,7 +145,8 @@ export async function getScriptByPath(path: string): Promise<{
|
||||
description: script.description,
|
||||
tag: script.tag,
|
||||
concurrent_limit: script.concurrent_limit,
|
||||
concurrency_time_window_s: script.concurrency_time_window_s
|
||||
concurrency_time_window_s: script.concurrency_time_window_s,
|
||||
lock: script.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +451,7 @@
|
||||
|
||||
<svelte:fragment slot="flow_step">
|
||||
{#if stepDetail}
|
||||
<FlowGraphViewerStep {flow} {stepDetail} />
|
||||
<FlowGraphViewerStep schema={flow.schema} {stepDetail} />
|
||||
{/if}
|
||||
</svelte:fragment>
|
||||
</DetailPageLayout>
|
||||
|
||||
@@ -707,16 +707,9 @@
|
||||
{#if job?.['scheduled_for'] && forLater(job?.['scheduled_for'])}
|
||||
<div class="max-w-7xl mx-auto w-full px-4 sm:px-0">
|
||||
<h2 class="mt-10">Scheduled to be executed later: {displayDate(job?.['scheduled_for'])}</h2>
|
||||
<div class="w-full pt-8">
|
||||
<LogViewer
|
||||
jobId={job.id}
|
||||
isLoading={!(job && 'logs' in job && job.logs)}
|
||||
content={job?.logs}
|
||||
tag={job?.tag}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{:else if job?.job_kind !== 'flow' && job?.job_kind !== 'flowpreview' && job?.job_kind !== 'singlescriptflow'}
|
||||
{/if}
|
||||
{#if job?.job_kind !== 'flow' && job?.job_kind !== 'flowpreview' && job?.job_kind !== 'singlescriptflow'}
|
||||
<div class="max-w-7xl mx-auto w-full px-4 sm:px-0 mb-10">
|
||||
{#if job?.flow_status && typeof job.flow_status == 'object' && !('_metadata' in job.flow_status)}
|
||||
<div class="mt-10" />
|
||||
|
||||
Reference in New Issue
Block a user