Skip to content

Commit

Permalink
feat(frontend): add indicator when required field are missing (#3935)
Browse files Browse the repository at this point in the history
* feat(frontend): add indicator when required field are missing

* feat(frontend): add indicator when required field are missing

* feat(frontend): remove reactivity

* feat(frontend): remove reactivity

* feat(frontend): remove reactivity

* feat(frontend): remove reactivity

* feat(frontend): remove reactivity

* feat(frontend): handle init flow

* feat(frontend): done

* feat(frontend): done

* feat(frontend): simplify code

* feat(frontend): improve code

* feat(frontend): improve ArgInput

* feat(frontend): fix icon alignement
  • Loading branch information
fatonramadani committed Jun 20, 2024
1 parent da999d0 commit 7007f14
Show file tree
Hide file tree
Showing 16 changed files with 232 additions and 70 deletions.
16 changes: 15 additions & 1 deletion frontend/src/lib/components/ArgInput.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import type { EnumType, SchemaProperty } from '$lib/common'
import { setInputCat as computeInputCat, emptyString } from '$lib/utils'
import { setInputCat as computeInputCat, debounce, emptyString } from '$lib/utils'
import { DollarSign, Pipette, Plus, X } from 'lucide-svelte'
import { createEventDispatcher, tick } from 'svelte'
import Multiselect from 'svelte-multiselect'
Expand Down Expand Up @@ -29,6 +29,7 @@
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
import SchemaFormDnd from './schema/SchemaFormDND.svelte'
import SchemaForm from './SchemaForm.svelte'
import { deepEqual } from 'fast-equals'
export let label: string = ''
export let value: any
Expand Down Expand Up @@ -77,6 +78,7 @@
export let order: string[] | undefined = undefined
export let editor: SimpleEditor | undefined = undefined
export let orderEditable = false
export let shouldDispatchChanges: boolean = false
let oneOfSelected: string | undefined = undefined
async function updateOneOfSelected(oneOf: SchemaProperty[] | undefined) {
Expand Down Expand Up @@ -233,6 +235,18 @@
let itemsLimit = 50
$: validateInput(pattern, value, required)
let oldValue = value
function compareValues(value) {
if (!deepEqual(oldValue, value)) {
oldValue = value
dispatch('change')
}
}
let debounced = debounce(() => compareValues(value), 50)
$: shouldDispatchChanges && debounced(value)
</script>

<S3FilePicker
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/lib/components/Dev.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import { writable } from 'svelte/store'
import type { FlowState } from './flows/flowState'
import { initHistory } from '$lib/history'
import type { FlowEditorContext } from './flows/types'
import type { FlowEditorContext, FlowInput } from './flows/types'
import { dfs } from './flows/dfs'
import { loadSchemaFromModule } from './flows/flowInfers'
import { CornerDownLeft, Play } from 'lucide-svelte'
Expand Down Expand Up @@ -439,7 +439,8 @@
flowStore,
testStepStore,
saveDraft: () => {},
initialPath: ''
initialPath: '',
flowInputsStore: writable<FlowInput | undefined>(undefined)
})
$: updateFlow($flowStore)
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/lib/components/FlowBuilder.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
import FlowImportExportMenu from './flows/header/FlowImportExportMenu.svelte'
import FlowPreviewButtons from './flows/header/FlowPreviewButtons.svelte'
import { loadFlowSchedule, type Schedule } from './flows/scheduleUtils'
import type { FlowEditorContext } from './flows/types'
import type { FlowEditorContext, FlowInput } from './flows/types'
import { cleanInputs, emptyFlowModuleState } from './flows/utils'
import { Calendar, Pen, Save, DiffIcon } from 'lucide-svelte'
import { createEventDispatcher } from 'svelte'
Expand Down Expand Up @@ -377,7 +377,8 @@
pathStore,
testStepStore,
saveDraft,
initialPath
initialPath,
flowInputsStore: writable<FlowInput | undefined>({})
})
async function loadSchedule() {
Expand Down
17 changes: 16 additions & 1 deletion frontend/src/lib/components/InputTransformForm.svelte
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts">
import type { Schema } from '$lib/common'
import type { InputCat } from '$lib/utils'
import { getContext } from 'svelte'
import { createEventDispatcher, getContext } from 'svelte'
import ArgInput from './ArgInput.svelte'
import FieldHeader from './FieldHeader.svelte'
Expand Down Expand Up @@ -42,6 +42,8 @@
let monacoTemplate: TemplateEditor | undefined = undefined
let argInput: ArgInput | undefined = undefined
const dispatch = createEventDispatcher()
$: inputCat = computeInputCat(
schema.properties[argName].type,
schema.properties[argName].format,
Expand Down Expand Up @@ -372,6 +374,9 @@
}}
bind:code={arg.value}
fontSize={14}
on:change={() => {
dispatch('change', { argName })
}}
/>
{/if}
</div>
Expand All @@ -385,6 +390,10 @@
on:blur={() => {
focused = false
}}
shouldDispatchChanges
on:change={() => {
dispatch('change', { argName })
}}
label={argName}
bind:editor={monaco}
bind:description={schema.properties[argName].description}
Expand Down Expand Up @@ -416,6 +425,9 @@
<SimpleEditor
bind:this={monaco}
bind:code={arg.expr}
on:change={() => {
dispatch('change', { argName })
}}
{extraLib}
lang="javascript"
shouldBindKey={false}
Expand All @@ -426,6 +438,9 @@
return false
})
}}
on:change={() => {
dispatch('change', { argName })
}}
on:blur={() => {
focused = false
}}
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/lib/components/InputTransformSchemaForm.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { VariableService, type InputTransform } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import { allTrue } from '$lib/utils'
import { createEventDispatcher } from 'svelte'
import { Button } from './common'
import StepInputsGen from './copilot/StepInputsGen.svelte'
import type { PickableProperties } from './flows/previousResults'
Expand All @@ -27,6 +28,9 @@
export { clazz as class }
let inputCheck: { [id: string]: boolean } = {}
const dispatch = createEventDispatcher()
$: isValid = allTrue(inputCheck) ?? false
$: if (args == undefined || typeof args !== 'object') {
Expand Down Expand Up @@ -94,6 +98,10 @@
{noDynamicToggle}
{pickableProperties}
{enableAi}
on:change={(e) => {
const { argName } = e.detail
dispatch('changeArg', { argName })
}}
/>
</div>
{/if}
Expand Down
25 changes: 23 additions & 2 deletions frontend/src/lib/components/flows/content/FlowEditorPanel.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script lang="ts">
import { getContext } from 'svelte'
import { getContext, onMount } from 'svelte'
import type { FlowEditorContext } from '../types'
import FlowModuleWrapper from './FlowModuleWrapper.svelte'
Expand All @@ -8,11 +8,13 @@
import FlowFailureModule from './FlowFailureModule.svelte'
import FlowConstants from './FlowConstants.svelte'
import type { FlowModule } from '$lib/gen'
import { initRequiredInputFilled } from '../utils'
export let noEditor = false
export let enableAi = false
const { selectedId, flowStore } = getContext<FlowEditorContext>('FlowEditorContext')
const { selectedId, flowStore, flowStateStore, flowInputsStore } =
getContext<FlowEditorContext>('FlowEditorContext')
function checkDup(modules: FlowModule[]): string | undefined {
let seenModules: string[] = []
Expand All @@ -24,6 +26,25 @@
seenModules.push(m.id)
}
}
onMount(() => {
$flowStore?.value?.modules?.forEach((module) => {
if (!module) {
return
}
if (!$flowStateStore) {
$flowInputsStore = {}
}
$flowInputsStore![module?.id] = {
requiredInputsFilled: initRequiredInputFilled(
module.value,
$flowStateStore?.[module?.id]?.schema ?? {}
)
}
})
})
</script>

