diff --git a/web/src/lib/components/workflows/SchemaFormFields.svelte b/web/src/lib/components/workflows/SchemaFormFields.svelte index e5b1ab951e..8854b70904 100644 --- a/web/src/lib/components/workflows/SchemaFormFields.svelte +++ b/web/src/lib/components/workflows/SchemaFormFields.svelte @@ -1,6 +1,6 @@ @@ -123,6 +81,8 @@ {@const options = component.options?.map((opt) => { return { label: opt.label, value: String(opt.value) }; }) || [{ label: 'N/A', value: '' }]} + {@const currentValue = actualConfig[key]} + {@const selectedItem = options.find((opt) => opt.value === String(currentValue)) ?? options[0]} - updateConfig(key, opt.value)} value={selectedItem} /> {/if} @@ -147,6 +107,8 @@ {@const options = component.options?.map((opt) => { return { label: opt.label, value: String(opt.value) }; }) || [{ label: 'N/A', value: '' }]} + {@const currentValues = (actualConfig[key] as string[]) ?? []} + {@const selectedItems = options.filter((opt) => currentValues.includes(opt.value))} o.value), )} - bind:values={multiSelectValue} + values={selectedItems} /> {/if} {:else if component.type === 'switch'} + {@const checked = Boolean(actualConfig[key])} - updateConfig(key, check)} /> + updateConfig(key, check)} /> diff --git a/web/src/lib/services/workflow.service.ts b/web/src/lib/services/workflow.service.ts index 4a2739926a..b4186b7ebf 100644 --- a/web/src/lib/services/workflow.service.ts +++ b/web/src/lib/services/workflow.service.ts @@ -58,20 +58,69 @@ export const getActionsByContext = ( }; /** - * Initialize filter configurations from existing workflow + * Remap configs when items are reordered (drag-drop) + * Moves config from old index to new index position */ -export const initializeFilterConfigs = ( - workflow: WorkflowResponseDto, - availableFilters: PluginFilterResponseDto[], +export const remapConfigsOnReorder = ( + configs: Record, + prefix: 'filter' | 'action', + fromIndex: number, + toIndex: number, + totalCount: number, ): Record => { + const newConfigs: Record = {}; + + // Create an array of configs in order + const configArray: unknown[] = []; + for (let i = 0; i < totalCount; i++) { + configArray.push(configs[`${prefix}_${i}`] ?? {}); + } + + // Move the item from fromIndex to toIndex + const [movedItem] = configArray.splice(fromIndex, 1); + configArray.splice(toIndex, 0, movedItem); + + // Rebuild the configs object with new indices + for (let i = 0; i < configArray.length; i++) { + newConfigs[`${prefix}_${i}`] = configArray[i]; + } + + return newConfigs; +}; + +/** + * Remap configs when an item is removed + * Shifts all configs after the removed index down by one + */ +export const remapConfigsOnRemove = ( + configs: Record, + prefix: 'filter' | 'action', + removedIndex: number, + totalCount: number, +): Record => { + const newConfigs: Record = {}; + + let newIndex = 0; + for (let i = 0; i < totalCount; i++) { + if (i !== removedIndex) { + newConfigs[`${prefix}_${newIndex}`] = configs[`${prefix}_${i}`] ?? {}; + newIndex++; + } + } + + return newConfigs; +}; + +/** + * Initialize filter configurations from existing workflow + * Uses index-based keys to support multiple filters of the same type + */ +export const initializeFilterConfigs = (workflow: WorkflowResponseDto): Record => { const configs: Record = {}; if (workflow.filters) { - for (const workflowFilter of workflow.filters) { - const filterDef = availableFilters.find((f) => f.id === workflowFilter.pluginFilterId); - if (filterDef) { - configs[filterDef.methodName] = workflowFilter.filterConfig ?? {}; - } + for (const [index, workflowFilter] of workflow.filters.entries()) { + configs[`filter_${index}`] = workflowFilter.filterConfig ?? {}; } } @@ -80,19 +129,14 @@ export const initializeFilterConfigs = ( /** * Initialize action configurations from existing workflow + * Uses index-based keys to support multiple actions of the same type */ -export const initializeActionConfigs = ( - workflow: WorkflowResponseDto, - availableActions: PluginActionResponseDto[], -): Record => { +export const initializeActionConfigs = (workflow: WorkflowResponseDto): Record => { const configs: Record = {}; if (workflow.actions) { - for (const workflowAction of workflow.actions) { - const actionDef = availableActions.find((a) => a.id === workflowAction.pluginActionId); - if (actionDef) { - configs[actionDef.methodName] = workflowAction.actionConfig ?? {}; - } + for (const [index, workflowAction] of workflow.actions.entries()) { + configs[`action_${index}`] = workflowAction.actionConfig ?? {}; } } @@ -101,6 +145,7 @@ export const initializeActionConfigs = ( /** * Build workflow payload from current state + * Uses index-based keys to support multiple filters/actions of the same type */ export const buildWorkflowPayload = ( name: string, @@ -112,12 +157,12 @@ export const buildWorkflowPayload = ( filterConfigs: Record, actionConfigs: Record, ): WorkflowPayload => { - const filters = orderedFilters.map((filter) => ({ - [filter.methodName]: filterConfigs[filter.methodName] ?? {}, + const filters = orderedFilters.map((filter, index) => ({ + [filter.methodName]: filterConfigs[`filter_${index}`] ?? {}, })); - const actions = orderedActions.map((action) => ({ - [action.methodName]: actionConfigs[action.methodName] ?? {}, + const actions = orderedActions.map((action, index) => ({ + [action.methodName]: actionConfigs[`action_${index}`] ?? {}, })); return { @@ -158,30 +203,30 @@ export const parseWorkflowJson = ( // Find trigger const trigger = availableTriggers.find((t) => t.type === parsed.triggerType); - // Parse filters + // Parse filters (using index-based keys to support multiple of same type) const filters: PluginFilterResponseDto[] = []; const filterConfigs: Record = {}; if (Array.isArray(parsed.filters)) { - for (const filterObj of parsed.filters) { + for (const [index, filterObj] of parsed.filters.entries()) { const methodName = Object.keys(filterObj)[0]; const filter = availableFilters.find((f) => f.methodName === methodName); if (filter) { filters.push(filter); - filterConfigs[methodName] = (filterObj as Record)[methodName]; + filterConfigs[`filter_${index}`] = (filterObj as Record)[methodName]; } } } - // Parse actions + // Parse actions (using index-based keys to support multiple of same type) const actions: PluginActionResponseDto[] = []; const actionConfigs: Record = {}; if (Array.isArray(parsed.actions)) { - for (const actionObj of parsed.actions) { + for (const [index, actionObj] of parsed.actions.entries()) { const methodName = Object.keys(actionObj)[0]; const action = availableActions.find((a) => a.methodName === methodName); if (action) { actions.push(action); - actionConfigs[methodName] = (actionObj as Record)[methodName]; + actionConfigs[`action_${index}`] = (actionObj as Record)[methodName]; } } } @@ -220,8 +265,6 @@ export const hasWorkflowChanged = ( orderedActions: PluginActionResponseDto[], filterConfigs: Record, actionConfigs: Record, - availableFilters: PluginFilterResponseDto[], - availableActions: PluginActionResponseDto[], ): boolean => { // Check enabled state if (enabled !== previousWorkflow.enabled) { @@ -252,25 +295,19 @@ export const hasWorkflowChanged = ( return true; } - // Check filter configs + // Check filter configs (using index-based keys) const previousFilterConfigs: Record = {}; - for (const wf of previousWorkflow.filters ?? []) { - const filterDef = availableFilters.find((f) => f.id === wf.pluginFilterId); - if (filterDef) { - previousFilterConfigs[filterDef.methodName] = wf.filterConfig ?? {}; - } + for (const [index, wf] of (previousWorkflow.filters ?? []).entries()) { + previousFilterConfigs[`filter_${index}`] = wf.filterConfig ?? {}; } if (JSON.stringify(previousFilterConfigs) !== JSON.stringify(filterConfigs)) { return true; } - // Check action configs + // Check action configs (using index-based keys) const previousActionConfigs: Record = {}; - for (const wa of previousWorkflow.actions ?? []) { - const actionDef = availableActions.find((a) => a.id === wa.pluginActionId); - if (actionDef) { - previousActionConfigs[actionDef.methodName] = wa.actionConfig ?? {}; - } + for (const [index, wa] of (previousWorkflow.actions ?? []).entries()) { + previousActionConfigs[`action_${index}`] = wa.actionConfig ?? {}; } if (JSON.stringify(previousActionConfigs) !== JSON.stringify(actionConfigs)) { return true; @@ -293,14 +330,14 @@ export const handleUpdateWorkflow = async ( filterConfigs: Record, actionConfigs: Record, ): Promise => { - const filters = orderedFilters.map((filter) => ({ + const filters = orderedFilters.map((filter, index) => ({ pluginFilterId: filter.id, - filterConfig: filterConfigs[filter.methodName] ?? {}, + filterConfig: filterConfigs[`filter_${index}`] ?? {}, })) as WorkflowFilterItemDto[]; - const actions = orderedActions.map((action) => ({ + const actions = orderedActions.map((action, index) => ({ pluginActionId: action.id, - actionConfig: actionConfigs[action.methodName] ?? {}, + actionConfig: actionConfigs[`action_${index}`] ?? {}, })) as WorkflowActionItemDto[]; const updateDto: WorkflowUpdateDto = { diff --git a/web/src/lib/utils/workflow.ts b/web/src/lib/utils/workflow.ts index ad379b5c82..c588f9836e 100644 --- a/web/src/lib/utils/workflow.ts +++ b/web/src/lib/utils/workflow.ts @@ -28,6 +28,23 @@ interface JSONSchema { required?: string[]; } +export const getComponentDefaultValue = (component: ComponentConfig): unknown => { + if (component.defaultValue !== undefined) { + return component.defaultValue; + } + + // Initialize with appropriate empty value based on component type + if (component.type === 'multiselect' || (component.type === 'text' && component.subType === 'people-picker')) { + return []; + } + + if (component.type === 'switch') { + return false; + } + + return ''; +}; + export const getComponentFromSchema = (schema: object | null): Record | null => { if (!schema || !isJSONSchema(schema) || !schema.properties) { return null; diff --git a/web/src/routes/(user)/utilities/workflows/[workflowId]/+page.svelte b/web/src/routes/(user)/utilities/workflows/[workflowId]/+page.svelte index 61f7402c35..747a7be094 100644 --- a/web/src/routes/(user)/utilities/workflows/[workflowId]/+page.svelte +++ b/web/src/routes/(user)/utilities/workflows/[workflowId]/+page.svelte @@ -18,6 +18,8 @@ initializeActionConfigs, initializeFilterConfigs, parseWorkflowJson, + remapConfigsOnRemove, + remapConfigsOnReorder, type WorkflowPayload, } from '$lib/services/workflow.service'; import { handleError } from '$lib/utils/handle-error'; @@ -93,8 +95,8 @@ ), ); - let filterConfigs: Record = $derived(initializeFilterConfigs(editWorkflow, supportFilters)); - let actionConfigs: Record = $derived(initializeActionConfigs(editWorkflow, supportActions)); + let filterConfigs: Record = $derived(initializeFilterConfigs(editWorkflow)); + let actionConfigs: Record = $derived(initializeActionConfigs(editWorkflow)); $effect(() => { editWorkflow.triggerType = triggerType; @@ -195,8 +197,6 @@ selectedActions, filterConfigs, actionConfigs, - filters, - actions, ), ); @@ -222,6 +222,9 @@ return; } + // Remap configs to follow the new order + filterConfigs = remapConfigsOnReorder(filterConfigs, 'filter', draggedFilterIndex, index, selectedFilters.length); + const newFilters = [...selectedFilters]; const [draggedItem] = newFilters.splice(draggedFilterIndex, 1); newFilters.splice(index, 0, draggedItem); @@ -249,6 +252,9 @@ return; } + // Remap configs to follow the new order + actionConfigs = remapConfigsOnReorder(actionConfigs, 'action', draggedActionIndex, index, selectedActions.length); + const newActions = [...selectedActions]; const [draggedItem] = newActions.splice(draggedActionIndex, 1); newActions.splice(index, 0, draggedItem); @@ -277,10 +283,14 @@ }; const handleRemoveFilter = (index: number) => { + // Remap configs to account for the removed item + filterConfigs = remapConfigsOnRemove(filterConfigs, 'filter', index, selectedFilters.length); selectedFilters = selectedFilters.filter((_, i) => i !== index); }; const handleRemoveAction = (index: number) => { + // Remap configs to account for the removed item + actionConfigs = remapConfigsOnRemove(actionConfigs, 'action', index, selectedActions.length); selectedActions = selectedActions.filter((_, i) => i !== index); }; @@ -473,7 +483,7 @@
@@ -542,7 +552,7 @@