mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-12 00:06:14 +00:00
Fix suspend step duplicate forms (#7581)
* nit totalEventsWaiting * remove temp console log * loading indicator in Resume button * refactor * Revert "refactor" This reverts commiteae6213cdd. * useThrottle * fix promises * Revert "fix promises" This reverts commit90a5613245. * svelte 5 migration * Fix duplicate resume forms * Check all flow_jobs
This commit is contained in:
@@ -32,16 +32,6 @@
|
||||
extra,
|
||||
result_streams
|
||||
}: Props = $props()
|
||||
|
||||
// Sometimes the approval form is duplicated but I can't reproduce the issue
|
||||
// This is a temporary debug log to try to catch it when it happens
|
||||
// (See the #each below)
|
||||
$effect(() =>
|
||||
console.log(
|
||||
'suspendStatusVal',
|
||||
Object.entries(suspendStatus.val || {}).map(([k, v]) => [k, v.job.id])
|
||||
)
|
||||
)
|
||||
</script>
|
||||
|
||||
<FlowPreviewStatus {job} {hideJobId} {extra} />
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
import JobAssetsViewer from './assets/JobAssetsViewer.svelte'
|
||||
import McpToolCallDetails from './McpToolCallDetails.svelte'
|
||||
import { SelectionManager } from './graph/selectionUtils.svelte'
|
||||
import { useThrottle } from 'runed'
|
||||
|
||||
let {
|
||||
flowState: flowStateStore,
|
||||
@@ -503,8 +504,21 @@
|
||||
}
|
||||
|
||||
let jobMissingStartedAt: Record<string, number | 'P'> = {}
|
||||
let lastSelectedLoopSwitch: number | undefined
|
||||
let selectedLoopSwitchTimeout: number | undefined = undefined
|
||||
|
||||
let setSelectedLoopSwitch = useThrottle(async (lastStarted: string, mod: FlowStatusModule) => {
|
||||
let position = mod.flow_jobs?.indexOf(lastStarted)
|
||||
if (!position) return
|
||||
for (const flow_job of mod.flow_jobs ?? []) {
|
||||
if (flow_job === lastStarted) {
|
||||
break
|
||||
} else if (flow_job !== lastStarted && suspendStatus.val[flow_job]) {
|
||||
console.log('setSelectedLoopSwitch deleting suspend for', flow_job)
|
||||
delete suspendStatus.val[flow_job]
|
||||
}
|
||||
}
|
||||
console.log('setSelectedLoopSwitch', position, lastStarted, mod.id, suspendStatus)
|
||||
setIteration(position, lastStarted, false, mod.id ?? '', true)
|
||||
}, 2000)
|
||||
|
||||
function updateInnerModules() {
|
||||
if (localModuleStates) {
|
||||
@@ -641,29 +655,7 @@
|
||||
})
|
||||
if (anySet) {
|
||||
updateDurationStatuses(key, nDurationStatuses)
|
||||
selectedLoopSwitchTimeout && clearTimeout(selectedLoopSwitchTimeout)
|
||||
function setSelectedLoopSwitch() {
|
||||
if (lastStarted) {
|
||||
let position = mod.flow_jobs?.indexOf(lastStarted)
|
||||
if (position != undefined) {
|
||||
lastSelectedLoopSwitch = new Date().getTime()
|
||||
console.log('setSelectedLoopSwitch', position, lastStarted)
|
||||
setIteration(position, lastStarted, false, mod.id ?? '', true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
lastSelectedLoopSwitch &&
|
||||
new Date().getTime() - lastSelectedLoopSwitch < 3000
|
||||
) {
|
||||
selectedLoopSwitchTimeout = setTimeout(() => {
|
||||
setSelectedLoopSwitch()
|
||||
}, 2000)
|
||||
} else {
|
||||
console.log('setSelectedLoopSwitch')
|
||||
setSelectedLoopSwitch()
|
||||
}
|
||||
if (lastStarted) setSelectedLoopSwitch(lastStarted, mod)
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
@@ -1293,6 +1285,10 @@
|
||||
tabsHeight.graphHeight
|
||||
)
|
||||
)
|
||||
|
||||
let totalEventsWaiting = $derived(
|
||||
Object.values(suspendStatus?.val ?? {}).reduce((a, b) => a + (b?.nb ?? 0), 0)
|
||||
)
|
||||
</script>
|
||||
|
||||
<JobLoader workspaceOverride={workspaceId} {noLogs} noCode bind:this={jobLoader} />
|
||||
@@ -1744,13 +1740,11 @@
|
||||
</span>
|
||||
{/if}
|
||||
{/each}
|
||||
{#each Object.values(suspendStatus?.val ?? {}) as count}
|
||||
{#if count.nb}
|
||||
<span class="text-sm">
|
||||
Flow suspended, waiting for {count.nb} events
|
||||
</span>
|
||||
{/if}
|
||||
{/each}
|
||||
{#if totalEventsWaiting}
|
||||
<span class="text-sm">
|
||||
Flow suspended, waiting for {totalEventsWaiting} events
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<FlowGraphV2
|
||||
{selectionManager}
|
||||
|
||||
@@ -9,24 +9,26 @@
|
||||
import { Button } from './common'
|
||||
import SchemaForm from './SchemaForm.svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
import { untrack } from 'svelte'
|
||||
|
||||
export let isOwner: boolean
|
||||
export let workspaceId: string | undefined
|
||||
export let job: Job
|
||||
export let light: boolean = false
|
||||
interface Props {
|
||||
isOwner: boolean
|
||||
workspaceId: string | undefined
|
||||
job: Job
|
||||
light?: boolean
|
||||
}
|
||||
|
||||
let default_payload: object = {}
|
||||
let resumeUrl: string | undefined = undefined
|
||||
let cancelUrl: string | undefined = undefined
|
||||
let description: any = undefined
|
||||
let hide_cancel = false
|
||||
let { isOwner, workspaceId, job, light = false }: Props = $props()
|
||||
|
||||
$: approvalStep = (job?.flow_status?.step ?? 1) - 1
|
||||
let default_payload: object = $state({})
|
||||
let resumeUrl: string | undefined = $state(undefined)
|
||||
let cancelUrl: string | undefined = $state(undefined)
|
||||
let description: any = $state(undefined)
|
||||
let hide_cancel = $state(false)
|
||||
|
||||
let defaultValues = {}
|
||||
$: job && getDefaultArgs()
|
||||
let defaultValues = $state({})
|
||||
|
||||
let schema = {}
|
||||
let schema = $state({})
|
||||
let lastJobId: string | undefined = undefined
|
||||
async function getDefaultArgs() {
|
||||
let jobId = job?.flow_status?.modules?.[approvalStep]?.job
|
||||
@@ -56,7 +58,9 @@
|
||||
)
|
||||
}
|
||||
|
||||
let loading = $state(false)
|
||||
async function continu(approve: boolean) {
|
||||
loading = true
|
||||
if ((resumeUrl && approve) || (cancelUrl && !approve)) {
|
||||
let split = (approve ? resumeUrl : cancelUrl)!.split('/')
|
||||
let signatureUrl = split.pop() ?? ''
|
||||
@@ -114,6 +118,10 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
let approvalStep = $derived((job?.flow_status?.step ?? 1) - 1)
|
||||
$effect(() => {
|
||||
job && untrack(() => getDefaultArgs())
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full mt-2 text-xs text-primary">
|
||||
@@ -130,6 +138,7 @@
|
||||
<div>
|
||||
<Button
|
||||
title="Cancel the step"
|
||||
{loading}
|
||||
iconOnly
|
||||
startIcon={{ icon: X }}
|
||||
variant="default"
|
||||
@@ -139,12 +148,13 @@
|
||||
</div>
|
||||
{/if}
|
||||
<div>
|
||||
<Button variant="accent" on:click={() => continu(true)}
|
||||
>Resume <Tooltip class="text-white"
|
||||
>Since you are an owner of this flow, you can send resume events without necessarily
|
||||
knowing the resume id sent by the approval step</Tooltip
|
||||
></Button
|
||||
>
|
||||
<Button variant="accent" on:click={() => continu(true)} {loading}>
|
||||
Resume
|
||||
<Tooltip class="text-white">
|
||||
Since you are an owner of this flow, you can send resume events without necessarily
|
||||
knowing the resume id sent by the approval step
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{#if job?.raw_flow?.modules?.[approvalStep]?.suspend?.resume_form?.schema}
|
||||
@@ -157,9 +167,9 @@
|
||||
<SchemaForm onlyMaskPassword bind:args={default_payload} {defaultValues} {schema} />
|
||||
</div>
|
||||
{/if}
|
||||
<Tooltip
|
||||
>The payload is optional, it is passed to the following step through the `resume` variable</Tooltip
|
||||
>
|
||||
<Tooltip>
|
||||
The payload is optional, it is passed to the following step through the `resume` variable
|
||||
</Tooltip>
|
||||
</div>
|
||||
{:else}
|
||||
You cannot resume the flow yourself without receiving the resume secret since you are not an
|
||||
|
||||
Reference in New Issue
Block a user