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]}
-
{/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 @@