feat(frontend): Migrate flow advanced settings to new layout (#2589)

* feat(frontend): wip

* feat(frontend): migrate flow advanced settings to new layout

---------

Co-authored-by: Ruben Fiszel <ruben@windmill.dev>
This commit is contained in:
Faton Ramadani
2023-11-08 17:12:31 +01:00
committed by GitHub
parent 4dbaf2f56b
commit 55e3a95618
8 changed files with 495 additions and 476 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
export let tooltip: string | undefined = undefined
</script>
<div>
<div class="w-full">
<div class="flex flex-row justify-between">
<h2 class="text-base font-semibold mb-2 flex flex-row gap-1">
{label}
@@ -1,4 +1,6 @@
<script lang="ts">
import Label from '$lib/components/Label.svelte'
import Section from '$lib/components/Section.svelte'
import Toggle from '$lib/components/Toggle.svelte'
import Tooltip from '$lib/components/Tooltip.svelte'
@@ -10,40 +12,40 @@
$: isCacheEnabled = Boolean(flowModule.cache_ttl)
</script>
<h2 class="pb-4">
Cache
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/cache">
If defined, the result of the step will be cached for the number of seconds defined such that if
this step were to be re-triggered with the same input it would retrieve and return its cached
value instead of recomputing it.
</Tooltip>
</h2>
{#if flowModule.value.type != 'rawscript'}
<p
>The cache settings need to be set in the referenced script/flow settings directly. Cache for
hub scripts is not available yet.</p
>
{:else}
<Toggle
checked={isCacheEnabled}
on:change={() => {
if (isCacheEnabled && flowModule.cache_ttl != undefined) {
flowModule.cache_ttl = undefined
} else {
flowModule.cache_ttl = 600
}
}}
options={{
right: 'Cache the results for each possible inputs'
}}
/>
<div class="mb-4">
<span class="text-xs font-bold">How long to keep cache valid</span>
<Section label="Cache" class="flex flex-col gap-4">
<svelte:fragment slot="header">
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/cache">
If defined, the result of the step will be cached for the number of seconds defined such that
if this step were to be re-triggered with the same input it would retrieve and return its
cached value instead of recomputing it.
</Tooltip>
</svelte:fragment>
{#if flowModule.cache_ttl}
<SecondsInput bind:seconds={flowModule.cache_ttl} />
{:else}
<SecondsInput disabled />
{/if}
</div>
{/if}
{#if flowModule.value.type != 'rawscript'}
<p
>The cache settings need to be set in the referenced script/flow settings directly. Cache for
hub scripts is not available yet.</p
>
{:else}
<Toggle
checked={isCacheEnabled}
on:change={() => {
if (isCacheEnabled && flowModule.cache_ttl != undefined) {
flowModule.cache_ttl = undefined
} else {
flowModule.cache_ttl = 600
}
}}
options={{
right: 'Cache the results for each possible inputs'
}}
/>
<Label label="How long to keep cache valid">
{#if flowModule.cache_ttl}
<SecondsInput bind:seconds={flowModule.cache_ttl} />
{:else}
<SecondsInput disabled />
{/if}
</Label>
{/if}
</Section>
@@ -7,6 +7,7 @@
import type { FlowEditorContext } from '../types'
import { getContext } from 'svelte'
import { NEVER_TESTED_THIS_FAR } from '../models'
import Section from '$lib/components/Section.svelte'
const { flowStateStore } = getContext<FlowEditorContext>('FlowEditorContext')
@@ -15,72 +16,75 @@
let editor: SimpleEditor | undefined = undefined
$: isStopAfterIfEnabled = Boolean(flowModule.stop_after_if)
$: result = $flowStateStore[flowModule.id]?.previewResult ?? NEVER_TESTED_THIS_FAR
</script>
<div class="flex flex-col items-start space-y-2 {$$props.class}">
<h2 class="pb-4">
Early stop/Break
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/early_stop">
If defined, at the end of the step, the predicate expression will be evaluated to decide if
the flow should stop early.
</Tooltip>
</h2>
<Toggle
checked={isStopAfterIfEnabled}
on:change={() => {
if (isStopAfterIfEnabled && flowModule.stop_after_if) {
flowModule.stop_after_if = undefined
} else {
flowModule.stop_after_if = {
expr: 'result == undefined',
skip_if_stopped: false
}
}
}}
options={{
right: 'Early stop or Break if condition met'
}}
/>
<Section label="Early stop/Break" class="w-full">
<svelte:fragment slot="header">
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/early_stop">
If defined, at the end of the step, the predicate expression will be evaluated to decide if
the flow should stop early.
</Tooltip>
</svelte:fragment>
<div
class="w-full border p-2 flex flex-col {flowModule.stop_after_if ? '' : 'bg-surface-secondary'}"
>
{#if flowModule.stop_after_if}
<Toggle
bind:checked={flowModule.stop_after_if.skip_if_stopped}
options={{
right: 'Label flow as "skipped" if stopped'
}}
/>
<span class="text-xs font-bold">Stop condition expression</span>
<div class="border w-full">
<PropPickerWrapper
{result}
pickableProperties={undefined}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
<Toggle
checked={isStopAfterIfEnabled}
on:change={() => {
if (isStopAfterIfEnabled && flowModule.stop_after_if) {
flowModule.stop_after_if = undefined
} else {
flowModule.stop_after_if = {
expr: 'result == undefined',
skip_if_stopped: false
}
}
}}
options={{
right: 'Early stop or Break if condition met'
}}
/>
<div
class="w-full border p-2 flex flex-col {flowModule.stop_after_if
? ''
: 'bg-surface-secondary'}"
>
{#if flowModule.stop_after_if}
<Toggle
bind:checked={flowModule.stop_after_if.skip_if_stopped}
options={{
right: 'Label flow as "skipped" if stopped'
}}
>
<SimpleEditor
bind:this={editor}
lang="javascript"
bind:code={flowModule.stop_after_if.expr}
class="small-editor"
extraLib={`declare const result = ${JSON.stringify(result)};`}
/>
</PropPickerWrapper>
</div>
{:else}
<Toggle
disabled
options={{
right: 'Label flow as "skipped" if stopped'
}}
/> <span class="text-xs font-bold">Stop condition expression</span>
<textarea disabled rows="3" class="min-h-[80px]" />
{/if}
</div>
/>
<span class="text-xs font-bold">Stop condition expression</span>
<div class="border w-full">
<PropPickerWrapper
{result}
pickableProperties={undefined}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
}}
>
<SimpleEditor
bind:this={editor}
lang="javascript"
bind:code={flowModule.stop_after_if.expr}
class="few-lines-editor"
extraLib={`declare const result = ${JSON.stringify(result)};`}
/>
</PropPickerWrapper>
</div>
{:else}
<Toggle
disabled
options={{
right: 'Label flow as "skipped" if stopped'
}}
/> <span class="text-xs font-bold">Stop condition expression</span>
<textarea disabled rows="3" class="min-h-[80px]" />
{/if}
</div>
</Section>
</div>
@@ -4,6 +4,7 @@
import type { FlowModule } from '$lib/gen'
import JsonEditor from '$lib/components/apps/editor/settingsPanel/inputEditor/JsonEditor.svelte'
import Section from '$lib/components/Section.svelte'
export let flowModule: FlowModule
@@ -14,43 +15,45 @@
$: isMockEnabled = Boolean(flowModule.mock?.enabled)
</script>
<h2 class="pb-4">
Mock
<Tooltip>
If defined and enabled, the step will immediately return the mock value instead of being
executed.
</Tooltip>
</h2>
<Toggle
checked={isMockEnabled}
on:change={() => {
if (isMockEnabled) {
flowModule.mock = {
enabled: false,
return_value: flowModule.mock?.return_value
<Section label="Mock">
<svelte:fragment slot="header">
<Tooltip>
If defined and enabled, the step will immediately return the mock value instead of being
executed.
</Tooltip>
</svelte:fragment>
<Toggle
checked={isMockEnabled}
on:change={() => {
if (isMockEnabled) {
flowModule.mock = {
enabled: false,
return_value: flowModule.mock?.return_value
}
} else {
flowModule.mock = {
enabled: true,
return_value: flowModule.mock?.return_value ?? { example: 'value' }
}
code = JSON.stringify(flowModule.mock?.return_value, null, 2)
}
} else {
flowModule.mock = {
enabled: true,
return_value: flowModule.mock?.return_value ?? { example: 'value' }
}
code = JSON.stringify(flowModule.mock?.return_value, null, 2)
}
}}
options={{
right: 'Enable step mocking'
}}
/>
<div>
<span class="text-xs font-bold">Mocked Return value</span>
{#if flowModule?.mock?.return_value != undefined}
<JsonEditor {code} bind:value={flowModule.mock.return_value} />
{:else}
<input
type="text"
disabled
value={flowModule.mock?.return_value ? code : ''}
class="w-full p-2 border rounded-md"
/>
{/if}
</div>
}}
options={{
right: 'Enable step mocking'
}}
/>
<div>
<span class="text-xs font-bold">Mocked Return value</span>
{#if flowModule?.mock?.return_value != undefined}
<JsonEditor {code} bind:value={flowModule.mock.return_value} />
{:else}
<input
type="text"
disabled
value={flowModule.mock?.return_value ? code : ''}
class="w-full p-2 border rounded-md"
/>
{/if}
</div>
</Section>
@@ -9,6 +9,8 @@
import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte'
import type { FlowEditorContext } from '../types'
import { SecondsInput } from '../../common'
import Section from '$lib/components/Section.svelte'
import Label from '$lib/components/Label.svelte'
export let flowModule: FlowModule
export let previousModuleId: string | undefined
@@ -27,53 +29,54 @@
$: isSleepEnabled = Boolean(flowModule.sleep)
</script>
<h2 class="pb-4">
Sleep
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/sleep">
If defined, at the end of the step, the flow will sleep for a number of seconds before
scheduling the next job (if any, no effect if the step is the last one).
</Tooltip>
</h2>
<Toggle
checked={isSleepEnabled}
on:change={() => {
if (isSleepEnabled && flowModule.sleep != undefined) {
flowModule.sleep = undefined
} else {
flowModule.sleep = {
type: 'static',
value: 0
<Section label="Sleep" class="w-full">
<svelte:fragment slot="header">
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/sleep">
If defined, at the end of the step, the flow will sleep for a number of seconds before
scheduling the next job (if any, no effect if the step is the last one).
</Tooltip>
</svelte:fragment>
<Toggle
checked={isSleepEnabled}
on:change={() => {
if (isSleepEnabled && flowModule.sleep != undefined) {
flowModule.sleep = undefined
} else {
flowModule.sleep = {
type: 'static',
value: 0
}
}
}
}}
options={{
right: 'Sleep after module successful execution'
}}
/>
<div>
<span class="text-xs font-bold">Sleep for duration</span>
{#if flowModule.sleep && schema.properties['sleep']}
<div class="border">
<PropPickerWrapper
notSelectable
{result}
displayContext={false}
pickableProperties={undefined}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
}}
>
<InputTransformForm
bind:arg={flowModule.sleep}
argName="sleep"
{schema}
{previousModuleId}
argExtra={{ seconds: true }}
/>
</PropPickerWrapper>
</div>
{:else}
<SecondsInput disabled />
{/if}
</div>
}}
options={{
right: 'Sleep after module successful execution'
}}
/>
<Label label="Sleep for duration">
{#if flowModule.sleep && schema.properties['sleep']}
<div class="border">
<PropPickerWrapper
notSelectable
{result}
displayContext={false}
pickableProperties={undefined}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
}}
>
<InputTransformForm
bind:arg={flowModule.sleep}
argName="sleep"
{schema}
{previousModuleId}
argExtra={{ seconds: true }}
/>
</PropPickerWrapper>
</div>
{:else}
<SecondsInput disabled />
{/if}
</Label>
</Section>
@@ -14,6 +14,8 @@
import { SecondsInput } from '../../common'
import PropPickerWrapper from '../propPicker/PropPickerWrapper.svelte'
import type { FlowEditorContext } from '../types'
import Section from '$lib/components/Section.svelte'
import Label from '$lib/components/Label.svelte'
const { selectedId, flowStateStore } = getContext<FlowEditorContext>('FlowEditorContext')
const result = $flowStateStore[$selectedId]?.previewResult ?? {}
@@ -47,165 +49,166 @@
}
</script>
<h2 class="pb-4">
Suspend/Approval
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/flow_approval">
If defined, at the end of the step, the flow will be suspended until it receives external
requests to be resumed or canceled. This is most useful to implement approval steps but can be
used flexibly for other purpose.
</Tooltip>
</h2>
<Section label="Suspend/Approval" class="w-full">
<svelte:fragment slot="header">
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/flow_approval">
If defined, at the end of the step, the flow will be suspended until it receives external
requests to be resumed or canceled. This is most useful to implement approval steps but can be
used flexibly for other purpose.
</Tooltip>
</svelte:fragment>
<Toggle
checked={isSuspendEnabled}
on:change={() => {
if (isSuspendEnabled && flowModule.suspend != undefined) {
flowModule.suspend = undefined
} else {
flowModule.suspend = {
required_events: 1,
timeout: 1800
<Toggle
checked={isSuspendEnabled}
on:change={() => {
if (isSuspendEnabled && flowModule.suspend != undefined) {
flowModule.suspend = undefined
} else {
flowModule.suspend = {
required_events: 1,
timeout: 1800
}
}
}
}}
options={{
right: 'Suspend flow execution until events/approvals received'
}}
/>
}}
options={{
right: 'Suspend flow execution until events/approvals received'
}}
/>
<div class="overflow-x-auto scrollbar-hidden">
<Tabs bind:selected={suspendTabSelected}>
<Tab size="xs" value="core" disabled={!isSuspendEnabled}>
<div class="flex gap-2 items-center my-1">Core</div>
</Tab>
<Tab size="xs" value="form" disabled={!isSuspendEnabled}>
<div class="flex gap-2 items-center my-1">Form</div>
</Tab>
<Tab size="xs" value="permissions" disabled={!isSuspendEnabled}>
<div class="flex gap-2 items-center my-1">Permissions</div>
</Tab>
</Tabs>
</div>
{#if suspendTabSelected === 'core'}
<div class="flex flex-col mt-4 gap-4">
<span class="text-xs font-bold">Number of approvals/events required for resuming flow</span>
{#if flowModule.suspend}
<input
bind:value={flowModule.suspend.required_events}
type="number"
min="1"
placeholder="1"
/>
{:else}
<input type="number" disabled />
{/if}
<span class="text-xs font-bold">Timeout</span>
{#if flowModule.suspend}
<SecondsInput bind:seconds={flowModule.suspend.timeout} />
{:else}
<SecondsInput disabled />
{/if}
<div class="overflow-x-auto scrollbar-hidden">
<Tabs bind:selected={suspendTabSelected}>
<Tab size="xs" value="core" disabled={!isSuspendEnabled}>
<div class="flex gap-2 items-center my-1">Core</div>
</Tab>
<Tab size="xs" value="form" disabled={!isSuspendEnabled}>
<div class="flex gap-2 items-center my-1">Form</div>
</Tab>
<Tab size="xs" value="permissions" disabled={!isSuspendEnabled}>
<div class="flex gap-2 items-center my-1">Permissions</div>
</Tab>
</Tabs>
</div>
{:else if suspendTabSelected === 'permissions'}
<div class="flex flex-col mt-4 gap-4">
{#if emptyString($enterpriseLicense)}
<Alert type="warning" title="Editing permissions is only available in enterprise version" />
{/if}
{#if flowModule.suspend}
<div class="flex flex-col gap-2">
{#if suspendTabSelected === 'core'}
<div class="flex flex-col mt-4 gap-4">
<Label label="Number of approvals/events required for resuming flow">
{#if flowModule.suspend}
<input
bind:value={flowModule.suspend.required_events}
type="number"
min="1"
placeholder="1"
/>
{:else}
<input type="number" disabled />
{/if}
</Label>
<Label label="Timeout">
{#if flowModule.suspend}
<SecondsInput bind:seconds={flowModule.suspend.timeout} />
{:else}
<SecondsInput disabled />
{/if}
</Label>
</div>
{:else if suspendTabSelected === 'permissions'}
<div class="flex flex-col mt-4 gap-4">
{#if emptyString($enterpriseLicense)}
<Alert type="warning" title="Editing permissions is only available in enterprise version" />
{/if}
{#if flowModule.suspend}
<div class="flex flex-col gap-2">
<Toggle
disabled={emptyString($enterpriseLicense)}
checked={Boolean(flowModule.suspend.user_auth_required)}
options={{
right: 'Require approvers to be logged in'
}}
on:change={(e) => {
if (flowModule.suspend) {
flowModule.suspend.user_auth_required = e.detail
if (e.detail && flowModule.suspend?.user_groups_required === undefined) {
flowModule.suspend.user_groups_required = {
type: 'static',
value: []
}
} else if (!e.detail) {
flowModule.suspend.user_groups_required = undefined
}
}
}}
/>
<div class="mb-4" />
<span class="text-xs font-bold"
>Require approvers to be members of one of the following user groups (leave empty for
any)
</span>
{#if allUserGroups.length !== 0 && flowModule.suspend && schema.properties['groups']}
<div class="border">
<PropPickerWrapper
{result}
displayContext={false}
pickableProperties={undefined}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
}}
>
<InputTransformForm
class="min-h-[256px] items-start"
bind:arg={flowModule.suspend.user_groups_required}
argName="groups"
{schema}
{previousModuleId}
/>
</PropPickerWrapper>
</div>
{/if}
</div>
{/if}
</div>
{:else}
<div class="flex flex-col mt-4 gap-4">
{#if flowModule.suspend}
<Toggle
disabled={emptyString($enterpriseLicense)}
checked={Boolean(flowModule.suspend.user_auth_required)}
checked={Boolean(flowModule.suspend.resume_form)}
options={{
right: 'Require approvers to be logged in'
right: 'Add a form to the approval page'
}}
on:change={(e) => {
if (flowModule.suspend) {
flowModule.suspend.user_auth_required = e.detail
if (e.detail && flowModule.suspend?.user_groups_required === undefined) {
flowModule.suspend.user_groups_required = {
type: 'static',
value: []
if (e.detail) {
flowModule.suspend.resume_form = {
schema: emptySchema()
}
} else if (!e.detail) {
flowModule.suspend.user_groups_required = undefined
} else {
flowModule.suspend.resume_form = undefined
}
}
}}
/>
<div class="mb-4" />
<span class="text-xs font-bold"
>Require approvers to be members of one of the following user groups (leave empty for any)
</span>
{#if allUserGroups.length !== 0 && flowModule.suspend && schema.properties['groups']}
<div class="border">
<PropPickerWrapper
{result}
displayContext={false}
pickableProperties={undefined}
on:select={({ detail }) => {
editor?.insertAtCursor(detail)
editor?.focus()
}}
>
<InputTransformForm
class="min-h-[256px] items-start"
bind:arg={flowModule.suspend.user_groups_required}
argName="groups"
{schema}
{previousModuleId}
/>
</PropPickerWrapper>
</div>
{/if}
</div>
{/if}
</div>
{:else}
<div class="flex flex-col mt-4 gap-4">
{#if flowModule.suspend}
<Toggle
checked={Boolean(flowModule.suspend.resume_form)}
options={{
right: 'Add a form to the approval page'
}}
on:change={(e) => {
if (flowModule.suspend) {
if (e.detail) {
flowModule.suspend.resume_form = {
schema: emptySchema()
}
} else {
flowModule.suspend.resume_form = undefined
}
}
}}
/>
<div>
<Slider size="xs" text="How to add dynamic default args">
As one of the return key of this step, return an object `default_args` that contains the
default arguments of the form arguments. e.g:
<pre
><code
>{`return {
<div>
<Slider size="xs" text="How to add dynamic default args">
As one of the return key of this step, return an object `default_args` that contains the
default arguments of the form arguments. e.g:
<pre
><code
>{`return {
endpoints,
default_args: {
foo: "foo",
bar: true,
},
}`}</code
></pre
>
</Slider></div
>
{/if}
{#if flowModule.suspend?.resume_form}
<SchemaEditor bind:schema={flowModule.suspend.resume_form.schema} />
{/if}
</div>
{/if}
></pre
>
</Slider></div
>
{/if}
{#if flowModule.suspend?.resume_form}
<SchemaEditor bind:schema={flowModule.suspend.resume_form.schema} />
{/if}
</div>
{/if}
</Section>
@@ -1,4 +1,5 @@
<script lang="ts">
import Section from '$lib/components/Section.svelte'
import Toggle from '$lib/components/Toggle.svelte'
import Tooltip from '$lib/components/Tooltip.svelte'
@@ -10,39 +11,40 @@
$: istimeoutEnabled = Boolean(flowModule.timeout)
</script>
<h2 class="pb-4">
Timeout
<Tooltip>
If defined, the custom timeout will be used instead of the instance timeout for the step. The
step's timeout cannot be greater than the instance timeout.
</Tooltip>
</h2>
<Section label="Timeout">
<svelte:fragment slot="header">
<Tooltip>
If defined, the custom timeout will be used instead of the instance timeout for the step. The
step's timeout cannot be greater than the instance timeout.
</Tooltip>
</svelte:fragment>
<Toggle
checked={istimeoutEnabled}
on:change={() => {
if (istimeoutEnabled && flowModule.timeout != undefined) {
flowModule.timeout = undefined
} else {
flowModule.timeout = 300
}
}}
options={{
right: 'Add a custom timeout for this step'
}}
/>
<div class="mb-4">
<span class="text-xs font-bold">Timeout duration</span>
<Toggle
checked={istimeoutEnabled}
on:change={() => {
if (istimeoutEnabled && flowModule.timeout != undefined) {
flowModule.timeout = undefined
} else {
flowModule.timeout = 300
}
}}
options={{
right: 'Add a custom timeout for this step'
}}
/>
<div class="mb-4">
<span class="text-xs font-bold">Timeout duration</span>
{#if flowModule.timeout}
<SecondsInput bind:seconds={flowModule.timeout} />
{:else}
<SecondsInput disabled />
{/if}
{#if flowModule.timeout}
<SecondsInput bind:seconds={flowModule.timeout} />
{:else}
<SecondsInput disabled />
{/if}
<div class="mt-4" />
<div class="mt-4" />
<Alert title="Only used when testing the full flow" type="info">
<p class="text-sm"> The timeout will be ignored when running "Test this step" </p>
</Alert>
</div>
<Alert title="Only used when testing the full flow" type="info">
<p class="text-sm"> The timeout will be ignored when running "Test this step" </p>
</Alert>
</div>
</Section>
@@ -3,6 +3,7 @@
import Tooltip from '$lib/components/Tooltip.svelte'
import type { FlowModule } from '$lib/gen'
import { SecondsInput } from '$lib/components/common'
import Section from '$lib/components/Section.svelte'
export let flowModule: FlowModule
@@ -32,123 +33,124 @@
</script>
<div class="h-full flex flex-col {$$props.class ?? ''}">
<h2>
Retries
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/retries">
If defined, upon error this step will be retried with a delay and a maximum number of attempts
as defined below.
</Tooltip>
</h2>
<Section label="Retries">
<svelte:fragment slot="header">
<Tooltip documentationLink="https://www.windmill.dev/docs/flows/retries">
If defined, upon error this step will be retried with a delay and a maximum number of
attempts as defined below.
</Tooltip>
</svelte:fragment>
<div class="flex h-[calc(100%-22px)]">
<div class="w-1/2 h-full overflow-auto pr-2">
<div class="pt-4">
<Toggle
checked={isConstantRetryEnabled}
on:change={() => {
if (isConstantRetryEnabled && flowModule.retry?.constant) {
flowModule.retry.constant = undefined
if (!isExponentialRetryEnabled) {
flowModule.retry = undefined
<div class="flex h-[calc(100%-22px)]">
<div class="w-1/2 h-full overflow-auto pr-2">
<div class="pt-4">
<Toggle
checked={isConstantRetryEnabled}
on:change={() => {
if (isConstantRetryEnabled && flowModule.retry?.constant) {
flowModule.retry.constant = undefined
if (!isExponentialRetryEnabled) {
flowModule.retry = undefined
}
} else {
setConstantRetries()
}
} else {
setConstantRetries()
}
}}
options={{
right: 'Constant retry enabled'
}}
/>
</div>
{#if flowModule.retry?.constant}
<div class="text-xs font-bold !mt-2">Attempts</div>
<input bind:value={flowModule.retry.constant.attempts} type="number" />
<div class="text-xs font-bold !mt-2">Delay</div>
<SecondsInput bind:seconds={flowModule.retry.constant.seconds} />
{:else}
<div class="text-xs font-bold !mt-2">Attempts</div>
<input type="number" disabled />
<div class="text-xs font-bold !mt-2">Delay</div>
<SecondsInput disabled />
{/if}
<div class="pt-6">
<Toggle
checked={isExponentialRetryEnabled}
on:change={() => {
if (isExponentialRetryEnabled && flowModule.retry?.exponential) {
flowModule.retry.exponential = undefined
if (!isConstantRetryEnabled) {
flowModule.retry = undefined
}
} else {
setExpoentialRetries()
}
}}
options={{
right: 'Exponential backoff enabled'
}}
/>
</div>
{#if flowModule.retry?.exponential}
<div class="text-xs font-bold !mt-2">Attempts</div>
<input bind:value={flowModule.retry.exponential.attempts} type="number" />
<div class="text-xs font-bold !mt-2">Mulitplier</div>
<span class="text-xs text-tertiary">delay = multiplier * base ^ (number of attempt)</span>
<input bind:value={flowModule.retry.exponential.multiplier} type="number" />
<div class="text-xs font-bold !mt-2">Base (in seconds)</div>
<input bind:value={flowModule.retry.exponential.seconds} type="number" step="1" />
{:else}
<div class="text-xs font-bold !mt-2">Attempts</div>
<input type="number" disabled />
<div class="text-xs font-bold !mt-2">Mulitplier</div>
<span class="text-xs text-tertiary">delay = multiplier * base ^ (number of attempt)</span>
<input type="number" disabled />
<div class="text-xs font-bold !mt-2">Base (in seconds)</div>
<input type="number" disabled />
{/if}
</div>
<div class="w-1/2 h-full overflow-auto pl-2">
{#if true}
{@const { attempts: cAttempts, seconds: cSeconds } = flowModule.retry?.constant || {}}
{@const {
attempts: eAttempts,
seconds: eSeconds,
multiplier
} = flowModule.retry?.exponential || {}}
{@const cArray = Array.from({ length: cAttempts || 0 }, () => cSeconds)}
{@const eArray = Array.from(
{ length: eAttempts || 0 },
(_, i) => (multiplier || 0) * (eSeconds || 0) ** (i + cArray.length + 1)
)}
{@const array = [...cArray, ...eArray]}
<div class="bg-surface-secondary border rounded px-4 py-2">
<div class="text-xs font-medium mb-2">Retry attempts</div>
{#if array.length > 0}
<table class="text-xs">
<tr>
<td class="font-semibold pr-1 pb-1">1:</td>
<td class="pb-1">After {array[0]} second{array[0] === 1 ? '' : 's'}</td>
</tr>
{#each array.slice(1) as delay, i}
{@const index = i + 2}
<tr>
<td class="font-semibold pr-1 align-top">{index}:</td>
<td class="pb-1 whitespace-nowrap">
{delay} second{delay === 1 ? '' : 's'} after attempt #{index - 1}
{#if i > cArray.length - 2}
<span class="text-gray-400 pl-2">
({multiplier} * {eSeconds}<sup>{index}</sup>)
</span>
{/if}
</td>
</tr>
{/each}
</table>
{:else}
<div class="text-xs">No retries</div>
{/if}
}}
options={{
right: 'Constant retry enabled'
}}
/>
</div>
{/if}
{#if flowModule.retry?.constant}
<div class="text-xs font-bold !mt-2">Attempts</div>
<input bind:value={flowModule.retry.constant.attempts} type="number" />
<div class="text-xs font-bold !mt-2">Delay</div>
<SecondsInput bind:seconds={flowModule.retry.constant.seconds} />
{:else}
<div class="text-xs font-bold !mt-2">Attempts</div>
<input type="number" disabled />
<div class="text-xs font-bold !mt-2">Delay</div>
<SecondsInput disabled />
{/if}
<div class="pt-6">
<Toggle
checked={isExponentialRetryEnabled}
on:change={() => {
if (isExponentialRetryEnabled && flowModule.retry?.exponential) {
flowModule.retry.exponential = undefined
if (!isConstantRetryEnabled) {
flowModule.retry = undefined
}
} else {
setExpoentialRetries()
}
}}
options={{
right: 'Exponential backoff enabled'
}}
/>
</div>
{#if flowModule.retry?.exponential}
<div class="text-xs font-bold !mt-2">Attempts</div>
<input bind:value={flowModule.retry.exponential.attempts} type="number" />
<div class="text-xs font-bold !mt-2">Mulitplier</div>
<span class="text-xs text-tertiary">delay = multiplier * base ^ (number of attempt)</span>
<input bind:value={flowModule.retry.exponential.multiplier} type="number" />
<div class="text-xs font-bold !mt-2">Base (in seconds)</div>
<input bind:value={flowModule.retry.exponential.seconds} type="number" step="1" />
{:else}
<div class="text-xs font-bold !mt-2">Attempts</div>
<input type="number" disabled />
<div class="text-xs font-bold !mt-2">Mulitplier</div>
<span class="text-xs text-tertiary">delay = multiplier * base ^ (number of attempt)</span>
<input type="number" disabled />
<div class="text-xs font-bold !mt-2">Base (in seconds)</div>
<input type="number" disabled />
{/if}
</div>
<div class="w-1/2 h-full overflow-auto pl-2">
{#if true}
{@const { attempts: cAttempts, seconds: cSeconds } = flowModule.retry?.constant || {}}
{@const {
attempts: eAttempts,
seconds: eSeconds,
multiplier
} = flowModule.retry?.exponential || {}}
{@const cArray = Array.from({ length: cAttempts || 0 }, () => cSeconds)}
{@const eArray = Array.from(
{ length: eAttempts || 0 },
(_, i) => (multiplier || 0) * (eSeconds || 0) ** (i + cArray.length + 1)
)}
{@const array = [...cArray, ...eArray]}
<div class="bg-surface-secondary border rounded px-4 py-2">
<div class="text-xs font-medium mb-2">Retry attempts</div>
{#if array.length > 0}
<table class="text-xs">
<tr>
<td class="font-semibold pr-1 pb-1">1:</td>
<td class="pb-1">After {array[0]} second{array[0] === 1 ? '' : 's'}</td>
</tr>
{#each array.slice(1) as delay, i}
{@const index = i + 2}
<tr>
<td class="font-semibold pr-1 align-top">{index}:</td>
<td class="pb-1 whitespace-nowrap">
{delay} second{delay === 1 ? '' : 's'} after attempt #{index - 1}
{#if i > cArray.length - 2}
<span class="text-gray-400 pl-2">
({multiplier} * {eSeconds}<sup>{index}</sup>)
</span>
{/if}
</td>
</tr>
{/each}
</table>
{:else}
<div class="text-xs">No retries</div>
{/if}
</div>
{/if}
</div>
</div>
</div>
</Section>
</div>