diff --git a/projects/workflows-creator/src/lib/builder/builder.component.html b/projects/workflows-creator/src/lib/builder/builder.component.html index c580a97..7a01dda 100644 --- a/projects/workflows-creator/src/lib/builder/builder.component.html +++ b/projects/workflows-creator/src/lib/builder/builder.component.html @@ -25,6 +25,7 @@ [isLast]="true" [isFirst]="true" [nodeType]="types.GROUP" + [eventGroups]="eventGroups" [popupTemplate]="nodePopup" [templateMap]="templateMap" [allColumns]="allColumns" diff --git a/projects/workflows-creator/src/lib/builder/builder.component.ts b/projects/workflows-creator/src/lib/builder/builder.component.ts index 3d26060..4ddf0c3 100644 --- a/projects/workflows-creator/src/lib/builder/builder.component.ts +++ b/projects/workflows-creator/src/lib/builder/builder.component.ts @@ -113,6 +113,20 @@ export class BuilderComponent implements OnInit, OnChanges { nodeList: AbstractBaseGroup[] = []; processId: string; + + // Current selected event identifier for action filtering + get currentSelectedEvent(): string | undefined { + // Look through event groups to find the first selected event + for (const eventGroup of this.eventGroups) { + if (eventGroup?.children?.length > 0) { + const firstChild = eventGroup.children[0]; + if (firstChild?.node?.type === NodeTypes.EVENT) { + return firstChild.node.getIdentifier(); + } + } + } + return undefined; + } // sonarignore:start // TODO: Refactor this code to be more flexible // sonarignore:start @@ -225,6 +239,9 @@ export class BuilderComponent implements OnInit, OnChanges { * @param event - ElementsWithInput */ onEventAdded(event: ElementsWithInput) { + // Check if we need to clear existing actions when event changes + this.clearIncompatibleActions(event.node.getIdentifier()); + this.eventAdded.emit({ name: event.node.getIdentifier(), event: event.newNode.node as WorkflowEvent, @@ -237,6 +254,44 @@ export class BuilderComponent implements OnInit, OnChanges { event.node.getIdentifier() === EventTypes.OnAddItemEvent); } + /** + * Clears actions that are not compatible with the selected event + * @param selectedEvent - The identifier of the selected event + */ + private clearIncompatibleActions(selectedEvent: string) { + if (this.actionGroups[0]?.children?.length > 0) { + // Get list of actions that should remain (compatible with new event) + const compatibleActions = this.nodes.getActions(selectedEvent); + const compatibleActionIds = new Set( + compatibleActions.map(action => action.getIdentifier()), + ); + + // Filter out incompatible actions + const currentActions = [...this.actionGroups[0].children]; + const actionsToRemove: number[] = []; + + currentActions.forEach((action, index) => { + const actionId = action.node.getIdentifier(); + if (!compatibleActionIds.has(actionId)) { + actionsToRemove.push(index); + } + }); + + // Remove incompatible actions (reverse order to maintain indexes) + actionsToRemove.reverse().forEach(index => { + this.actionGroups[0].children.splice(index, 1); + }); + + // Clear state for removed actions + actionsToRemove.forEach(index => { + const removedAction = currentActions[index]; + if (removedAction) { + this.updateState(removedAction.node, removedAction.inputs, true); + } + }); + } + } + /** * The function is called when an event is removed from the workflow. * Hides the else block when it is not needed. diff --git a/projects/workflows-creator/src/lib/builder/group/group.component.ts b/projects/workflows-creator/src/lib/builder/group/group.component.ts index 5e375af..51bf406 100644 --- a/projects/workflows-creator/src/lib/builder/group/group.component.ts +++ b/projects/workflows-creator/src/lib/builder/group/group.component.ts @@ -176,13 +176,7 @@ export class GroupComponent implements OnInit, AfterViewInit { ngOnInit(): void { this.events = this.nodes.getEvents(); this.triggerEvents = this.nodes.getEvents(true); - this.actions = this.nodes - .getActions() - .sort((a, b) => - a.name - .toString() - .localeCompare(b.name.toString(), undefined, {sensitivity: 'base'}), - ); + this.actions = []; this.typeSubjectPlaceholder = this.localizationSvc.getLocalizedString( LocalizedStringKeys.TypeSubject, @@ -323,15 +317,44 @@ export class GroupComponent implements OnInit, AfterViewInit { */ openPopup(type: NodeTypes) { if (type === NodeTypes.ACTION) { - this.nodeList = this.actions; - } else if (type === NodeTypes.EVENT) { + const selectedEvent = this.fetchSelectedEvent(); + this.nodeList = this.nodes.getActions(selectedEvent); + return; + } + if (type === NodeTypes.EVENT) { this.nodeList = - this.eventGroups.length === 1 && !this.group.children.length + this.eventGroups?.length === 1 && !this.group.children.length ? this.triggerEvents : this.events; - } else { - throw new InvalidEntityError('' + type); + return; + } + throw new InvalidEntityError(String(type)); + } + + private fetchSelectedEvent() { + let selectedEvent: string | undefined; + + // Method 1: find first EVENT in eventGroups + if (this.eventGroups?.length) { + for (const group of this.eventGroups) { + const firstChild = group?.children?.[0]; + if (firstChild?.node?.type === NodeTypes.EVENT) { + selectedEvent = firstChild.node.getIdentifier(); + break; + } + } + } + + // Method 2: fallback - find first EVENT in current group + if (!selectedEvent) { + for (const child of this.group.children ?? []) { + if (child?.node?.type === NodeTypes.EVENT) { + selectedEvent = child.node.getIdentifier(); + break; + } + } } + return selectedEvent; } /** @@ -362,6 +385,7 @@ export class GroupComponent implements OnInit, AfterViewInit { if (newNode.node.getIdentifier() === 'OnIntervalEvent') { newNode.node.state.change('valueInputType', 'number'); } + this.actions = this.nodes.getActions(); // Get all actions initially this.group.children.push(newNode as EventWithInput); this.eventAdded.emit({ node: node, diff --git a/projects/workflows-creator/src/lib/classes/services/abstract-node-service.class.ts b/projects/workflows-creator/src/lib/classes/services/abstract-node-service.class.ts index 0ae7224..c7f2805 100644 --- a/projects/workflows-creator/src/lib/classes/services/abstract-node-service.class.ts +++ b/projects/workflows-creator/src/lib/classes/services/abstract-node-service.class.ts @@ -4,7 +4,7 @@ import {WorkflowNode} from '../../types/base.types'; import {AbstractBaseGroup} from '../nodes'; export abstract class NodeService { - abstract getActions(): WorkflowNode[]; + abstract getActions(selectedEvent?: string): WorkflowNode[]; abstract getEvents(trigger?: boolean): WorkflowNode[]; abstract getGroups( trigger?: boolean, diff --git a/projects/workflows-creator/src/lib/services/bpmn/node.service.ts b/projects/workflows-creator/src/lib/services/bpmn/node.service.ts index 386f973..07f0ab7 100644 --- a/projects/workflows-creator/src/lib/services/bpmn/node.service.ts +++ b/projects/workflows-creator/src/lib/services/bpmn/node.service.ts @@ -34,18 +34,36 @@ export class BpmnNodesService extends NodeService { * > Get all the nodes that are of type `ACTION` * * The function is a bit more complicated than that, but that's the gist of it + * @param selectedEvent - Optional event identifier to filter actions by eventBinded property * @returns An array of action nodes. */ - getActions() { - return this.nodes - .map( - Node => - new Node( - this.localizationSvc.getLocalizedStringMap(), - this.utils.uuid(), - ), - ) - .filter(n => n.type === NodeTypes.ACTION); + getActions(selectedEvent?: string) { + const localizedStrings = this.localizationSvc.getLocalizedStringMap(); + + const actions = this.nodes + .map(Node => new Node(localizedStrings, this.utils.uuid())) + .filter(n => n.type === NodeTypes.ACTION) + .sort((a, b) => + a.name.toString().localeCompare(b.name.toString(), undefined, { + sensitivity: 'base', + }), + ); + + return actions.filter(action => { + const a = action as WorkflowAction & {eventBinded?: string[]}; + const events = a.eventBinded; + + if (!Array.isArray(events)) { + // unbound actions always allowed + return true; + } + + if (!selectedEvent) { + // no selectedEvent → return only unbound actions + return false; + } + return events.includes(selectedEvent); + }); } /**