mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-30 08:16:49 +00:00
fix(frontend): Support of suspend & stop expression + restore import/export menu (#580)
* feature(frontend): Various UI fix + restore removed features * feature(frontend): WIP Prop picker panel * feature(frontend): Advanced flow module tab * feature(frontend): Add import/export flow menu * feature(frontend): Remove summary edition + add a enable/disable button for suspend
This commit is contained in:
@@ -226,3 +226,8 @@ html {
|
||||
padding: 0;
|
||||
@apply text-xs;
|
||||
}
|
||||
|
||||
/* svelte-split-pane separator class override */
|
||||
.separator {
|
||||
background-color: #ddd !important;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
import FlowEditor from './flows/FlowEditor.svelte'
|
||||
import { flowStateStore, flowStateToFlow, type FlowState } from './flows/flowState'
|
||||
import { flowStore } from './flows/flowStore'
|
||||
import FlowImportExportMenu from './flows/header/FlowImportExportMenu.svelte'
|
||||
import { loadFlowSchedule, type Schedule } from './flows/scheduleUtils'
|
||||
import type { FlowEditorContext } from './flows/types'
|
||||
import { cleanInputs } from './flows/utils'
|
||||
@@ -193,7 +194,7 @@
|
||||
</button>
|
||||
</BreadcrumbItem>
|
||||
</Breadcrumb>
|
||||
<div class="flex flex-row-reverse ml-2">
|
||||
<div class="flex flex-row-reverse ml-2 space-x-reverse space-x-2">
|
||||
{#if step == 1}
|
||||
<button
|
||||
disabled={pathError != ''}
|
||||
@@ -204,15 +205,16 @@
|
||||
</button>
|
||||
<button
|
||||
disabled={pathError != ''}
|
||||
class="default-button-secondary px-6 max-h-8 mr-2"
|
||||
class="default-button-secondary px-6 max-h-8"
|
||||
on:click={saveFlow}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
<FlowImportExportMenu />
|
||||
{:else}
|
||||
<button class="default-button px-6 self-end" on:click={saveFlow}>Save</button>
|
||||
<button
|
||||
class="default-button-secondary px-6 max-h-8 mr-2"
|
||||
class="default-button-secondary px-6 max-h-8"
|
||||
on:click={async () => {
|
||||
changeStep(1)
|
||||
}}
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
export let placement = 'right'
|
||||
export let size = '600px'
|
||||
|
||||
export function toggleDrawer() {
|
||||
open = !open
|
||||
}
|
||||
|
||||
let mounted = false
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
import FlowModuleHeader from './FlowModuleHeader.svelte'
|
||||
import { flowStateStore, type FlowModuleSchema } from '../flowState'
|
||||
import { scriptLangToEditorLang } from '$lib/utils'
|
||||
import { getContext } from 'svelte'
|
||||
import type { FlowEditorContext } from '../types'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import FlowModuleAdvancedSettings from './FlowModuleAdvancedSettings.svelte'
|
||||
|
||||
export let indexes: string
|
||||
export let flowModule: FlowModule
|
||||
@@ -34,7 +38,7 @@
|
||||
export let schema: Schema
|
||||
export let childFlowModules: FlowModuleSchema[] | undefined = undefined
|
||||
|
||||
const [i] = indexes.split('-').map(Number)
|
||||
const [parentIndex] = indexes.split('-').map(Number)
|
||||
|
||||
let editor: Editor
|
||||
let websocketAlive = { pyright: false, black: false, deno: false }
|
||||
@@ -65,6 +69,8 @@
|
||||
async function applyCreateLoop() {
|
||||
await apply(createLoop, null)
|
||||
}
|
||||
|
||||
const { selectedId, select } = getContext<FlowEditorContext>('FlowEditorContext')
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col h-full ">
|
||||
@@ -87,9 +93,13 @@
|
||||
</svelte:fragment>
|
||||
{#if shouldPick}
|
||||
<FlowInputs
|
||||
shouldDisableTriggerScripts={i != 0}
|
||||
shouldDisableLoopCreation={indexes.length > 1 || i == 0}
|
||||
on:loop={() => applyCreateLoop()}
|
||||
shouldDisableTriggerScripts={parentIndex != 0}
|
||||
shouldDisableLoopCreation={indexes.length > 1 || parentIndex == 0}
|
||||
on:loop={() => {
|
||||
applyCreateLoop()
|
||||
|
||||
select(['loop', $selectedId].join('-'))
|
||||
}}
|
||||
on:pick={(e) => apply(pickScript, e.detail.path)}
|
||||
on:new={(e) =>
|
||||
apply(createInlineScriptModule, {
|
||||
@@ -131,6 +141,7 @@
|
||||
<Tabs selected="inputs">
|
||||
<Tab value="inputs">Inputs</Tab>
|
||||
<Tab value="preview">Test</Tab>
|
||||
<Tab value="advanced">Advanced</Tab>
|
||||
|
||||
<svelte:fragment slot="content">
|
||||
<div class="h-full pb-16 overflow-y-scroll bg-white">
|
||||
@@ -161,6 +172,10 @@
|
||||
/>
|
||||
{/if}
|
||||
</TabContent>
|
||||
|
||||
<TabContent value="advanced">
|
||||
<FlowModuleAdvancedSettings bind:flowModule />
|
||||
</TabContent>
|
||||
</div>
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
<script lang="ts">
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
|
||||
import type { FlowModule } from '$lib/gen'
|
||||
|
||||
export let flowModule: FlowModule
|
||||
</script>
|
||||
|
||||
<div class="space-y-4 ">
|
||||
{#if flowModule.suspend}
|
||||
<button
|
||||
class="flex items-center text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-1 focus:outline-none "
|
||||
on:click={() => {
|
||||
flowModule.suspend = undefined
|
||||
}}
|
||||
>
|
||||
Disable suspend
|
||||
</button>
|
||||
<div class="font-bold text-sm">Suspend</div>
|
||||
|
||||
<input bind:value={flowModule.suspend} type="number" min="1" placeholder="0" />
|
||||
{:else}
|
||||
<button
|
||||
class="flex items-center text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-1 focus:outline-none "
|
||||
on:click={() => {
|
||||
flowModule.suspend = 1
|
||||
}}
|
||||
>
|
||||
Enable suspend
|
||||
</button>
|
||||
{/if}
|
||||
{#if flowModule.stop_after_if}
|
||||
<button
|
||||
class="flex items-center text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-1 focus:outline-none "
|
||||
on:click={() => {
|
||||
flowModule.stop_after_if = undefined
|
||||
}}
|
||||
>
|
||||
Disable stop expression
|
||||
</button>
|
||||
<div class="flex flex-col">
|
||||
<div class="font-bold text-sm mb-2">Stop condition expression</div>
|
||||
|
||||
<SimpleEditor
|
||||
lang="javascript"
|
||||
bind:code={flowModule.stop_after_if.expr}
|
||||
class="few-lines-editor border"
|
||||
/>
|
||||
<Toggle
|
||||
bind:checked={flowModule.stop_after_if.skip_if_stopped}
|
||||
options={{
|
||||
right: 'Should skip if stopped'
|
||||
}}
|
||||
on:change
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<button
|
||||
class="flex items-center text-white bg-blue-500 hover:bg-blue-700 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-1 focus:outline-none "
|
||||
on:click={() => {
|
||||
flowModule.stop_after_if = {
|
||||
skip_if_stopped: false,
|
||||
expr: 'result == undefined'
|
||||
}
|
||||
}}
|
||||
>
|
||||
Enable stop expression
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -22,7 +22,8 @@ import {
|
||||
import { get } from 'svelte/store'
|
||||
import { flowStateStore, type FlowModuleSchema, type FlowState } from './flowState'
|
||||
import { flowStore } from './flowStore'
|
||||
import { jobsToResults, loadSchemaFromModule } from './utils'
|
||||
import { loadSchemaFromModule } from './utils'
|
||||
|
||||
export function emptyFlowModuleSchema(): FlowModuleSchema {
|
||||
return {
|
||||
flowModule: emptyModule(),
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
<script lang="ts">
|
||||
import Drawer from '$lib/components/common/drawer/Drawer.svelte'
|
||||
import DrawerContent from '$lib/components/common/drawer/DrawerContent.svelte'
|
||||
|
||||
import Menu from '$lib/components/common/menu/Menu.svelte'
|
||||
import MenuItem from '$lib/components/common/menu/MenuItem.svelte'
|
||||
import FlowViewer from '$lib/components/FlowViewer.svelte'
|
||||
import SimpleEditor from '$lib/components/SimpleEditor.svelte'
|
||||
import { sendUserToast } from '$lib/utils'
|
||||
import { faCloudscale } from '@fortawesome/free-brands-svg-icons'
|
||||
import {
|
||||
faCloud,
|
||||
faCloudUpload,
|
||||
faFileExport,
|
||||
faFileImport,
|
||||
faGlobe,
|
||||
faUpload
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import Icon from 'svelte-awesome'
|
||||
import { flowStore, initFlow } from '../flowStore'
|
||||
import { cleanInputs } from '../utils'
|
||||
|
||||
let jsonSetterDrawer: Drawer
|
||||
let jsonViewerDrawer: Drawer
|
||||
let pendingJson: string
|
||||
|
||||
function importJson() {
|
||||
Object.assign($flowStore, JSON.parse(pendingJson))
|
||||
|
||||
initFlow($flowStore)
|
||||
sendUserToast('OpenFlow imported from JSON')
|
||||
jsonSetterDrawer.toggleDrawer()
|
||||
}
|
||||
</script>
|
||||
|
||||
<Menu placement="bottom-end">
|
||||
<button
|
||||
slot="trigger"
|
||||
class="flex items-center text-gray-900 h-8 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-200 font-medium rounded-lg text-sm px-4 py-2"
|
||||
>
|
||||
Import/Export flow
|
||||
</button>
|
||||
<div class="divide-y divide-gray-200">
|
||||
<MenuItem on:click={() => jsonSetterDrawer.toggleDrawer()}>
|
||||
<Icon data={faFileImport} scale={0.6} class="inline mr-2" />
|
||||
Import from a JSON OpenFlow
|
||||
</MenuItem>
|
||||
<MenuItem on:click={() => jsonViewerDrawer.toggleDrawer()}>
|
||||
<Icon data={faFileExport} scale={0.6} class="inline mr-2" />
|
||||
Export to a JSON OpenFlow
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
const url = new URL('https://hub.windmill.dev/flows/add')
|
||||
const openFlow = {
|
||||
value: $flowStore.value,
|
||||
summary: $flowStore.summary,
|
||||
description: $flowStore.description,
|
||||
schema: $flowStore.schema
|
||||
}
|
||||
url.searchParams.append('flow', btoa(JSON.stringify(openFlow)))
|
||||
window.open(url, '_blank')?.focus()
|
||||
}}
|
||||
>
|
||||
<Icon data={faGlobe} scale={0.6} class="inline mr-2" />
|
||||
Publish to Hub
|
||||
</MenuItem>
|
||||
</div>
|
||||
</Menu>
|
||||
|
||||
<Drawer bind:this={jsonSetterDrawer} size="800px">
|
||||
<DrawerContent title="Import JSON" on:close={() => jsonSetterDrawer.toggleDrawer()}>
|
||||
<button class="default-button px-4 py-2 font-semibold" on:click={() => importJson()}>
|
||||
Import
|
||||
</button>
|
||||
<SimpleEditor bind:code={pendingJson} lang="json" class="h-full" />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
<Drawer bind:this={jsonViewerDrawer} size="800px">
|
||||
<DrawerContent title="See JSON" on:close={() => jsonViewerDrawer.toggleDrawer()}>
|
||||
<FlowViewer flow={cleanInputs($flowStore)} tab="json" />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
Reference in New Issue
Block a user