{#if $selectedId?.startsWith('settings')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,17 @@
import { enterpriseLicense } from '$lib/stores'
import { isCloudHosted } from '$lib/cloud'
import { loadSchemaFromModule } from '../flowInfers'
import { initRequiredInputFilled, setRequiredInputFilled } from '../utils'
const { selectedId, previewArgs, flowStateStore, flowStore, pathStore, saveDraft } =
getContext<FlowEditorContext>('FlowEditorContext')
const {
selectedId,
previewArgs,
flowStateStore,
flowStore,
pathStore,
saveDraft,
flowInputsStore
} = getContext<FlowEditorContext>('FlowEditorContext')
export let flowModule: FlowModule
export let failureModule: boolean = false
Expand Down Expand Up @@ -132,8 +140,14 @@
try {
const { input_transforms, schema } = await loadSchemaFromModule(flowModule)
validCode = true
if (inputTransformSchemaForm) {
inputTransformSchemaForm.setArgs(input_transforms)
if (!deepEqual(schema, $flowStateStore[flowModule.id]?.schema)) {
$flowInputsStore![flowModule?.id] = {
requiredInputsFilled: initRequiredInputFilled(flowModule.value, schema ?? {})
}
}
} else {
if (
flowModule.value.type == 'rawscript' ||
Expand Down Expand Up @@ -175,9 +189,25 @@
})
let forceReload = 0
let editorPanelSize = noEditor ? 0 : flowModule.value.type == 'script' ? 30 : 50
let editorSettingsPanelSize = 100 - editorPanelSize
function setFlowInput(argName: string) {
if ($flowInputsStore && $flowInputsStore?.[flowModule.id] === undefined) {
$flowInputsStore[flowModule.id] = {}
}
if ($flowInputsStore) {
const requiredInputsFilled = setRequiredInputFilled(
argName,
flowModule.value,
$flowInputsStore[flowModule.id].requiredInputsFilled ?? {},
$flowStateStore[$selectedId]?.schema
)
$flowInputsStore[flowModule.id].requiredInputsFilled = requiredInputsFilled
}
}
</script>

<svelte:window on:keydown={onKeyDown} />
Expand Down Expand Up @@ -342,6 +372,10 @@
bind:args={flowModule.value.input_transforms}
extraLib={stepPropPicker.extraLib}
{enableAi}
on:changeArg={(e) => {
const { argName } = e.detail
setFlowInput(argName)
}}
/>
</PropPickerWrapper>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
import FlowBranchesAllWrapper from './FlowBranchesAllWrapper.svelte'
import FlowBranchesOneWrapper from './FlowBranchesOneWrapper.svelte'
import FlowWhileLoop from './FlowWhileLoop.svelte'
import { initRequiredInputFilled } from '../utils'
export let flowModule: FlowModule
export let noEditor: boolean = false
export let enableAi = false
const { selectedId, schedule, flowStateStore } =
const { selectedId, schedule, flowStateStore, flowInputsStore } =
getContext<FlowEditorContext>('FlowEditorContext')
let scriptKind: 'script' | 'trigger' | 'approval' = 'script'
Expand Down Expand Up @@ -134,6 +135,15 @@

flowModule = module
$flowStateStore[module.id] = state

if ($flowInputsStore) {
$flowInputsStore[module.id] = {
requiredInputsFilled: initRequiredInputFilled(
module.value,
$flowStateStore[module.id].schema
)
}
}
}}
failureModule={$selectedId === 'failure'}
/>
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/lib/components/flows/flowStateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ export async function loadFlowModuleState(flowModule: FlowModule): Promise<FlowM
) {
flowModule.value.input_transforms = input_transforms
}
return { schema, previewResult: NEVER_TESTED_THIS_FAR }

return {
schema,
previewResult: NEVER_TESTED_THIS_FAR
}
} catch (e) {
console.debug(e)
return emptyFlowModuleState()
Expand Down
20 changes: 20 additions & 0 deletions frontend/src/lib/components/flows/map/FlowModuleSchemaItem.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import Popover from '$lib/components/Popover.svelte'
import { classNames } from '$lib/utils'
import {
AlertTriangle,
Bed,
Database,
Gauge,
Expand All @@ -16,6 +17,8 @@
} from 'lucide-svelte'
import { createEventDispatcher, getContext } from 'svelte'
import { fade } from 'svelte/transition'
import type { FlowInput } from '../types'
import type { Writable } from 'svelte/store'
export let selected: boolean = false
export let deletable: boolean = false
Expand All @@ -33,6 +36,9 @@
export let concurrency: boolean = false
export let retries: number | undefined = undefined
const { flowInputsStore } = getContext<{ flowInputsStore: Writable<FlowInput | undefined> }>(
'FlowGraphContext'
)
const dispatch = createEventDispatcher()
const { currentStepStore: copilotCurrentStepStore } =
Expand Down Expand Up @@ -132,6 +138,7 @@
</Popover>
{/if}
</div>

<div
class="flex gap-1 justify-between items-center w-full overflow-hidden rounded-sm
border border-gray-400 p-2 text-2xs module text-primary"
Expand Down Expand Up @@ -166,6 +173,19 @@ hover:border-blue-700 {selected ? '' : '!hidden'}"
>
<Move class="mx-[3px]" size={14} strokeWidth={2} />
</button>

{#if id && !Object.values($flowInputsStore?.[id]?.requiredInputsFilled || {}).every(Boolean)}
<div class="absolute -top-[10px] -left-[10px]">
<Popover>
<svelte:fragment slot="text">At least one required input is not set.</svelte:fragment>
<div
class="flex items-center justify-center h-full w-full rounded-md p-0.5 border border-yellow-600 text-yellow-600 bg-yellow-100 duration-150 hover:bg-yellow-300"
>
<AlertTriangle size={14} strokeWidth={2} />
</div>
</Popover>
</div>
{/if}
{/if}
</div>

Expand Down
Loading

0 comments on commit 7007f14

Please sign in to comment.