verben-workflow-ui 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -338,7 +338,7 @@ class HttpWebRequestService {
338
338
  buildHeaders() {
339
339
  return {
340
340
  'Content-Type': 'application/json',
341
- Authorization: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJNYWlsQWRkcmVzcyI6InZlcmJlbmFAZ21haWwuY29tIiwiQXBwbGljYXRpb24iOiIiLCJOYW1lIjoiVmVyYmVuYSBMb2dpYyBMaW1pdGVkIiwiUm9sZUlEIjoiUk9MLVpYQVhYVCIsIlRlbmFudElkIjoiUERMVEM2IiwiU2VydmljZU5hbWUiOiJXaGl0ZTM2MCIsIm5iZiI6MTc0MDkwODYxMywiZXhwIjoxNzQxMTI0NjEzLCJpYXQiOjE3NDA5MDg2MTN9.3V63j6NICpTItBjowYuHI96sjYLwoPghryCd1u1-kSw',
341
+ Authorization: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJNYWlsQWRkcmVzcyI6InZlcmJlbmFAZ21haWwuY29tIiwiQXBwbGljYXRpb24iOiIiLCJOYW1lIjoiVmVyYmVuYSBMb2dpYyBMaW1pdGVkIiwiUm9sZUlEIjoiUk9MLVpYQVhYVCIsIlRlbmFudElkIjoiUERMVEM2IiwiU2VydmljZU5hbWUiOiJXaGl0ZTM2MCIsIkNvbXBhbnkiOiIiLCJuYmYiOjE3NDEwODE0NDcsImV4cCI6MTc0MTI5NzQ0NywiaWF0IjoxNzQxMDgxNDQ3fQ.u0ZkTg7mSHnPGjAIRwJxN1i1vjaRf1_F8mih9fbgQm0',
342
342
  };
343
343
  }
344
344
  buildUrl(url, baseUrl) {
@@ -2585,6 +2585,10 @@ class WorkflowDesignerState {
2585
2585
  draggingConnectionData = {};
2586
2586
  workflowFormId = null;
2587
2587
  workflowFormName = null;
2588
+ workflowId = null;
2589
+ setWorkflowId(id) {
2590
+ this.workflowId = id;
2591
+ }
2588
2592
  connectionRules = {
2589
2593
  stage: ['stage', 'decision', 'subflow', 'form'],
2590
2594
  decision: ['stage'], // Decisions can only connect to Stages
@@ -2763,7 +2767,7 @@ class WorkflowDesignerState {
2763
2767
  type,
2764
2768
  x,
2765
2769
  y: adjustedY,
2766
- width: type === 'subflow' ? 88 : 169, // Width from SVG
2770
+ width: type === 'subflow' ? 150 : 169, // Width from SVG
2767
2771
  height: 100, // Height from SVG
2768
2772
  isStartNode: false, // Default to false
2769
2773
  stageData: stageData || {}, // Store stage data
@@ -2909,119 +2913,6 @@ class WorkflowDesignerState {
2909
2913
  const sourceNodeType = sourceNodeInfo.node.type;
2910
2914
  return this.connectionRules[sourceNodeType] || [];
2911
2915
  }
2912
- // transformToWorkflowModel(): Workflow {
2913
- // // Create base workflow object
2914
- // const workflow: Workflow = {
2915
- // Name: 'New Workflow', // Default name or get it from somewhere
2916
- // Description: '', // Default description
2917
- // StageEntryRule: '',
2918
- // AssignmentType: TaskAssignmentType.AutoRoute, // Default assignment type
2919
- // Status: Status.Active, // Default status
2920
- // Actions: [],
2921
- // Lanes: [],
2922
- // Stages: [],
2923
- // };
2924
- // // Transform swimlanes to SwimLane[]
2925
- // workflow.Lanes = this.swimlanes.map((swimlane, index) => {
2926
- // return {
2927
- // Id: `lane-${index}`,
2928
- // Workflow: workflow.Id,
2929
- // Tags: swimlane.tags,
2930
- // Position: swimlane.order,
2931
- // Coordinates: { X: 0, Y: swimlane.order * 263 }, // Calculate Y position based on order
2932
- // Size: { Width: 3000, Height: 263 }, // Default size
2933
- // // Add other required properties from BaseModel
2934
- // Code: '',
2935
- // TenantId: '',
2936
- // id: `lane-${index}`,
2937
- // ServiceName: '',
2938
- // CreatedAt: new Date(),
2939
- // UpdatedAt: new Date(),
2940
- // DataState: ObjectState.New,
2941
- // };
2942
- // });
2943
- // // Transform nodes to WorkflowStage[]
2944
- // const stages: WorkflowStage[] = [];
2945
- // this.swimlanes.forEach((swimlane, swimlaneIndex) => {
2946
- // swimlane.nodes?.forEach((node) => {
2947
- // if (node.type === 'stage') {
2948
- // // Create a stage from the node
2949
- // const stage: WorkflowStage = {
2950
- // Id: node.id,
2951
- // Workflow: workflow.Id,
2952
- // Name: node.stageData?.Name || 'Unnamed Stage',
2953
- // Description: node.stageData?.Description || '',
2954
- // Duration: node.stageData?.Duration || 0,
2955
- // PassOnRule: node.stageData?.PassOnRule || '',
2956
- // ActorRule: node.stageData?.ActorRule || StageActorRule.None,
2957
- // MinNoOfActor: node.stageData?.MinNoOfActor || 0,
2958
- // IsParallel: false, // Default value, will be updated below
2959
- // IsEntryPoint: node.isStartNode,
2960
- // IsExitPoint: false, // Determine based on connections
2961
- // Tags: node.stageData?.Tags || [],
2962
- // Forms: node.stageData?.Forms || [],
2963
- // AllowMultiSubProcess: false,
2964
- // AssignmentType:
2965
- // node.stageData?.AssignmentType || TaskAssignmentType.AutoRoute,
2966
- // SubWorkFlow: node.stageData?.SubWorkFlow || '',
2967
- // SwimLane: workflow.Lanes[swimlaneIndex].Id,
2968
- // Coordinates: { X: node.x, Y: node.y },
2969
- // IsSubProcess: false,
2970
- // // Add other required properties from BaseModel
2971
- // Code: '',
2972
- // TenantId: '',
2973
- // id: node.id,
2974
- // ServiceName: '',
2975
- // CreatedAt: new Date(),
2976
- // UpdatedAt: new Date(),
2977
- // DataState: ObjectState.New,
2978
- // };
2979
- // stages.push(stage);
2980
- // }
2981
- // });
2982
- // });
2983
- // // Now process the connections to set IsParallel on target stages
2984
- // this.connections.forEach((conn) => {
2985
- // const sourceNodeInfo = this.findNodeById(conn.sourceNodeId);
2986
- // if (
2987
- // sourceNodeInfo &&
2988
- // sourceNodeInfo.node.type === 'stage' &&
2989
- // sourceNodeInfo.node.stageData?.hasParallel === true
2990
- // ) {
2991
- // // Find the target stage in our stages array
2992
- // const targetStage = stages.find((s) => s.Id === conn.targetNodeId);
2993
- // if (targetStage) {
2994
- // // Set IsParallel to true for this target stage
2995
- // targetStage.IsParallel = true;
2996
- // }
2997
- // }
2998
- // });
2999
- // // Transform connections to WorkflowAction[]
3000
- // workflow.Actions = this.connections.map((conn) => {
3001
- // const sourceNode = this.findNodeById(conn.sourceNodeId)?.node;
3002
- // const targetNode = this.findNodeById(conn.targetNodeId)?.node;
3003
- // return {
3004
- // Id: conn.id,
3005
- // Workflow: workflow.Id,
3006
- // Name: `Action from ${sourceNode?.stageData?.Name || 'Unknown'} to ${
3007
- // targetNode?.stageData?.Name || 'Unknown'
3008
- // }`,
3009
- // FromStage: conn.sourceNodeId,
3010
- // ToStage: conn.targetNodeId,
3011
- // IsParallel: targetNode?.stageData?.IsParallel || false,
3012
- // // Add other required properties from BaseModel
3013
- // Code: '',
3014
- // TenantId: '',
3015
- // id: conn.id,
3016
- // ServiceName: '',
3017
- // CreatedAt: new Date(),
3018
- // UpdatedAt: new Date(),
3019
- // DataState: ObjectState.New,
3020
- // };
3021
- // });
3022
- // workflow.Stages = stages;
3023
- // return workflow;
3024
- // }
3025
2916
  updateSwimlane(index, name, tags) {
3026
2917
  console.log('State: Updating swimlane at index', index, 'with name', name, 'and tags', tags);
3027
2918
  if (index >= 0 && index < this.swimlanes.length) {
@@ -3039,40 +2930,54 @@ class WorkflowDesignerState {
3039
2930
  transformToWorkflowModel() {
3040
2931
  // Create base workflow object
3041
2932
  const workflow = {
3042
- Id: '', // Will be assigned by backend on first save
3043
- Code: '',
2933
+ // BaseModel properties
2934
+ Id: this.wasLoadedFromApi(this.workflowId || '')
2935
+ ? this.workflowId || ''
2936
+ : '',
2937
+ Code: this.getCodeForObject(this.workflowId || ''),
3044
2938
  TenantId: '',
3045
- id: '',
2939
+ id: this.wasLoadedFromApi(this.workflowId || '')
2940
+ ? this.workflowId || ''
2941
+ : '',
3046
2942
  ServiceName: '',
3047
2943
  CreatedAt: new Date(),
3048
2944
  UpdatedAt: new Date(),
3049
- DataState: ObjectState.New,
3050
- Name: 'New Workflow', // Default name or get it from somewhere
2945
+ DataState: this.wasLoadedFromApi(this.workflowId || '')
2946
+ ? ObjectState.Changed
2947
+ : ObjectState.New,
2948
+ // Workflow specific properties
2949
+ Name: 'New Workflow', // Default name
3051
2950
  Description: '', // Default description
3052
2951
  StageEntryRule: '',
3053
2952
  Form: this.workflowFormId || undefined,
3054
- AssignmentType: TaskAssignmentType.AutoRoute, // Default assignment type
3055
- Status: Status.Active, // Default status
2953
+ AssignmentType: TaskAssignmentType.AutoRoute,
2954
+ Operation: '',
2955
+ Status: Status.Active,
3056
2956
  Actions: [],
3057
2957
  Lanes: [],
3058
2958
  Stages: [],
3059
2959
  };
3060
2960
  // Transform swimlanes to SwimLane[]
3061
2961
  workflow.Lanes = this.swimlanes.map((swimlane, index) => {
2962
+ const laneId = `lane-${index}`;
3062
2963
  return {
3063
- Id: `lane-${index}`,
3064
- Code: '',
2964
+ // BaseModel properties
2965
+ Id: laneId,
2966
+ Code: this.getCodeForObject(laneId),
3065
2967
  TenantId: '',
3066
- id: `lane-${index}`,
2968
+ id: laneId,
3067
2969
  ServiceName: '',
3068
2970
  CreatedAt: new Date(),
3069
2971
  UpdatedAt: new Date(),
3070
- DataState: ObjectState.New,
2972
+ DataState: this.wasLoadedFromApi(laneId)
2973
+ ? ObjectState.Changed
2974
+ : ObjectState.New,
2975
+ // SwimLane specific properties
3071
2976
  Workflow: workflow.Id,
3072
2977
  Tags: swimlane.tags || [],
3073
2978
  Position: swimlane.order,
3074
- Coordinates: { X: 0, Y: swimlane.order * 263 }, // Calculate Y position based on order
3075
- Size: { Width: 3000, Height: 263 }, // Default size
2979
+ Coordinates: { X: 0, Y: swimlane.order * 263 },
2980
+ Size: { Width: 3000, Height: 263 },
3076
2981
  };
3077
2982
  });
3078
2983
  // Transform nodes to WorkflowStage[]
@@ -3082,14 +2987,18 @@ class WorkflowDesignerState {
3082
2987
  if (node.type === 'stage') {
3083
2988
  // Create a stage from the node
3084
2989
  const stage = {
2990
+ // BaseModel properties
3085
2991
  Id: node.id,
3086
- Code: '',
2992
+ Code: this.getCodeForObject(node.id),
3087
2993
  TenantId: '',
3088
2994
  id: node.id,
3089
2995
  ServiceName: '',
3090
2996
  CreatedAt: new Date(),
3091
2997
  UpdatedAt: new Date(),
3092
- DataState: ObjectState.New,
2998
+ DataState: this.wasLoadedFromApi(node.id)
2999
+ ? ObjectState.Changed
3000
+ : ObjectState.New,
3001
+ // WorkflowStage specific properties
3093
3002
  Workflow: workflow.Id,
3094
3003
  Name: node.stageData?.Name || 'Unnamed Stage',
3095
3004
  Description: node.stageData?.Description || '',
@@ -3099,7 +3008,7 @@ class WorkflowDesignerState {
3099
3008
  MinNoOfActor: node.stageData?.MinNoOfActor || 0,
3100
3009
  IsParallel: node.stageData?.IsParallel || false,
3101
3010
  IsEntryPoint: node.isStartNode,
3102
- IsExitPoint: false, // Determine based on outgoing connections
3011
+ IsExitPoint: false, // Will be updated below
3103
3012
  Tags: node.stageData?.Tags || [],
3104
3013
  Forms: node.stageData?.formId ? [node.stageData.formId] : [],
3105
3014
  AllowMultiSubProcess: false,
@@ -3108,20 +3017,17 @@ class WorkflowDesignerState {
3108
3017
  SwimLane: workflow.Lanes[swimlaneIndex].Id,
3109
3018
  Coordinates: { X: node.x, Y: node.y },
3110
3019
  IsSubProcess: false,
3020
+ Key: node.stageData?.Key || undefined,
3111
3021
  };
3112
3022
  stages.push(stage);
3113
3023
  }
3114
3024
  });
3115
3025
  });
3116
- // Check which stages have no outgoing connections - they are exit points
3117
- this.connections.forEach((conn) => {
3118
- const targetStage = stages.find((s) => s.Id === conn.targetNodeId);
3119
- if (targetStage) {
3120
- // Check if this target has any outgoing connections
3121
- const hasOutgoingConnections = this.connections.some((c) => c.sourceNodeId === targetStage.Id);
3122
- if (!hasOutgoingConnections) {
3123
- targetStage.IsExitPoint = true;
3124
- }
3026
+ // Determine which stages are exit points (no outgoing connections)
3027
+ stages.forEach((stage) => {
3028
+ const hasOutgoingConnections = this.connections.some((conn) => conn.sourceNodeId === stage.Id);
3029
+ if (!hasOutgoingConnections) {
3030
+ stage.IsExitPoint = true;
3125
3031
  }
3126
3032
  });
3127
3033
  // Transform connections to WorkflowAction[]
@@ -3129,24 +3035,43 @@ class WorkflowDesignerState {
3129
3035
  const sourceNode = this.findNodeById(conn.sourceNodeId)?.node;
3130
3036
  const targetNode = this.findNodeById(conn.targetNodeId)?.node;
3131
3037
  return {
3038
+ // BaseModel properties
3132
3039
  Id: conn.id,
3133
- Code: '',
3040
+ Code: this.getCodeForObject(conn.id),
3134
3041
  TenantId: '',
3135
3042
  id: conn.id,
3136
3043
  ServiceName: '',
3137
3044
  CreatedAt: new Date(),
3138
3045
  UpdatedAt: new Date(),
3139
- DataState: ObjectState.New,
3046
+ DataState: this.wasLoadedFromApi(conn.id)
3047
+ ? ObjectState.Changed
3048
+ : ObjectState.New,
3049
+ // WorkflowAction specific properties
3140
3050
  Workflow: workflow.Id,
3141
3051
  Name: `Action from ${sourceNode?.stageData?.Name || 'Unknown'} to ${targetNode?.stageData?.Name || 'Unknown'}`,
3142
3052
  FromStage: conn.sourceNodeId,
3143
3053
  ToStage: conn.targetNodeId,
3144
3054
  IsParallel: sourceNode?.stageData?.hasParallel || false,
3055
+ PassOnRule: '', // Optional property
3145
3056
  };
3146
3057
  });
3147
3058
  workflow.Stages = stages;
3148
3059
  return workflow;
3149
3060
  }
3061
+ // Add a new property to track loaded objects
3062
+ loadedObjectIds = {}; // Format: { id: code }
3063
+ // Add a method to register loaded objects
3064
+ registerLoadedObject(id, code) {
3065
+ this.loadedObjectIds[id] = code;
3066
+ }
3067
+ // Method to check if an object was loaded from API
3068
+ wasLoadedFromApi(id) {
3069
+ return id in this.loadedObjectIds;
3070
+ }
3071
+ // Method to get the code for a loaded object
3072
+ getCodeForObject(id) {
3073
+ return this.loadedObjectIds[id] || '';
3074
+ }
3150
3075
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDesignerState, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3151
3076
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDesignerState, providedIn: 'root' });
3152
3077
  }
@@ -3425,7 +3350,7 @@ class StageNodeComponent {
3425
3350
  parallelExecutionToggled = new EventEmitter();
3426
3351
  // Properties for icon click events
3427
3352
  showFormPopup = false;
3428
- showShieldPopup = false;
3353
+ showShieldPopup = signal(false);
3429
3354
  showTimerPopup = false;
3430
3355
  showActionPopupLeft = false;
3431
3356
  showActionPopupRight = false;
@@ -3512,11 +3437,11 @@ class StageNodeComponent {
3512
3437
  event.stopPropagation();
3513
3438
  }
3514
3439
  // Toggle the shield popup
3515
- this.showShieldPopup = !this.showShieldPopup;
3516
- console.log('Shield popup toggled:', this.showShieldPopup, 'with data:', this.node.stageData);
3440
+ this.showShieldPopup.update((show) => !show);
3441
+ console.log('Shield popup toggled:', this.showShieldPopup(), 'with data:', this.node.stageData);
3517
3442
  // Force change detection in case Angular isn't detecting the state change
3518
3443
  setTimeout(() => {
3519
- if (this.showShieldPopup) {
3444
+ if (this.showShieldPopup()) {
3520
3445
  console.log('Shield popup should be visible now');
3521
3446
  }
3522
3447
  }, 0);
@@ -3560,7 +3485,7 @@ class StageNodeComponent {
3560
3485
  }
3561
3486
  onStagePropertiesSaved(stageData) {
3562
3487
  // Close the dialog
3563
- this.showShieldPopup = false;
3488
+ this.showShieldPopup.set(false);
3564
3489
  // Emit the event to the parent component with the node id and updated data
3565
3490
  this.stagePropertiesUpdated.emit({
3566
3491
  nodeId: this.node.id,
@@ -3569,11 +3494,11 @@ class StageNodeComponent {
3569
3494
  console.log('Stage properties updated:', stageData);
3570
3495
  }
3571
3496
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StageNodeComponent, deps: [{ token: WorkflowDesignerState }, { token: WorkflowDataService }], target: i0.ɵɵFactoryTarget.Component });
3572
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: StageNodeComponent, selector: "svg:g[lib-stage-node]", inputs: { node: "node", isStartNode: "isStartNode", stageData: "stageData" }, outputs: { stagePropertiesUpdated: "stagePropertiesUpdated", parallelExecutionToggled: "parallelExecutionToggled" }, ngImport: i0, template: "<svg:g>\n <!-- Stage node -->\n <svg:rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></svg:rect>\n\n <!-- Top-left icon: Stage form -->\n <svg:g\n (click)=\"toggleFormPopup($event)\"\n class=\"stage-icon\"\n transform=\"translate(6, 6)\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"node.stageData?.formId ? '#D36CFF' : 'black'\"\n />\n </svg:g>\n\n <!-- Top-right icon: Shield -->\n <svg:g\n (click)=\"toggleShieldPopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 6)'\"\n >\n <svg:path\n d=\"M7.5 12H16.5V13.5H7.5V12ZM7.5 7.5H16.5V9H7.5V7.5Z\"\n fill=\"black\"\n />\n <svg:path\n d=\"M12 22.5L7.36801 20.0303C6.0474 19.3279 4.94303 18.2791 4.17348 16.9964C3.40393 15.7138 2.99825 14.2458 3.00001 12.75V3C3.00001 2.60218 3.15804 2.22064 3.43935 1.93934C3.72065 1.65804 4.10218 1.5 4.50001 1.5H19.5C19.8978 1.5 20.2794 1.65804 20.5607 1.93934C20.842 2.22064 21 2.60218 21 3V12.75C21.0018 14.2458 20.5961 15.7138 19.8265 16.9964C19.057 18.2791 17.9526 19.3279 16.632 20.0303L12 22.5ZM4.50001 3V12.75C4.49917 13.9738 4.83141 15.1747 5.46111 16.224C6.09082 17.2733 6.99423 18.1315 8.07451 18.7065L12 20.7997L15.9255 18.7073C17.0059 18.1322 17.9094 17.2739 18.5391 16.2244C19.1688 15.175 19.501 13.9739 19.5 12.75V3H4.50001Z\"\n fill=\"black\"\n />\n </svg:g>\n\n <!-- Left-center icon: Thunderbolt -->\n <svg:g\n (click)=\"toggleActionPopup('left', $event)\"\n class=\"stage-icon\"\n transform=\"translate(6, 38)\"\n >\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Right-center icon: Thunderbolt -->\n <svg:g\n (click)=\"toggleActionPopup('right', $event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 38)'\"\n >\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Bottom-left icon: Double slash text -->\n <svg:g\n *ngIf=\"hasMultipleConnectedStages\"\n (click)=\"toggleCodePopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(6, ' + (node.height - 16) + ')'\"\n >\n <svg:text\n font-family=\"'Plus Jakarta Sans', sans-serif\"\n font-weight=\"500\"\n font-size=\"16px\"\n dominant-baseline=\"middle\"\n [attr.fill]=\"isParallelExecution ? '#D36CFF' : 'black'\"\n >\n //\n </svg:text>\n </svg:g>\n\n <!-- Bottom-right icon: Timer -->\n <svg:g\n (click)=\"toggleTimerPopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"\n 'translate(' + (node.width - 30) + ', ' + (node.height - 30) + ')'\n \"\n >\n <svg:path\n d=\"M11.5 3C14.0196 3 16.4359 4.00089 18.2175 5.78249C19.9991 7.56408 21 9.98044 21 12.5C21 15.0196 19.9991 17.4359 18.2175 19.2175C16.4359 20.9991 14.0196 22 11.5 22C8.98044 22 6.56408 20.9991 4.78249 19.2175C3.00089 17.4359 2 15.0196 2 12.5C2 9.98044 3.00089 7.56408 4.78249 5.78249C6.56408 4.00089 8.98044 3 11.5 3ZM11.5 4C9.24566 4 7.08365 4.89553 5.48959 6.48959C3.89553 8.08365 3 10.2457 3 12.5C3 14.7543 3.89553 16.9163 5.48959 18.5104C7.08365 20.1045 9.24566 21 11.5 21C12.6162 21 13.7215 20.7801 14.7528 20.353C15.7841 19.9258 16.7211 19.2997 17.5104 18.5104C18.2997 17.7211 18.9258 16.7841 19.353 15.7528C19.7801 14.7215 20 13.6162 20 12.5C20 10.2457 19.1045 8.08365 17.5104 6.48959C15.9163 4.89553 13.7543 4 11.5 4ZM11 7H12V12.42L16.7 15.13L16.2 16L11 13V7Z\"\n fill=\"black\"\n />\n </svg:g>\n\n <!-- Label in the center -->\n <svg:text\n [attr.x]=\"node.width / 2\"\n [attr.y]=\"node.height / 2\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-size=\"14\"\n fill=\"#000000\"\n >\n {{ node.stageData?.Name || \"Stage\" }}\n </svg:text>\n</svg:g>\n\n<lib-stage-dialog\n [visible]=\"showShieldPopup\"\n [stageData]=\"node.stageData || {}\"\n (closed)=\"showShieldPopup = false\"\n (saved)=\"onStagePropertiesSaved($event)\"\n></lib-stage-dialog>\n\n<div\n *ngIf=\"showFormPopup\"\n [style.position]=\"'fixed'\"\n [style.left.px]=\"formPopupX\"\n [style.top.px]=\"formPopupY\"\n [style.background-color]=\"'white'\"\n>\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Form</h4>\n <div *ngIf=\"isLoadingForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-2\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectForm(null)\"\n >\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of formsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectForm(form)\"\n >\n {{ form.Name }}\n </button>\n </div>\n <div\n *ngIf=\"formsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n</div>\n", styles: [".stage-icon{cursor:pointer}.stage-icon:hover path{fill:#d36cff}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i8.VerbenPopUpComponent, selector: "verben-pop-Up", inputs: ["dropdownOpen", "dropdownWidth", "color", "customStyles", "popUpClass", "border", "borderRadius", "enableMouseLeave"], outputs: ["dropdownOpenChange", "close"] }, { kind: "component", type: StageDialogComponent, selector: "lib-stage-dialog", inputs: ["visible", "stageData"], outputs: ["closed", "saved"] }] });
3497
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: StageNodeComponent, selector: "svg:g[lib-stage-node]", inputs: { node: "node", isStartNode: "isStartNode", stageData: "stageData" }, outputs: { stagePropertiesUpdated: "stagePropertiesUpdated", parallelExecutionToggled: "parallelExecutionToggled" }, ngImport: i0, template: "<svg:g>\n <!-- Stage node -->\n <svg:rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></svg:rect>\n\n <!-- Top-left icon: Stage form -->\n <svg:g\n (click)=\"toggleFormPopup($event)\"\n class=\"stage-icon\"\n transform=\"translate(6, 6)\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"node.stageData?.formId ? '#D36CFF' : 'black'\"\n />\n </svg:g>\n\n <!-- Top-right icon: Shield -->\n <svg:g\n (click)=\"toggleShieldPopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 6)'\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n\n <svg:path\n d=\"M7.5 12H16.5V13.5H7.5V12ZM7.5 7.5H16.5V9H7.5V7.5Z\"\n fill=\"black\"\n />\n <svg:path\n d=\"M12 22.5L7.36801 20.0303C6.0474 19.3279 4.94303 18.2791 4.17348 16.9964C3.40393 15.7138 2.99825 14.2458 3.00001 12.75V3C3.00001 2.60218 3.15804 2.22064 3.43935 1.93934C3.72065 1.65804 4.10218 1.5 4.50001 1.5H19.5C19.8978 1.5 20.2794 1.65804 20.5607 1.93934C20.842 2.22064 21 2.60218 21 3V12.75C21.0018 14.2458 20.5961 15.7138 19.8265 16.9964C19.057 18.2791 17.9526 19.3279 16.632 20.0303L12 22.5ZM4.50001 3V12.75C4.49917 13.9738 4.83141 15.1747 5.46111 16.224C6.09082 17.2733 6.99423 18.1315 8.07451 18.7065L12 20.7997L15.9255 18.7073C17.0059 18.1322 17.9094 17.2739 18.5391 16.2244C19.1688 15.175 19.501 13.9739 19.5 12.75V3H4.50001Z\"\n fill=\"black\"\n />\n </svg:g>\n\n <!-- Left-center icon: Thunderbolt -->\n <svg:g\n (click)=\"toggleActionPopup('left', $event)\"\n class=\"stage-icon\"\n transform=\"translate(6, 38)\"\n >\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Right-center icon: Thunderbolt -->\n <svg:g\n (click)=\"toggleActionPopup('right', $event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 38)'\"\n >\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Bottom-left icon: Double slash text -->\n <svg:g\n *ngIf=\"hasMultipleConnectedStages\"\n (click)=\"toggleCodePopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(6, ' + (node.height - 16) + ')'\"\n >\n <svg:text\n font-family=\"'Plus Jakarta Sans', sans-serif\"\n font-weight=\"500\"\n font-size=\"16px\"\n dominant-baseline=\"middle\"\n [attr.fill]=\"isParallelExecution ? '#D36CFF' : 'black'\"\n >\n //\n </svg:text>\n </svg:g>\n\n <!-- Bottom-right icon: Timer -->\n <svg:g\n (click)=\"toggleTimerPopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"\n 'translate(' + (node.width - 30) + ', ' + (node.height - 30) + ')'\n \"\n >\n <svg:path\n d=\"M11.5 3C14.0196 3 16.4359 4.00089 18.2175 5.78249C19.9991 7.56408 21 9.98044 21 12.5C21 15.0196 19.9991 17.4359 18.2175 19.2175C16.4359 20.9991 14.0196 22 11.5 22C8.98044 22 6.56408 20.9991 4.78249 19.2175C3.00089 17.4359 2 15.0196 2 12.5C2 9.98044 3.00089 7.56408 4.78249 5.78249C6.56408 4.00089 8.98044 3 11.5 3ZM11.5 4C9.24566 4 7.08365 4.89553 5.48959 6.48959C3.89553 8.08365 3 10.2457 3 12.5C3 14.7543 3.89553 16.9163 5.48959 18.5104C7.08365 20.1045 9.24566 21 11.5 21C12.6162 21 13.7215 20.7801 14.7528 20.353C15.7841 19.9258 16.7211 19.2997 17.5104 18.5104C18.2997 17.7211 18.9258 16.7841 19.353 15.7528C19.7801 14.7215 20 13.6162 20 12.5C20 10.2457 19.1045 8.08365 17.5104 6.48959C15.9163 4.89553 13.7543 4 11.5 4ZM11 7H12V12.42L16.7 15.13L16.2 16L11 13V7Z\"\n fill=\"black\"\n />\n </svg:g>\n\n <!-- Label in the center -->\n <svg:text\n [attr.x]=\"node.width / 2\"\n [attr.y]=\"node.height / 2\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-size=\"14\"\n fill=\"#000000\"\n >\n {{ node.stageData?.Name || \"Stage\" }}\n </svg:text>\n</svg:g>\n\n<lib-stage-dialog\n [visible]=\"showShieldPopup()\"\n [stageData]=\"node.stageData || {}\"\n (closed)=\"showShieldPopup.set(false)\"\n (saved)=\"onStagePropertiesSaved($event)\"\n></lib-stage-dialog>\n\n<div\n *ngIf=\"showFormPopup\"\n [style.position]=\"'fixed'\"\n [style.left.px]=\"formPopupX\"\n [style.top.px]=\"formPopupY\"\n [style.background-color]=\"'white'\"\n>\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Form</h4>\n <div *ngIf=\"isLoadingForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-2\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectForm(null)\"\n >\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of formsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectForm(form)\"\n >\n {{ form.Name }}\n </button>\n </div>\n <div\n *ngIf=\"formsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n</div>\n", styles: [".stage-icon{cursor:pointer}.stage-icon:hover path{fill:#d36cff}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i8.VerbenPopUpComponent, selector: "verben-pop-Up", inputs: ["dropdownOpen", "dropdownWidth", "color", "customStyles", "popUpClass", "border", "borderRadius", "enableMouseLeave"], outputs: ["dropdownOpenChange", "close"] }, { kind: "component", type: StageDialogComponent, selector: "lib-stage-dialog", inputs: ["visible", "stageData"], outputs: ["closed", "saved"] }] });
3573
3498
  }
3574
3499
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: StageNodeComponent, decorators: [{
3575
3500
  type: Component,
3576
- args: [{ selector: 'svg:g[lib-stage-node]', template: "<svg:g>\n <!-- Stage node -->\n <svg:rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></svg:rect>\n\n <!-- Top-left icon: Stage form -->\n <svg:g\n (click)=\"toggleFormPopup($event)\"\n class=\"stage-icon\"\n transform=\"translate(6, 6)\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"node.stageData?.formId ? '#D36CFF' : 'black'\"\n />\n </svg:g>\n\n <!-- Top-right icon: Shield -->\n <svg:g\n (click)=\"toggleShieldPopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 6)'\"\n >\n <svg:path\n d=\"M7.5 12H16.5V13.5H7.5V12ZM7.5 7.5H16.5V9H7.5V7.5Z\"\n fill=\"black\"\n />\n <svg:path\n d=\"M12 22.5L7.36801 20.0303C6.0474 19.3279 4.94303 18.2791 4.17348 16.9964C3.40393 15.7138 2.99825 14.2458 3.00001 12.75V3C3.00001 2.60218 3.15804 2.22064 3.43935 1.93934C3.72065 1.65804 4.10218 1.5 4.50001 1.5H19.5C19.8978 1.5 20.2794 1.65804 20.5607 1.93934C20.842 2.22064 21 2.60218 21 3V12.75C21.0018 14.2458 20.5961 15.7138 19.8265 16.9964C19.057 18.2791 17.9526 19.3279 16.632 20.0303L12 22.5ZM4.50001 3V12.75C4.49917 13.9738 4.83141 15.1747 5.46111 16.224C6.09082 17.2733 6.99423 18.1315 8.07451 18.7065L12 20.7997L15.9255 18.7073C17.0059 18.1322 17.9094 17.2739 18.5391 16.2244C19.1688 15.175 19.501 13.9739 19.5 12.75V3H4.50001Z\"\n fill=\"black\"\n />\n </svg:g>\n\n <!-- Left-center icon: Thunderbolt -->\n <svg:g\n (click)=\"toggleActionPopup('left', $event)\"\n class=\"stage-icon\"\n transform=\"translate(6, 38)\"\n >\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Right-center icon: Thunderbolt -->\n <svg:g\n (click)=\"toggleActionPopup('right', $event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 38)'\"\n >\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Bottom-left icon: Double slash text -->\n <svg:g\n *ngIf=\"hasMultipleConnectedStages\"\n (click)=\"toggleCodePopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(6, ' + (node.height - 16) + ')'\"\n >\n <svg:text\n font-family=\"'Plus Jakarta Sans', sans-serif\"\n font-weight=\"500\"\n font-size=\"16px\"\n dominant-baseline=\"middle\"\n [attr.fill]=\"isParallelExecution ? '#D36CFF' : 'black'\"\n >\n //\n </svg:text>\n </svg:g>\n\n <!-- Bottom-right icon: Timer -->\n <svg:g\n (click)=\"toggleTimerPopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"\n 'translate(' + (node.width - 30) + ', ' + (node.height - 30) + ')'\n \"\n >\n <svg:path\n d=\"M11.5 3C14.0196 3 16.4359 4.00089 18.2175 5.78249C19.9991 7.56408 21 9.98044 21 12.5C21 15.0196 19.9991 17.4359 18.2175 19.2175C16.4359 20.9991 14.0196 22 11.5 22C8.98044 22 6.56408 20.9991 4.78249 19.2175C3.00089 17.4359 2 15.0196 2 12.5C2 9.98044 3.00089 7.56408 4.78249 5.78249C6.56408 4.00089 8.98044 3 11.5 3ZM11.5 4C9.24566 4 7.08365 4.89553 5.48959 6.48959C3.89553 8.08365 3 10.2457 3 12.5C3 14.7543 3.89553 16.9163 5.48959 18.5104C7.08365 20.1045 9.24566 21 11.5 21C12.6162 21 13.7215 20.7801 14.7528 20.353C15.7841 19.9258 16.7211 19.2997 17.5104 18.5104C18.2997 17.7211 18.9258 16.7841 19.353 15.7528C19.7801 14.7215 20 13.6162 20 12.5C20 10.2457 19.1045 8.08365 17.5104 6.48959C15.9163 4.89553 13.7543 4 11.5 4ZM11 7H12V12.42L16.7 15.13L16.2 16L11 13V7Z\"\n fill=\"black\"\n />\n </svg:g>\n\n <!-- Label in the center -->\n <svg:text\n [attr.x]=\"node.width / 2\"\n [attr.y]=\"node.height / 2\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-size=\"14\"\n fill=\"#000000\"\n >\n {{ node.stageData?.Name || \"Stage\" }}\n </svg:text>\n</svg:g>\n\n<lib-stage-dialog\n [visible]=\"showShieldPopup\"\n [stageData]=\"node.stageData || {}\"\n (closed)=\"showShieldPopup = false\"\n (saved)=\"onStagePropertiesSaved($event)\"\n></lib-stage-dialog>\n\n<div\n *ngIf=\"showFormPopup\"\n [style.position]=\"'fixed'\"\n [style.left.px]=\"formPopupX\"\n [style.top.px]=\"formPopupY\"\n [style.background-color]=\"'white'\"\n>\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Form</h4>\n <div *ngIf=\"isLoadingForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-2\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectForm(null)\"\n >\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of formsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectForm(form)\"\n >\n {{ form.Name }}\n </button>\n </div>\n <div\n *ngIf=\"formsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n</div>\n", styles: [".stage-icon{cursor:pointer}.stage-icon:hover path{fill:#d36cff}\n"] }]
3501
+ args: [{ selector: 'svg:g[lib-stage-node]', template: "<svg:g>\n <!-- Stage node -->\n <svg:rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></svg:rect>\n\n <!-- Top-left icon: Stage form -->\n <svg:g\n (click)=\"toggleFormPopup($event)\"\n class=\"stage-icon\"\n transform=\"translate(6, 6)\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"node.stageData?.formId ? '#D36CFF' : 'black'\"\n />\n </svg:g>\n\n <!-- Top-right icon: Shield -->\n <svg:g\n (click)=\"toggleShieldPopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 6)'\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n\n <svg:path\n d=\"M7.5 12H16.5V13.5H7.5V12ZM7.5 7.5H16.5V9H7.5V7.5Z\"\n fill=\"black\"\n />\n <svg:path\n d=\"M12 22.5L7.36801 20.0303C6.0474 19.3279 4.94303 18.2791 4.17348 16.9964C3.40393 15.7138 2.99825 14.2458 3.00001 12.75V3C3.00001 2.60218 3.15804 2.22064 3.43935 1.93934C3.72065 1.65804 4.10218 1.5 4.50001 1.5H19.5C19.8978 1.5 20.2794 1.65804 20.5607 1.93934C20.842 2.22064 21 2.60218 21 3V12.75C21.0018 14.2458 20.5961 15.7138 19.8265 16.9964C19.057 18.2791 17.9526 19.3279 16.632 20.0303L12 22.5ZM4.50001 3V12.75C4.49917 13.9738 4.83141 15.1747 5.46111 16.224C6.09082 17.2733 6.99423 18.1315 8.07451 18.7065L12 20.7997L15.9255 18.7073C17.0059 18.1322 17.9094 17.2739 18.5391 16.2244C19.1688 15.175 19.501 13.9739 19.5 12.75V3H4.50001Z\"\n fill=\"black\"\n />\n </svg:g>\n\n <!-- Left-center icon: Thunderbolt -->\n <svg:g\n (click)=\"toggleActionPopup('left', $event)\"\n class=\"stage-icon\"\n transform=\"translate(6, 38)\"\n >\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Right-center icon: Thunderbolt -->\n <svg:g\n (click)=\"toggleActionPopup('right', $event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(' + (node.width - 30) + ', 38)'\"\n >\n <svg:path d=\"M11 15H6L13 1V9H18L11 23V15Z\" fill=\"black\" />\n </svg:g>\n\n <!-- Bottom-left icon: Double slash text -->\n <svg:g\n *ngIf=\"hasMultipleConnectedStages\"\n (click)=\"toggleCodePopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"'translate(6, ' + (node.height - 16) + ')'\"\n >\n <svg:text\n font-family=\"'Plus Jakarta Sans', sans-serif\"\n font-weight=\"500\"\n font-size=\"16px\"\n dominant-baseline=\"middle\"\n [attr.fill]=\"isParallelExecution ? '#D36CFF' : 'black'\"\n >\n //\n </svg:text>\n </svg:g>\n\n <!-- Bottom-right icon: Timer -->\n <svg:g\n (click)=\"toggleTimerPopup($event)\"\n class=\"stage-icon\"\n [attr.transform]=\"\n 'translate(' + (node.width - 30) + ', ' + (node.height - 30) + ')'\n \"\n >\n <svg:path\n d=\"M11.5 3C14.0196 3 16.4359 4.00089 18.2175 5.78249C19.9991 7.56408 21 9.98044 21 12.5C21 15.0196 19.9991 17.4359 18.2175 19.2175C16.4359 20.9991 14.0196 22 11.5 22C8.98044 22 6.56408 20.9991 4.78249 19.2175C3.00089 17.4359 2 15.0196 2 12.5C2 9.98044 3.00089 7.56408 4.78249 5.78249C6.56408 4.00089 8.98044 3 11.5 3ZM11.5 4C9.24566 4 7.08365 4.89553 5.48959 6.48959C3.89553 8.08365 3 10.2457 3 12.5C3 14.7543 3.89553 16.9163 5.48959 18.5104C7.08365 20.1045 9.24566 21 11.5 21C12.6162 21 13.7215 20.7801 14.7528 20.353C15.7841 19.9258 16.7211 19.2997 17.5104 18.5104C18.2997 17.7211 18.9258 16.7841 19.353 15.7528C19.7801 14.7215 20 13.6162 20 12.5C20 10.2457 19.1045 8.08365 17.5104 6.48959C15.9163 4.89553 13.7543 4 11.5 4ZM11 7H12V12.42L16.7 15.13L16.2 16L11 13V7Z\"\n fill=\"black\"\n />\n </svg:g>\n\n <!-- Label in the center -->\n <svg:text\n [attr.x]=\"node.width / 2\"\n [attr.y]=\"node.height / 2\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-size=\"14\"\n fill=\"#000000\"\n >\n {{ node.stageData?.Name || \"Stage\" }}\n </svg:text>\n</svg:g>\n\n<lib-stage-dialog\n [visible]=\"showShieldPopup()\"\n [stageData]=\"node.stageData || {}\"\n (closed)=\"showShieldPopup.set(false)\"\n (saved)=\"onStagePropertiesSaved($event)\"\n></lib-stage-dialog>\n\n<div\n *ngIf=\"showFormPopup\"\n [style.position]=\"'fixed'\"\n [style.left.px]=\"formPopupX\"\n [style.top.px]=\"formPopupY\"\n [style.background-color]=\"'white'\"\n>\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Form</h4>\n <div *ngIf=\"isLoadingForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-2\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectForm(null)\"\n >\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of formsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectForm(form)\"\n >\n {{ form.Name }}\n </button>\n </div>\n <div\n *ngIf=\"formsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n</div>\n", styles: [".stage-icon{cursor:pointer}.stage-icon:hover path{fill:#d36cff}\n"] }]
3577
3502
  }], ctorParameters: () => [{ type: WorkflowDesignerState }, { type: WorkflowDataService }], propDecorators: { node: [{
3578
3503
  type: Input
3579
3504
  }], isStartNode: [{
@@ -4091,11 +4016,11 @@ class DesignerCanvasComponent {
4091
4016
  this.pendingConnectionSourceSwimlaneIndex = null;
4092
4017
  }
4093
4018
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DesignerCanvasComponent, deps: [{ token: WorkflowDesignerState }, { token: WorkflowDataService }], target: i0.ɵɵFactoryTarget.Component });
4094
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: DesignerCanvasComponent, selector: "lib-designer-canvas", inputs: { selectedTool: "selectedTool" }, outputs: { clickedPosition: "clickedPosition", subflowSelected: "subflowSelected", showStageDialog: "showStageDialog" }, host: { listeners: { "window:mouseup": "onWindowMouseUp($event)", "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"canvas-container\">\n <svg\n #canvas\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"canvasHeight\"\n class=\"designer-canvas\"\n (click)=\"onCanvasClick($event)\"\n >\n <defs>\n <!-- Grid pattern definition -->\n\n <pattern\n id=\"grid\"\n [attr.width]=\"gridSize\"\n [attr.height]=\"gridSize\"\n patternUnits=\"userSpaceOnUse\"\n >\n <path\n d=\"M 20 0 L 0 0 0 20\"\n fill=\"none\"\n stroke=\"#e2e8f0\"\n stroke-width=\"0.5\"\n />\n </pattern>\n\n <!-- Arrow head marker definition -->\n <marker\n id=\"arrowhead\"\n markerWidth=\"10\"\n markerHeight=\"7\"\n refX=\"9\"\n refY=\"3.5\"\n orient=\"auto\"\n >\n <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"#D36CFF\" />\n </marker>\n\n <!-- Connection point styles -->\n <circle\n id=\"connection-point-template\"\n r=\"5\"\n fill=\"#D36CFF\"\n stroke=\"#FFFFFF\"\n stroke-width=\"1\"\n />\n\n <!-- Dashed line style for connection preview -->\n <pattern\n id=\"dashed-line\"\n width=\"10\"\n height=\"10\"\n patternUnits=\"userSpaceOnUse\"\n >\n <line\n x1=\"0\"\n y1=\"5\"\n x2=\"10\"\n y2=\"5\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n stroke-dasharray=\"5,5\"\n />\n </pattern>\n </defs>\n\n <!-- Background grid -->\n <rect width=\"100%\" height=\"100%\" fill=\"url(#grid)\" />\n\n <!-- Display a message when no swimlanes exist -->\n @if (state.swimlanes.length === 0) {\n <text\n x=\"50%\"\n y=\"50%\"\n font-family=\"sans-serif\"\n font-size=\"16\"\n fill=\"#94a3b8\"\n text-anchor=\"middle\"\n >\n Select the Swimlane tool and click on the canvas to add a swimlane\n </text>\n }\n\n <!-- This is where workflow elements will be added later -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <g [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\">\n <!-- Swimlane container -->\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"263\"\n fill=\"#ffffff\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <!-- Swimlane header -->\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n height=\"40\"\n fill=\"#f8fafc\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <!-- Swimlane label -->\n <text\n x=\"20\"\n y=\"25\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#333333\"\n font-weight=\"bold\"\n >\n {{ swimlane.label }}\n </text>\n\n <!-- Edit button -->\n <g\n class=\"edit-swimlane-button\"\n [attr.transform]=\"'translate(200, 20)'\"\n (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \"\n >\n <rect\n x=\"-5\"\n y=\"-15\"\n width=\"40\"\n height=\"20\"\n fill=\"#f3e8ff\"\n rx=\"3\"\n ry=\"3\"\n stroke=\"#d8b4fe\"\n stroke-width=\"1\"\n ></rect>\n <text\n x=\"15\"\n y=\"0\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#7e22ce\"\n text-anchor=\"middle\"\n >\n Edit\n </text>\n </g>\n\n <!-- Tag indicators -->\n <g [attr.transform]=\"'translate(200, 20)'\">\n @for (tag of swimlane.tags.slice(0, 3); track tag.Name; let i = $index)\n {\n <text\n [attr.x]=\"i * 100\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n {{ tag.Name }}\n </text>\n } @if (swimlane.tags.length > 3) {\n <text\n [attr.x]=\"3 * 100 + 10\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n +{{ swimlane.tags.length - 3 }} more\n </text>\n }\n </g>\n\n <!-- Render nodes in this swimlane -->\n @for (node of swimlane.nodes; track node.id) {\n <g\n [attr.transform]=\"'translate(' + node.x + ',' + (node.y + 40) + ')'\"\n (mouseenter)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseleave)=\"hideConnectionPoints(node.id)\"\n >\n <!-- Start node indicator (circle and arrow) for the first node -->\n @if (node.isStartNode) {\n <!-- Circle -->\n <circle\n cx=\"-30\"\n cy=\"50\"\n r=\"15\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></circle>\n <!-- Form icon for start node -->\n <svg:g\n (click)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order)\n \"\n class=\"stage-icon\"\n transform=\"translate(-45, 42)\"\n style=\"cursor: pointer; pointer-events: all\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"state.workflowFormId ? '#D36CFF' : 'black'\"\n transform=\"scale(0.7)\"\n />\n </svg:g>\n <!-- Arrow from circle to node -->\n <path\n d=\"M -20 50 L 0 50\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n marker-end=\"url(#arrowhead)\"\n ></path>\n }\n\n <!-- Stage node -->\n @if (node.type === 'stage') {\n <!-- <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></rect> -->\n <svg:g\n lib-stage-node\n [node]=\"node\"\n [isStartNode]=\"node.isStartNode\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\"\n ></svg:g>\n }\n\n <!-- Decision node -->\n @if (node.type === 'decision') {\n <path\n [attr.d]=\"\n 'M 0 ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' 0' +\n ' L ' +\n node.width +\n ' ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' ' +\n node.height +\n ' Z'\n \"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></path>\n }\n\n <!-- Form node -->\n @if (node.type === 'form') {\n <path\n d=\"M95.0625 50.5591V95.0625C95.0625 97.9716 93.9069 100.762 91.8498 102.819C89.7928 104.876 87.0028 106.031 84.0938 106.031H32.9062C29.9972 106.031 27.2072 104.876 25.1502 102.819C23.0931 100.762 21.9375 97.9716 21.9375 95.0625V21.9375C21.9375 19.0284 23.0931 16.2385 25.1502 14.1814C27.2072 12.1244 29.9972 10.9688 32.9062 10.9688H55.4722C57.4109 10.969 59.2701 11.7392 60.6412 13.1099L92.9213 45.3901C94.292 46.7611 95.0622 48.6204 95.0625 50.5591Z\"\n transform=\"scale(0.7)\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M58.5 12.7969V40.2188C58.5 42.1581 59.2704 44.0181 60.6418 45.3895C62.0131 46.7608 63.8731 47.5312 65.8125 47.5312H93.2344\"\n transform=\"scale(0.7)\"\n stroke=\"#D36CFF\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <text\n x=\"50%\"\n y=\"50%\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#D36CFF\"\n >\n Form\n </text>\n }\n\n <!-- Subflow node -->\n @if (node.type === 'subflow') {\n <path\n [attr.d]=\"\n 'M 1 ' +\n node.height / 4 +\n ' L ' +\n node.width / 2 +\n ' 1 L ' +\n (node.width - 1) +\n ' ' +\n node.height / 4 +\n ' V ' +\n (node.height * 3) / 4 +\n ' L ' +\n node.width / 2 +\n ' ' +\n (node.height - 1) +\n ' L 1 ' +\n (node.height * 3) / 4 +\n ' Z'\n \"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></path>\n <text\n x=\"50%\"\n y=\"50%\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#000000\"\n >\n {{ node.workflowData?.name || \"Subflow\" }}\n </text>\n }\n\n <!-- Connection points for this node -->\n @for (point of node.connectionPoints || []; track point.id) { @if\n (isConnectionPointVisible(node.id)) {\n <use\n [attr.href]=\"'#connection-point-template'\"\n [attr.x]=\"point.x\"\n [attr.y]=\"point.y\"\n [attr.id]=\"point.id\"\n (mousedown)=\"startConnectionDrag($event, point, swimlane.order)\"\n />\n } }\n </g>\n <!-- A transparent hover area for improved hover detection -->\n <rect\n [attr.x]=\"node.x - 10\"\n [attr.y]=\"node.y + 40 - 10\"\n [attr.width]=\"node.width + 20\"\n [attr.height]=\"node.height + 20\"\n fill=\"transparent\"\n (mouseover)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseout)=\"hideConnectionPoints(node.id)\"\n style=\"pointer-events: none\"\n />\n }\n </g>\n } @for (connection of state.connections; track connection.id) {\n <g>\n <path\n [attr.d]=\"getConnectionPathForSavedConnection(connection)\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n fill=\"none\"\n marker-end=\"url(#arrowhead)\"\n ></path>\n </g>\n }\n\n <!-- Connection preview line -->\n @if (state.isConnectionDragging()) {\n <g>\n <path\n [attr.d]=\"getConnectionPath()\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n stroke-dasharray=\"5,5\"\n fill=\"none\"\n ></path>\n </g>\n }\n </svg>\n\n <div\n *ngIf=\"popupVisible\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"popupX\"\n [style.top.px]=\"popupY\"\n >\n <verben-pop-Up\n [dropdownOpen]=\"true\"\n [customStyles]=\"{ 'z-index': '99' }\"\n [enableMouseLeave]=\"false\"\n >\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md\"\n dropdown-content\n >\n <h4 class=\"mb-2 font-medium\">Create Connection</h4>\n <div class=\"flex flex-col gap-2\">\n <ng-container *ngFor=\"let type of allowedNodeTypes\">\n <button\n class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"createNodeConnection(type)\"\n >\n Create {{ type | titlecase }}\n </button>\n </ng-container>\n <button\n class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"hideConnectionPopup()\"\n >\n Cancel\n </button>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div\n *ngIf=\"showStartNodeFormPopup\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"startNodeFormPopupX\"\n [style.top.px]=\"startNodeFormPopupY\"\n >\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Workflow Form</h4>\n <div *ngIf=\"isLoadingStartNodeForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingStartNodeForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-2\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(null)\"\n >\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of startNodeFormsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(form)\"\n >\n {{ form.Name }}\n </button>\n </div>\n <div\n *ngIf=\"startNodeFormsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div\n *ngIf=\"showSubflowPopup\"\n [style.position]=\"'fiabsolutexed'\"\n [style.left.px]=\"subflowPopupX\"\n [style.top.px]=\"subflowPopupY\"\n >\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Workflow</h4>\n <div *ngIf=\"isLoadingWorkflows\" class=\"text-center py-2\">\n Loading workflows...\n </div>\n <div *ngIf=\"!isLoadingWorkflows\" class=\"max-h-48 overflow-y-auto\">\n <div *ngFor=\"let workflow of workflowsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectSubflowWorkflow(workflow)\"\n >\n {{ workflow.Name }}\n </button>\n </div>\n <div\n *ngIf=\"workflowsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No workflows available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n</div>\n", styles: [".canvas-container{flex:1;overflow:auto;background-color:#fff}.designer-canvas{min-width:100%;min-height:100%;cursor:default}.designer-canvas:focus{outline:none}.edit-swimlane-button{cursor:pointer}.edit-swimlane-button:hover rect{fill:#ddd6fe}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i8.VerbenPopUpComponent, selector: "verben-pop-Up", inputs: ["dropdownOpen", "dropdownWidth", "color", "customStyles", "popUpClass", "border", "borderRadius", "enableMouseLeave"], outputs: ["dropdownOpenChange", "close"] }, { kind: "component", type: StageNodeComponent, selector: "svg:g[lib-stage-node]", inputs: ["node", "isStartNode", "stageData"], outputs: ["stagePropertiesUpdated", "parallelExecutionToggled"] }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }] });
4019
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: DesignerCanvasComponent, selector: "lib-designer-canvas", inputs: { selectedTool: "selectedTool" }, outputs: { clickedPosition: "clickedPosition", subflowSelected: "subflowSelected", showStageDialog: "showStageDialog" }, host: { listeners: { "window:mouseup": "onWindowMouseUp($event)", "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true, static: true }], ngImport: i0, template: "<div class=\"canvas-container\">\n <svg\n #canvas\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"canvasHeight\"\n class=\"designer-canvas\"\n (click)=\"onCanvasClick($event)\"\n >\n <defs>\n <!-- Grid pattern definition -->\n\n <pattern\n id=\"grid\"\n [attr.width]=\"gridSize\"\n [attr.height]=\"gridSize\"\n patternUnits=\"userSpaceOnUse\"\n >\n <path\n d=\"M 20 0 L 0 0 0 20\"\n fill=\"none\"\n stroke=\"#e2e8f0\"\n stroke-width=\"0.5\"\n />\n </pattern>\n\n <!-- Arrow head marker definition -->\n <marker\n id=\"arrowhead\"\n markerWidth=\"10\"\n markerHeight=\"7\"\n refX=\"9\"\n refY=\"3.5\"\n orient=\"auto\"\n >\n <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"#D36CFF\" />\n </marker>\n\n <!-- Connection point styles -->\n <circle\n id=\"connection-point-template\"\n r=\"5\"\n fill=\"#D36CFF\"\n stroke=\"#FFFFFF\"\n stroke-width=\"1\"\n />\n\n <!-- Dashed line style for connection preview -->\n <pattern\n id=\"dashed-line\"\n width=\"10\"\n height=\"10\"\n patternUnits=\"userSpaceOnUse\"\n >\n <line\n x1=\"0\"\n y1=\"5\"\n x2=\"10\"\n y2=\"5\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n stroke-dasharray=\"5,5\"\n />\n </pattern>\n </defs>\n\n <!-- Background grid -->\n <rect width=\"100%\" height=\"100%\" fill=\"url(#grid)\" />\n\n <!-- Display a message when no swimlanes exist -->\n @if (state.swimlanes.length === 0) {\n <text\n x=\"50%\"\n y=\"50%\"\n font-family=\"sans-serif\"\n font-size=\"16\"\n fill=\"#94a3b8\"\n text-anchor=\"middle\"\n >\n Select the Swimlane tool and click on the canvas to add a swimlane\n </text>\n }\n\n <!-- This is where workflow elements will be added later -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <g [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\">\n <!-- Swimlane container -->\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"263\"\n fill=\"#ffffff\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <!-- Swimlane header -->\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n height=\"40\"\n fill=\"#f8fafc\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <!-- Swimlane label -->\n <text\n x=\"20\"\n y=\"25\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#333333\"\n font-weight=\"bold\"\n >\n {{ swimlane.label }}\n </text>\n\n <!-- Edit button -->\n <g\n class=\"edit-swimlane-button\"\n [attr.transform]=\"'translate(100, 20)'\"\n (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \"\n >\n <rect\n x=\"-5\"\n y=\"-15\"\n width=\"40\"\n height=\"20\"\n fill=\"#f3e8ff\"\n rx=\"3\"\n ry=\"3\"\n stroke=\"#d8b4fe\"\n stroke-width=\"1\"\n ></rect>\n <text\n x=\"15\"\n y=\"0\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#7e22ce\"\n text-anchor=\"middle\"\n >\n Edit\n </text>\n </g>\n\n <!-- Tag indicators -->\n <g [attr.transform]=\"'translate(200, 20)'\">\n @for (tag of swimlane.tags.slice(0, 3); track tag.Name; let i = $index)\n {\n <text\n [attr.x]=\"i * 100\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n {{ tag.Name }}\n </text>\n } @if (swimlane.tags.length > 3) {\n <text\n [attr.x]=\"3 * 100 + 10\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n +{{ swimlane.tags.length - 3 }} more\n </text>\n }\n </g>\n\n <!-- Render nodes in this swimlane -->\n @for (node of swimlane.nodes; track node.id) {\n <g\n [attr.transform]=\"'translate(' + node.x + ',' + (node.y + 40) + ')'\"\n (mouseenter)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseleave)=\"hideConnectionPoints(node.id)\"\n >\n <!-- Start node indicator (circle and arrow) for the first node -->\n @if (node.isStartNode) {\n <!-- Circle -->\n <circle\n cx=\"-30\"\n cy=\"50\"\n r=\"15\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></circle>\n <!-- Form icon for start node -->\n <svg:g\n (click)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order)\n \"\n class=\"stage-icon\"\n transform=\"translate(-45, 42)\"\n style=\"cursor: pointer; pointer-events: all\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"state.workflowFormId ? '#D36CFF' : 'black'\"\n transform=\"scale(0.7)\"\n />\n </svg:g>\n <!-- Arrow from circle to node -->\n <path\n d=\"M -20 50 L 0 50\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n marker-end=\"url(#arrowhead)\"\n ></path>\n }\n\n <!-- Stage node -->\n @if (node.type === 'stage') {\n <!-- <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></rect> -->\n <svg:g\n lib-stage-node\n [node]=\"node\"\n [isStartNode]=\"node.isStartNode\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\"\n ></svg:g>\n }\n\n <!-- Decision node -->\n @if (node.type === 'decision') {\n <path\n [attr.d]=\"\n 'M 0 ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' 0' +\n ' L ' +\n node.width +\n ' ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' ' +\n node.height +\n ' Z'\n \"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></path>\n }\n\n <!-- Form node -->\n @if (node.type === 'form') {\n <path\n d=\"M95.0625 50.5591V95.0625C95.0625 97.9716 93.9069 100.762 91.8498 102.819C89.7928 104.876 87.0028 106.031 84.0938 106.031H32.9062C29.9972 106.031 27.2072 104.876 25.1502 102.819C23.0931 100.762 21.9375 97.9716 21.9375 95.0625V21.9375C21.9375 19.0284 23.0931 16.2385 25.1502 14.1814C27.2072 12.1244 29.9972 10.9688 32.9062 10.9688H55.4722C57.4109 10.969 59.2701 11.7392 60.6412 13.1099L92.9213 45.3901C94.292 46.7611 95.0622 48.6204 95.0625 50.5591Z\"\n transform=\"scale(0.7)\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M58.5 12.7969V40.2188C58.5 42.1581 59.2704 44.0181 60.6418 45.3895C62.0131 46.7608 63.8731 47.5312 65.8125 47.5312H93.2344\"\n transform=\"scale(0.7)\"\n stroke=\"#D36CFF\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <text\n x=\"50%\"\n y=\"50%\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#D36CFF\"\n >\n Form\n </text>\n }\n\n <!-- Subflow node -->\n @if (node.type === 'subflow') {\n <path\n [attr.d]=\"\n 'M 1 ' +\n node.height / 4 +\n ' L ' +\n node.width / 2 +\n ' 1 L ' +\n (node.width - 1) +\n ' ' +\n node.height / 4 +\n ' V ' +\n (node.height * 3) / 4 +\n ' L ' +\n node.width / 2 +\n ' ' +\n (node.height - 1) +\n ' L 1 ' +\n (node.height * 3) / 4 +\n ' Z'\n \"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></path>\n <text\n [attr.x]=\"node.width / 2\"\n [attr.y]=\"node.height / 2\"\n text-anchor=\"middle\"\n alignment-baseline=\"middle\"\n font-family=\"sans-serif\"\n font-size=\"10\"\n fill=\"#000000\"\n >\n <!-- Limit text length -->\n {{\n (node.workflowData?.name || \"Subflow\").length > 12\n ? (node.workflowData?.name || \"Subflow\").substring(0, 10) + \"...\"\n : node.workflowData?.name || \"Subflow\"\n }}\n </text>\n }\n\n <!-- Connection points for this node -->\n @for (point of node.connectionPoints || []; track point.id) { @if\n (isConnectionPointVisible(node.id)) {\n <use\n [attr.href]=\"'#connection-point-template'\"\n [attr.x]=\"point.x\"\n [attr.y]=\"point.y\"\n [attr.id]=\"point.id\"\n (mousedown)=\"startConnectionDrag($event, point, swimlane.order)\"\n />\n } }\n </g>\n <!-- A transparent hover area for improved hover detection -->\n <rect\n [attr.x]=\"node.x - 10\"\n [attr.y]=\"node.y + 40 - 10\"\n [attr.width]=\"node.width + 20\"\n [attr.height]=\"node.height + 20\"\n fill=\"transparent\"\n (mouseover)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseout)=\"hideConnectionPoints(node.id)\"\n style=\"pointer-events: none\"\n />\n }\n </g>\n } @for (connection of state.connections; track connection.id) {\n <g>\n <path\n [attr.d]=\"getConnectionPathForSavedConnection(connection)\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n fill=\"none\"\n marker-end=\"url(#arrowhead)\"\n ></path>\n </g>\n }\n\n <!-- Connection preview line -->\n @if (state.isConnectionDragging()) {\n <g>\n <path\n [attr.d]=\"getConnectionPath()\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n stroke-dasharray=\"5,5\"\n fill=\"none\"\n ></path>\n </g>\n }\n </svg>\n\n <div\n *ngIf=\"popupVisible\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"popupX\"\n [style.top.px]=\"popupY\"\n >\n <verben-pop-Up\n [dropdownOpen]=\"true\"\n [customStyles]=\"{ 'z-index': '99' }\"\n [enableMouseLeave]=\"false\"\n >\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md\"\n dropdown-content\n >\n <h4 class=\"mb-2 font-medium\">Create Connection</h4>\n <div class=\"flex flex-col gap-2\">\n <ng-container *ngFor=\"let type of allowedNodeTypes\">\n <button\n class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"createNodeConnection(type)\"\n >\n Create {{ type | titlecase }}\n </button>\n </ng-container>\n <button\n class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"hideConnectionPopup()\"\n >\n Cancel\n </button>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div\n *ngIf=\"showStartNodeFormPopup\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"startNodeFormPopupX\"\n [style.top.px]=\"startNodeFormPopupY\"\n >\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Workflow Form</h4>\n <div *ngIf=\"isLoadingStartNodeForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingStartNodeForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-2\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(null)\"\n >\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of startNodeFormsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(form)\"\n >\n {{ form.Name }}\n </button>\n </div>\n <div\n *ngIf=\"startNodeFormsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div\n *ngIf=\"showSubflowPopup\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"subflowPopupX\"\n [style.top.px]=\"subflowPopupY\"\n >\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Workflow</h4>\n <div *ngIf=\"isLoadingWorkflows\" class=\"text-center py-2\">\n Loading workflows...\n </div>\n <div *ngIf=\"!isLoadingWorkflows\" class=\"max-h-48 overflow-y-auto\">\n <div *ngFor=\"let workflow of workflowsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectSubflowWorkflow(workflow)\"\n >\n {{ workflow.Name }}\n </button>\n </div>\n <div\n *ngIf=\"workflowsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No workflows available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n</div>\n", styles: [".canvas-container{flex:1;overflow:auto;background-color:#fff}.designer-canvas{min-width:100%;min-height:100%;cursor:default}.designer-canvas:focus{outline:none}.edit-swimlane-button{cursor:pointer}.edit-swimlane-button:hover rect{fill:#ddd6fe}\n"], dependencies: [{ kind: "directive", type: i1$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i8.VerbenPopUpComponent, selector: "verben-pop-Up", inputs: ["dropdownOpen", "dropdownWidth", "color", "customStyles", "popUpClass", "border", "borderRadius", "enableMouseLeave"], outputs: ["dropdownOpenChange", "close"] }, { kind: "component", type: StageNodeComponent, selector: "svg:g[lib-stage-node]", inputs: ["node", "isStartNode", "stageData"], outputs: ["stagePropertiesUpdated", "parallelExecutionToggled"] }, { kind: "pipe", type: i1$1.TitleCasePipe, name: "titlecase" }] });
4095
4020
  }
4096
4021
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DesignerCanvasComponent, decorators: [{
4097
4022
  type: Component,
4098
- args: [{ selector: 'lib-designer-canvas', template: "<div class=\"canvas-container\">\n <svg\n #canvas\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"canvasHeight\"\n class=\"designer-canvas\"\n (click)=\"onCanvasClick($event)\"\n >\n <defs>\n <!-- Grid pattern definition -->\n\n <pattern\n id=\"grid\"\n [attr.width]=\"gridSize\"\n [attr.height]=\"gridSize\"\n patternUnits=\"userSpaceOnUse\"\n >\n <path\n d=\"M 20 0 L 0 0 0 20\"\n fill=\"none\"\n stroke=\"#e2e8f0\"\n stroke-width=\"0.5\"\n />\n </pattern>\n\n <!-- Arrow head marker definition -->\n <marker\n id=\"arrowhead\"\n markerWidth=\"10\"\n markerHeight=\"7\"\n refX=\"9\"\n refY=\"3.5\"\n orient=\"auto\"\n >\n <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"#D36CFF\" />\n </marker>\n\n <!-- Connection point styles -->\n <circle\n id=\"connection-point-template\"\n r=\"5\"\n fill=\"#D36CFF\"\n stroke=\"#FFFFFF\"\n stroke-width=\"1\"\n />\n\n <!-- Dashed line style for connection preview -->\n <pattern\n id=\"dashed-line\"\n width=\"10\"\n height=\"10\"\n patternUnits=\"userSpaceOnUse\"\n >\n <line\n x1=\"0\"\n y1=\"5\"\n x2=\"10\"\n y2=\"5\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n stroke-dasharray=\"5,5\"\n />\n </pattern>\n </defs>\n\n <!-- Background grid -->\n <rect width=\"100%\" height=\"100%\" fill=\"url(#grid)\" />\n\n <!-- Display a message when no swimlanes exist -->\n @if (state.swimlanes.length === 0) {\n <text\n x=\"50%\"\n y=\"50%\"\n font-family=\"sans-serif\"\n font-size=\"16\"\n fill=\"#94a3b8\"\n text-anchor=\"middle\"\n >\n Select the Swimlane tool and click on the canvas to add a swimlane\n </text>\n }\n\n <!-- This is where workflow elements will be added later -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <g [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\">\n <!-- Swimlane container -->\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"263\"\n fill=\"#ffffff\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <!-- Swimlane header -->\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n height=\"40\"\n fill=\"#f8fafc\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <!-- Swimlane label -->\n <text\n x=\"20\"\n y=\"25\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#333333\"\n font-weight=\"bold\"\n >\n {{ swimlane.label }}\n </text>\n\n <!-- Edit button -->\n <g\n class=\"edit-swimlane-button\"\n [attr.transform]=\"'translate(200, 20)'\"\n (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \"\n >\n <rect\n x=\"-5\"\n y=\"-15\"\n width=\"40\"\n height=\"20\"\n fill=\"#f3e8ff\"\n rx=\"3\"\n ry=\"3\"\n stroke=\"#d8b4fe\"\n stroke-width=\"1\"\n ></rect>\n <text\n x=\"15\"\n y=\"0\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#7e22ce\"\n text-anchor=\"middle\"\n >\n Edit\n </text>\n </g>\n\n <!-- Tag indicators -->\n <g [attr.transform]=\"'translate(200, 20)'\">\n @for (tag of swimlane.tags.slice(0, 3); track tag.Name; let i = $index)\n {\n <text\n [attr.x]=\"i * 100\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n {{ tag.Name }}\n </text>\n } @if (swimlane.tags.length > 3) {\n <text\n [attr.x]=\"3 * 100 + 10\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n +{{ swimlane.tags.length - 3 }} more\n </text>\n }\n </g>\n\n <!-- Render nodes in this swimlane -->\n @for (node of swimlane.nodes; track node.id) {\n <g\n [attr.transform]=\"'translate(' + node.x + ',' + (node.y + 40) + ')'\"\n (mouseenter)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseleave)=\"hideConnectionPoints(node.id)\"\n >\n <!-- Start node indicator (circle and arrow) for the first node -->\n @if (node.isStartNode) {\n <!-- Circle -->\n <circle\n cx=\"-30\"\n cy=\"50\"\n r=\"15\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></circle>\n <!-- Form icon for start node -->\n <svg:g\n (click)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order)\n \"\n class=\"stage-icon\"\n transform=\"translate(-45, 42)\"\n style=\"cursor: pointer; pointer-events: all\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"state.workflowFormId ? '#D36CFF' : 'black'\"\n transform=\"scale(0.7)\"\n />\n </svg:g>\n <!-- Arrow from circle to node -->\n <path\n d=\"M -20 50 L 0 50\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n marker-end=\"url(#arrowhead)\"\n ></path>\n }\n\n <!-- Stage node -->\n @if (node.type === 'stage') {\n <!-- <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></rect> -->\n <svg:g\n lib-stage-node\n [node]=\"node\"\n [isStartNode]=\"node.isStartNode\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\"\n ></svg:g>\n }\n\n <!-- Decision node -->\n @if (node.type === 'decision') {\n <path\n [attr.d]=\"\n 'M 0 ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' 0' +\n ' L ' +\n node.width +\n ' ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' ' +\n node.height +\n ' Z'\n \"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></path>\n }\n\n <!-- Form node -->\n @if (node.type === 'form') {\n <path\n d=\"M95.0625 50.5591V95.0625C95.0625 97.9716 93.9069 100.762 91.8498 102.819C89.7928 104.876 87.0028 106.031 84.0938 106.031H32.9062C29.9972 106.031 27.2072 104.876 25.1502 102.819C23.0931 100.762 21.9375 97.9716 21.9375 95.0625V21.9375C21.9375 19.0284 23.0931 16.2385 25.1502 14.1814C27.2072 12.1244 29.9972 10.9688 32.9062 10.9688H55.4722C57.4109 10.969 59.2701 11.7392 60.6412 13.1099L92.9213 45.3901C94.292 46.7611 95.0622 48.6204 95.0625 50.5591Z\"\n transform=\"scale(0.7)\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M58.5 12.7969V40.2188C58.5 42.1581 59.2704 44.0181 60.6418 45.3895C62.0131 46.7608 63.8731 47.5312 65.8125 47.5312H93.2344\"\n transform=\"scale(0.7)\"\n stroke=\"#D36CFF\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <text\n x=\"50%\"\n y=\"50%\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#D36CFF\"\n >\n Form\n </text>\n }\n\n <!-- Subflow node -->\n @if (node.type === 'subflow') {\n <path\n [attr.d]=\"\n 'M 1 ' +\n node.height / 4 +\n ' L ' +\n node.width / 2 +\n ' 1 L ' +\n (node.width - 1) +\n ' ' +\n node.height / 4 +\n ' V ' +\n (node.height * 3) / 4 +\n ' L ' +\n node.width / 2 +\n ' ' +\n (node.height - 1) +\n ' L 1 ' +\n (node.height * 3) / 4 +\n ' Z'\n \"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></path>\n <text\n x=\"50%\"\n y=\"50%\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#000000\"\n >\n {{ node.workflowData?.name || \"Subflow\" }}\n </text>\n }\n\n <!-- Connection points for this node -->\n @for (point of node.connectionPoints || []; track point.id) { @if\n (isConnectionPointVisible(node.id)) {\n <use\n [attr.href]=\"'#connection-point-template'\"\n [attr.x]=\"point.x\"\n [attr.y]=\"point.y\"\n [attr.id]=\"point.id\"\n (mousedown)=\"startConnectionDrag($event, point, swimlane.order)\"\n />\n } }\n </g>\n <!-- A transparent hover area for improved hover detection -->\n <rect\n [attr.x]=\"node.x - 10\"\n [attr.y]=\"node.y + 40 - 10\"\n [attr.width]=\"node.width + 20\"\n [attr.height]=\"node.height + 20\"\n fill=\"transparent\"\n (mouseover)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseout)=\"hideConnectionPoints(node.id)\"\n style=\"pointer-events: none\"\n />\n }\n </g>\n } @for (connection of state.connections; track connection.id) {\n <g>\n <path\n [attr.d]=\"getConnectionPathForSavedConnection(connection)\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n fill=\"none\"\n marker-end=\"url(#arrowhead)\"\n ></path>\n </g>\n }\n\n <!-- Connection preview line -->\n @if (state.isConnectionDragging()) {\n <g>\n <path\n [attr.d]=\"getConnectionPath()\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n stroke-dasharray=\"5,5\"\n fill=\"none\"\n ></path>\n </g>\n }\n </svg>\n\n <div\n *ngIf=\"popupVisible\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"popupX\"\n [style.top.px]=\"popupY\"\n >\n <verben-pop-Up\n [dropdownOpen]=\"true\"\n [customStyles]=\"{ 'z-index': '99' }\"\n [enableMouseLeave]=\"false\"\n >\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md\"\n dropdown-content\n >\n <h4 class=\"mb-2 font-medium\">Create Connection</h4>\n <div class=\"flex flex-col gap-2\">\n <ng-container *ngFor=\"let type of allowedNodeTypes\">\n <button\n class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"createNodeConnection(type)\"\n >\n Create {{ type | titlecase }}\n </button>\n </ng-container>\n <button\n class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"hideConnectionPopup()\"\n >\n Cancel\n </button>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div\n *ngIf=\"showStartNodeFormPopup\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"startNodeFormPopupX\"\n [style.top.px]=\"startNodeFormPopupY\"\n >\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Workflow Form</h4>\n <div *ngIf=\"isLoadingStartNodeForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingStartNodeForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-2\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(null)\"\n >\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of startNodeFormsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(form)\"\n >\n {{ form.Name }}\n </button>\n </div>\n <div\n *ngIf=\"startNodeFormsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div\n *ngIf=\"showSubflowPopup\"\n [style.position]=\"'fiabsolutexed'\"\n [style.left.px]=\"subflowPopupX\"\n [style.top.px]=\"subflowPopupY\"\n >\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Workflow</h4>\n <div *ngIf=\"isLoadingWorkflows\" class=\"text-center py-2\">\n Loading workflows...\n </div>\n <div *ngIf=\"!isLoadingWorkflows\" class=\"max-h-48 overflow-y-auto\">\n <div *ngFor=\"let workflow of workflowsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectSubflowWorkflow(workflow)\"\n >\n {{ workflow.Name }}\n </button>\n </div>\n <div\n *ngIf=\"workflowsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No workflows available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n</div>\n", styles: [".canvas-container{flex:1;overflow:auto;background-color:#fff}.designer-canvas{min-width:100%;min-height:100%;cursor:default}.designer-canvas:focus{outline:none}.edit-swimlane-button{cursor:pointer}.edit-swimlane-button:hover rect{fill:#ddd6fe}\n"] }]
4023
+ args: [{ selector: 'lib-designer-canvas', template: "<div class=\"canvas-container\">\n <svg\n #canvas\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"canvasHeight\"\n class=\"designer-canvas\"\n (click)=\"onCanvasClick($event)\"\n >\n <defs>\n <!-- Grid pattern definition -->\n\n <pattern\n id=\"grid\"\n [attr.width]=\"gridSize\"\n [attr.height]=\"gridSize\"\n patternUnits=\"userSpaceOnUse\"\n >\n <path\n d=\"M 20 0 L 0 0 0 20\"\n fill=\"none\"\n stroke=\"#e2e8f0\"\n stroke-width=\"0.5\"\n />\n </pattern>\n\n <!-- Arrow head marker definition -->\n <marker\n id=\"arrowhead\"\n markerWidth=\"10\"\n markerHeight=\"7\"\n refX=\"9\"\n refY=\"3.5\"\n orient=\"auto\"\n >\n <polygon points=\"0 0, 10 3.5, 0 7\" fill=\"#D36CFF\" />\n </marker>\n\n <!-- Connection point styles -->\n <circle\n id=\"connection-point-template\"\n r=\"5\"\n fill=\"#D36CFF\"\n stroke=\"#FFFFFF\"\n stroke-width=\"1\"\n />\n\n <!-- Dashed line style for connection preview -->\n <pattern\n id=\"dashed-line\"\n width=\"10\"\n height=\"10\"\n patternUnits=\"userSpaceOnUse\"\n >\n <line\n x1=\"0\"\n y1=\"5\"\n x2=\"10\"\n y2=\"5\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n stroke-dasharray=\"5,5\"\n />\n </pattern>\n </defs>\n\n <!-- Background grid -->\n <rect width=\"100%\" height=\"100%\" fill=\"url(#grid)\" />\n\n <!-- Display a message when no swimlanes exist -->\n @if (state.swimlanes.length === 0) {\n <text\n x=\"50%\"\n y=\"50%\"\n font-family=\"sans-serif\"\n font-size=\"16\"\n fill=\"#94a3b8\"\n text-anchor=\"middle\"\n >\n Select the Swimlane tool and click on the canvas to add a swimlane\n </text>\n }\n\n <!-- This is where workflow elements will be added later -->\n @for (swimlane of state.swimlanes; track swimlane.order) {\n <g [attr.transform]=\"'translate(0,' + swimlane.order * 263 + ')'\">\n <!-- Swimlane container -->\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n [attr.height]=\"263\"\n fill=\"#ffffff\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <!-- Swimlane header -->\n <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"canvasWidth\"\n height=\"40\"\n fill=\"#f8fafc\"\n stroke=\"#e2e8f0\"\n stroke-width=\"1\"\n ></rect>\n\n <!-- Swimlane label -->\n <text\n x=\"20\"\n y=\"25\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#333333\"\n font-weight=\"bold\"\n >\n {{ swimlane.label }}\n </text>\n\n <!-- Edit button -->\n <g\n class=\"edit-swimlane-button\"\n [attr.transform]=\"'translate(100, 20)'\"\n (click)=\"\n onEditSwimlane($event, swimlane, swimlane.order);\n $event.stopPropagation()\n \"\n >\n <rect\n x=\"-5\"\n y=\"-15\"\n width=\"40\"\n height=\"20\"\n fill=\"#f3e8ff\"\n rx=\"3\"\n ry=\"3\"\n stroke=\"#d8b4fe\"\n stroke-width=\"1\"\n ></rect>\n <text\n x=\"15\"\n y=\"0\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#7e22ce\"\n text-anchor=\"middle\"\n >\n Edit\n </text>\n </g>\n\n <!-- Tag indicators -->\n <g [attr.transform]=\"'translate(200, 20)'\">\n @for (tag of swimlane.tags.slice(0, 3); track tag.Name; let i = $index)\n {\n <text\n [attr.x]=\"i * 100\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n {{ tag.Name }}\n </text>\n } @if (swimlane.tags.length > 3) {\n <text\n [attr.x]=\"3 * 100 + 10\"\n y=\"5\"\n font-family=\"sans-serif\"\n font-size=\"12\"\n fill=\"#666666\"\n >\n +{{ swimlane.tags.length - 3 }} more\n </text>\n }\n </g>\n\n <!-- Render nodes in this swimlane -->\n @for (node of swimlane.nodes; track node.id) {\n <g\n [attr.transform]=\"'translate(' + node.x + ',' + (node.y + 40) + ')'\"\n (mouseenter)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseleave)=\"hideConnectionPoints(node.id)\"\n >\n <!-- Start node indicator (circle and arrow) for the first node -->\n @if (node.isStartNode) {\n <!-- Circle -->\n <circle\n cx=\"-30\"\n cy=\"50\"\n r=\"15\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></circle>\n <!-- Form icon for start node -->\n <svg:g\n (click)=\"\n toggleStartNodeFormPopup($event, node.x, node.y, swimlane.order)\n \"\n class=\"stage-icon\"\n transform=\"translate(-45, 42)\"\n style=\"cursor: pointer; pointer-events: all\"\n >\n <!-- Transparent rectangle to capture clicks -->\n <svg:rect\n x=\"-2\"\n y=\"-2\"\n width=\"24\"\n height=\"24\"\n fill=\"transparent\"\n style=\"cursor: pointer\"\n />\n <svg:path\n d=\"M16.5 20.475V17.475H13.5V16.475H16.5V13.475H17.5V16.475H20.5V17.475H17.5V20.475H16.5ZM3.5 17.5V16.5H4.5V17.5H3.5ZM6.5 17.5V16.5H11.517C11.5057 16.6767 11.5043 16.845 11.513 17.005C11.521 17.165 11.531 17.33 11.543 17.5H6.5ZM3.5 13.5V12.5H4.5V13.5H3.5ZM6.5 13.5V12.5H13.804C13.6127 12.6387 13.4333 12.7913 13.266 12.958C13.0993 13.1247 12.9377 13.3053 12.781 13.5H6.5ZM3.5 9.5V8.5H4.5V9.5H3.5ZM6.5 9.5V8.5H18.5V9.5H6.5ZM3.5 5.5V4.5H4.5V5.5H3.5ZM6.5 5.5V4.5H18.5V5.5H6.5Z\"\n [attr.fill]=\"state.workflowFormId ? '#D36CFF' : 'black'\"\n transform=\"scale(0.7)\"\n />\n </svg:g>\n <!-- Arrow from circle to node -->\n <path\n d=\"M -20 50 L 0 50\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n marker-end=\"url(#arrowhead)\"\n ></path>\n }\n\n <!-- Stage node -->\n @if (node.type === 'stage') {\n <!-- <rect\n x=\"0\"\n y=\"0\"\n [attr.width]=\"node.width\"\n [attr.height]=\"node.height\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></rect> -->\n <svg:g\n lib-stage-node\n [node]=\"node\"\n [isStartNode]=\"node.isStartNode\"\n (stagePropertiesUpdated)=\"onStagePropertiesUpdated($event)\"\n ></svg:g>\n }\n\n <!-- Decision node -->\n @if (node.type === 'decision') {\n <path\n [attr.d]=\"\n 'M 0 ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' 0' +\n ' L ' +\n node.width +\n ' ' +\n node.height / 2 +\n ' L ' +\n node.width / 2 +\n ' ' +\n node.height +\n ' Z'\n \"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></path>\n }\n\n <!-- Form node -->\n @if (node.type === 'form') {\n <path\n d=\"M95.0625 50.5591V95.0625C95.0625 97.9716 93.9069 100.762 91.8498 102.819C89.7928 104.876 87.0028 106.031 84.0938 106.031H32.9062C29.9972 106.031 27.2072 104.876 25.1502 102.819C23.0931 100.762 21.9375 97.9716 21.9375 95.0625V21.9375C21.9375 19.0284 23.0931 16.2385 25.1502 14.1814C27.2072 12.1244 29.9972 10.9688 32.9062 10.9688H55.4722C57.4109 10.969 59.2701 11.7392 60.6412 13.1099L92.9213 45.3901C94.292 46.7611 95.0622 48.6204 95.0625 50.5591Z\"\n transform=\"scale(0.7)\"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-linejoin=\"round\"\n />\n <path\n d=\"M58.5 12.7969V40.2188C58.5 42.1581 59.2704 44.0181 60.6418 45.3895C62.0131 46.7608 63.8731 47.5312 65.8125 47.5312H93.2344\"\n transform=\"scale(0.7)\"\n stroke=\"#D36CFF\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n <text\n x=\"50%\"\n y=\"50%\"\n text-anchor=\"middle\"\n dominant-baseline=\"middle\"\n font-family=\"sans-serif\"\n font-size=\"14\"\n fill=\"#D36CFF\"\n >\n Form\n </text>\n }\n\n <!-- Subflow node -->\n @if (node.type === 'subflow') {\n <path\n [attr.d]=\"\n 'M 1 ' +\n node.height / 4 +\n ' L ' +\n node.width / 2 +\n ' 1 L ' +\n (node.width - 1) +\n ' ' +\n node.height / 4 +\n ' V ' +\n (node.height * 3) / 4 +\n ' L ' +\n node.width / 2 +\n ' ' +\n (node.height - 1) +\n ' L 1 ' +\n (node.height * 3) / 4 +\n ' Z'\n \"\n fill=\"none\"\n stroke=\"#D36CFF\"\n stroke-width=\"1\"\n ></path>\n <text\n [attr.x]=\"node.width / 2\"\n [attr.y]=\"node.height / 2\"\n text-anchor=\"middle\"\n alignment-baseline=\"middle\"\n font-family=\"sans-serif\"\n font-size=\"10\"\n fill=\"#000000\"\n >\n <!-- Limit text length -->\n {{\n (node.workflowData?.name || \"Subflow\").length > 12\n ? (node.workflowData?.name || \"Subflow\").substring(0, 10) + \"...\"\n : node.workflowData?.name || \"Subflow\"\n }}\n </text>\n }\n\n <!-- Connection points for this node -->\n @for (point of node.connectionPoints || []; track point.id) { @if\n (isConnectionPointVisible(node.id)) {\n <use\n [attr.href]=\"'#connection-point-template'\"\n [attr.x]=\"point.x\"\n [attr.y]=\"point.y\"\n [attr.id]=\"point.id\"\n (mousedown)=\"startConnectionDrag($event, point, swimlane.order)\"\n />\n } }\n </g>\n <!-- A transparent hover area for improved hover detection -->\n <rect\n [attr.x]=\"node.x - 10\"\n [attr.y]=\"node.y + 40 - 10\"\n [attr.width]=\"node.width + 20\"\n [attr.height]=\"node.height + 20\"\n fill=\"transparent\"\n (mouseover)=\"showConnectionPoints(node.id, swimlane.order)\"\n (mouseout)=\"hideConnectionPoints(node.id)\"\n style=\"pointer-events: none\"\n />\n }\n </g>\n } @for (connection of state.connections; track connection.id) {\n <g>\n <path\n [attr.d]=\"getConnectionPathForSavedConnection(connection)\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n fill=\"none\"\n marker-end=\"url(#arrowhead)\"\n ></path>\n </g>\n }\n\n <!-- Connection preview line -->\n @if (state.isConnectionDragging()) {\n <g>\n <path\n [attr.d]=\"getConnectionPath()\"\n stroke=\"#D36CFF\"\n stroke-width=\"2\"\n stroke-dasharray=\"5,5\"\n fill=\"none\"\n ></path>\n </g>\n }\n </svg>\n\n <div\n *ngIf=\"popupVisible\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"popupX\"\n [style.top.px]=\"popupY\"\n >\n <verben-pop-Up\n [dropdownOpen]=\"true\"\n [customStyles]=\"{ 'z-index': '99' }\"\n [enableMouseLeave]=\"false\"\n >\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md\"\n dropdown-content\n >\n <h4 class=\"mb-2 font-medium\">Create Connection</h4>\n <div class=\"flex flex-col gap-2\">\n <ng-container *ngFor=\"let type of allowedNodeTypes\">\n <button\n class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"createNodeConnection(type)\"\n >\n Create {{ type | titlecase }}\n </button>\n </ng-container>\n <button\n class=\"px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50\"\n (click)=\"hideConnectionPopup()\"\n >\n Cancel\n </button>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div\n *ngIf=\"showStartNodeFormPopup\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"startNodeFormPopupX\"\n [style.top.px]=\"startNodeFormPopupY\"\n >\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Workflow Form</h4>\n <div *ngIf=\"isLoadingStartNodeForms\" class=\"text-center py-2\">\n Loading forms...\n </div>\n <div *ngIf=\"!isLoadingStartNodeForms\" class=\"max-h-48 overflow-y-auto\">\n <div class=\"mb-2\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(null)\"\n >\n Clear form selection\n </button>\n </div>\n <div *ngFor=\"let form of startNodeFormsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectStartNodeForm(form)\"\n >\n {{ form.Name }}\n </button>\n </div>\n <div\n *ngIf=\"startNodeFormsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No forms available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n\n <div\n *ngIf=\"showSubflowPopup\"\n [style.position]=\"'absolute'\"\n [style.left.px]=\"subflowPopupX\"\n [style.top.px]=\"subflowPopupY\"\n >\n <verben-pop-Up [dropdownOpen]=\"true\" [customStyles]=\"{ 'z-index': '100' }\">\n <div dropdown-trigger style=\"display: none\">\n <!-- Hidden trigger element -->\n </div>\n <div\n class=\"p-3 bg-white border border-gray-200 rounded shadow-md w-64\"\n dropdown-content\n style=\"background-color: white; z-index: 1000\"\n >\n <h4 class=\"mb-2 font-medium\">Select Workflow</h4>\n <div *ngIf=\"isLoadingWorkflows\" class=\"text-center py-2\">\n Loading workflows...\n </div>\n <div *ngIf=\"!isLoadingWorkflows\" class=\"max-h-48 overflow-y-auto\">\n <div *ngFor=\"let workflow of workflowsList\" class=\"mb-1\">\n <button\n class=\"w-full px-3 py-2 bg-white border border-gray-200 rounded text-sm hover:bg-gray-50 text-left\"\n (click)=\"selectSubflowWorkflow(workflow)\"\n >\n {{ workflow.Name }}\n </button>\n </div>\n <div\n *ngIf=\"workflowsList.length === 0\"\n class=\"text-center py-2 text-gray-500\"\n >\n No workflows available\n </div>\n </div>\n </div>\n </verben-pop-Up>\n </div>\n</div>\n", styles: [".canvas-container{flex:1;overflow:auto;background-color:#fff}.designer-canvas{min-width:100%;min-height:100%;cursor:default}.designer-canvas:focus{outline:none}.edit-swimlane-button{cursor:pointer}.edit-swimlane-button:hover rect{fill:#ddd6fe}\n"] }]
4099
4024
  }], ctorParameters: () => [{ type: WorkflowDesignerState }, { type: WorkflowDataService }], propDecorators: { selectedTool: [{
4100
4025
  type: Input
4101
4026
  }], clickedPosition: [{
@@ -4139,6 +4064,7 @@ class SwimlaneDialogComponent {
4139
4064
  // Reset form fields when dialog opens
4140
4065
  this.selectedTagNames = [];
4141
4066
  this.workflowName = '';
4067
+ this.searchQuery = '';
4142
4068
  this.dataService.getTags().then((data) => {
4143
4069
  this.tags = data.Result;
4144
4070
  // If editing an existing swimlane, populate the form
@@ -4184,6 +4110,9 @@ class SwimlaneDialogComponent {
4184
4110
  }
4185
4111
  onDialogClose(eventData) {
4186
4112
  console.log('Dialog closed, received data:', eventData);
4113
+ this.searchQuery = '';
4114
+ this.workflowName = '';
4115
+ this.selectedTagNames = [];
4187
4116
  this.closed.emit();
4188
4117
  }
4189
4118
  onDialogOpen(eventData) {
@@ -4278,7 +4207,10 @@ class WorkflowDesignerComponent {
4278
4207
  .getWorkflowWithParam(code)
4279
4208
  .then((response) => {
4280
4209
  if (response && response.Result) {
4281
- this.parseWorkflowData(response.Result);
4210
+ // Store the workflow ID
4211
+ const wf = response.Result[0];
4212
+ this.state.setWorkflowId(wf.Id);
4213
+ this.parseWorkflowData(wf);
4282
4214
  }
4283
4215
  this.isLoading = false;
4284
4216
  })
@@ -4325,6 +4257,29 @@ class WorkflowDesignerComponent {
4325
4257
  });
4326
4258
  }
4327
4259
  });
4260
+ // Register the workflow itself
4261
+ this.state.registerLoadedObject(workflow.Id, workflow.Code);
4262
+ // Register swimlanes when processing
4263
+ if (workflow.Lanes && workflow.Lanes.length) {
4264
+ workflow.Lanes.forEach((lane) => {
4265
+ this.state.registerLoadedObject(lane.Id, lane.Code);
4266
+ // Existing lane processing code...
4267
+ });
4268
+ }
4269
+ // Register stages when processing
4270
+ if (workflow.Stages && workflow.Stages.length) {
4271
+ workflow.Stages.forEach((stage) => {
4272
+ this.state.registerLoadedObject(stage.Id, stage.Code);
4273
+ // Existing stage processing code...
4274
+ });
4275
+ }
4276
+ // Register actions/connections when processing
4277
+ if (workflow.Actions && workflow.Actions.length) {
4278
+ workflow.Actions.forEach((action) => {
4279
+ this.state.registerLoadedObject(action.Id, action.Code);
4280
+ // Existing action processing code...
4281
+ });
4282
+ }
4328
4283
  }
4329
4284
  // Process connections/actions
4330
4285
  if (workflow.Actions && workflow.Actions.length) {
@@ -4526,11 +4481,11 @@ class WorkflowDesignerComponent {
4526
4481
  this.selectedTool = null;
4527
4482
  }
4528
4483
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDesignerComponent, deps: [{ token: WorkflowDesignerState }, { token: WorkflowDataService }], target: i0.ɵɵFactoryTarget.Component });
4529
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: WorkflowDesignerComponent, selector: "lib-workflow-designer", inputs: { workflowCode: "workflowCode" }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvasRef"], descendants: true }], ngImport: i0, template: "<div class=\"workflow-designer\">\n <div class=\"workflow-header\">\n <h1>Workflow Designer</h1>\n </div>\n\n <!-- Toolbar Component -->\n <lib-designer-toolbar\n [selectedTool]=\"selectedTool\"\n (toolSelected)=\"onToolSelected($event)\"\n >\n </lib-designer-toolbar>\n\n <!-- Show loading indicator if needed -->\n <div *ngIf=\"isLoading\" class=\"loading-overlay\">\n <div class=\"loading-spinner\"></div>\n <div class=\"loading-text\">Loading workflow...</div>\n </div>\n\n <!-- Canvas Component -->\n <lib-designer-canvas\n [selectedTool]=\"selectedTool\"\n (clickedPosition)=\"handleCanvasPositionClick($event)\"\n (subflowSelected)=\"onSubflowSelected()\"\n (showStageDialog)=\"onShowStageDialog($event)\"\n #canvasRef\n >\n </lib-designer-canvas>\n\n <lib-swimlane-dialog\n [visible]=\"showSwimlaneDialog\"\n [swimlaneData]=\"\n editingSwimlaneIndex !== null\n ? {\n name: state.swimlanes[editingSwimlaneIndex].label,\n tags: state.swimlanes[editingSwimlaneIndex].tags\n }\n : null\n \"\n (created)=\"onSwimlaneDialogFilled($event)\"\n (closed)=\"showSwimlaneDialog = false\"\n ></lib-swimlane-dialog>\n\n <lib-stage-dialog\n [visible]=\"showStageDialog\"\n [stageData]=\"pendingStageData\"\n (closed)=\"showStageDialog = false\"\n (saved)=\"onStageDialogSaved($event)\"\n ></lib-stage-dialog>\n</div>\n", styles: [".workflow-designer{display:flex;flex-direction:column;height:100vh;background-color:#f8fafc}.workflow-header{padding:1rem;background-color:#fff;border-bottom:1px solid #e2e8f0}.workflow-header h1{font-size:1.25rem;font-weight:600;color:#334155;margin:0}.loading-overlay{position:absolute;inset:0;background-color:#ffffffb3;display:flex;flex-direction:column;justify-content:center;align-items:center;z-index:1000}.loading-spinner{border:4px solid #f3f3f3;border-top:4px solid #d8b4fe;border-radius:50%;width:40px;height:40px;animation:spin 1s linear infinite}.loading-text{margin-top:1rem;font-size:1rem;color:#7e22ce}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DesignerToolbarComponent, selector: "lib-designer-toolbar", inputs: ["selectedTool", "isSaving"], outputs: ["toolSelected", "saveWorkflow"] }, { kind: "component", type: DesignerCanvasComponent, selector: "lib-designer-canvas", inputs: ["selectedTool"], outputs: ["clickedPosition", "subflowSelected", "showStageDialog"] }, { kind: "component", type: SwimlaneDialogComponent, selector: "lib-swimlane-dialog", inputs: ["visible", "swimlaneData"], outputs: ["closed", "created"] }, { kind: "component", type: StageDialogComponent, selector: "lib-stage-dialog", inputs: ["visible", "stageData"], outputs: ["closed", "saved"] }] });
4484
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: WorkflowDesignerComponent, selector: "lib-workflow-designer", inputs: { workflowCode: "workflowCode" }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvasRef"], descendants: true }], ngImport: i0, template: "<div class=\"workflow-designer\">\n <div class=\"workflow-header\">\n <h1>Workflow Designer</h1>\n </div>\n\n <!-- Toolbar Component -->\n <lib-designer-toolbar\n [selectedTool]=\"selectedTool\"\n [isSaving]=\"isSaving\"\n (toolSelected)=\"onToolSelected($event)\"\n (saveWorkflow)=\"saveWorkflow()\"\n >\n </lib-designer-toolbar>\n\n <!-- Show loading indicator if needed -->\n <div *ngIf=\"isLoading\" class=\"loading-overlay\">\n <div class=\"loading-spinner\"></div>\n <div class=\"loading-text\">Loading workflow...</div>\n </div>\n\n <!-- Canvas Component -->\n <lib-designer-canvas\n [selectedTool]=\"selectedTool\"\n (clickedPosition)=\"handleCanvasPositionClick($event)\"\n (subflowSelected)=\"onSubflowSelected()\"\n (showStageDialog)=\"onShowStageDialog($event)\"\n #canvasRef\n >\n </lib-designer-canvas>\n\n <lib-swimlane-dialog\n [visible]=\"showSwimlaneDialog\"\n [swimlaneData]=\"\n editingSwimlaneIndex !== null\n ? {\n name: state.swimlanes[editingSwimlaneIndex].label,\n tags: state.swimlanes[editingSwimlaneIndex].tags\n }\n : null\n \"\n (created)=\"onSwimlaneDialogFilled($event)\"\n (closed)=\"showSwimlaneDialog = false\"\n ></lib-swimlane-dialog>\n\n <lib-stage-dialog\n [visible]=\"showStageDialog\"\n [stageData]=\"pendingStageData\"\n (closed)=\"showStageDialog = false\"\n (saved)=\"onStageDialogSaved($event)\"\n ></lib-stage-dialog>\n</div>\n", styles: [".workflow-designer{display:flex;flex-direction:column;height:100vh;background-color:#f8fafc}.workflow-header{padding:1rem;background-color:#fff;border-bottom:1px solid #e2e8f0}.workflow-header h1{font-size:1.25rem;font-weight:600;color:#334155;margin:0}.loading-overlay{position:absolute;inset:0;background-color:#ffffffb3;display:flex;flex-direction:column;justify-content:center;align-items:center;z-index:1000}.loading-spinner{border:4px solid #f3f3f3;border-top:4px solid #d8b4fe;border-radius:50%;width:40px;height:40px;animation:spin 1s linear infinite}.loading-text{margin-top:1rem;font-size:1rem;color:#7e22ce}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: i1$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DesignerToolbarComponent, selector: "lib-designer-toolbar", inputs: ["selectedTool", "isSaving"], outputs: ["toolSelected", "saveWorkflow"] }, { kind: "component", type: DesignerCanvasComponent, selector: "lib-designer-canvas", inputs: ["selectedTool"], outputs: ["clickedPosition", "subflowSelected", "showStageDialog"] }, { kind: "component", type: SwimlaneDialogComponent, selector: "lib-swimlane-dialog", inputs: ["visible", "swimlaneData"], outputs: ["closed", "created"] }, { kind: "component", type: StageDialogComponent, selector: "lib-stage-dialog", inputs: ["visible", "stageData"], outputs: ["closed", "saved"] }] });
4530
4485
  }
4531
4486
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDesignerComponent, decorators: [{
4532
4487
  type: Component,
4533
- args: [{ selector: 'lib-workflow-designer', template: "<div class=\"workflow-designer\">\n <div class=\"workflow-header\">\n <h1>Workflow Designer</h1>\n </div>\n\n <!-- Toolbar Component -->\n <lib-designer-toolbar\n [selectedTool]=\"selectedTool\"\n (toolSelected)=\"onToolSelected($event)\"\n >\n </lib-designer-toolbar>\n\n <!-- Show loading indicator if needed -->\n <div *ngIf=\"isLoading\" class=\"loading-overlay\">\n <div class=\"loading-spinner\"></div>\n <div class=\"loading-text\">Loading workflow...</div>\n </div>\n\n <!-- Canvas Component -->\n <lib-designer-canvas\n [selectedTool]=\"selectedTool\"\n (clickedPosition)=\"handleCanvasPositionClick($event)\"\n (subflowSelected)=\"onSubflowSelected()\"\n (showStageDialog)=\"onShowStageDialog($event)\"\n #canvasRef\n >\n </lib-designer-canvas>\n\n <lib-swimlane-dialog\n [visible]=\"showSwimlaneDialog\"\n [swimlaneData]=\"\n editingSwimlaneIndex !== null\n ? {\n name: state.swimlanes[editingSwimlaneIndex].label,\n tags: state.swimlanes[editingSwimlaneIndex].tags\n }\n : null\n \"\n (created)=\"onSwimlaneDialogFilled($event)\"\n (closed)=\"showSwimlaneDialog = false\"\n ></lib-swimlane-dialog>\n\n <lib-stage-dialog\n [visible]=\"showStageDialog\"\n [stageData]=\"pendingStageData\"\n (closed)=\"showStageDialog = false\"\n (saved)=\"onStageDialogSaved($event)\"\n ></lib-stage-dialog>\n</div>\n", styles: [".workflow-designer{display:flex;flex-direction:column;height:100vh;background-color:#f8fafc}.workflow-header{padding:1rem;background-color:#fff;border-bottom:1px solid #e2e8f0}.workflow-header h1{font-size:1.25rem;font-weight:600;color:#334155;margin:0}.loading-overlay{position:absolute;inset:0;background-color:#ffffffb3;display:flex;flex-direction:column;justify-content:center;align-items:center;z-index:1000}.loading-spinner{border:4px solid #f3f3f3;border-top:4px solid #d8b4fe;border-radius:50%;width:40px;height:40px;animation:spin 1s linear infinite}.loading-text{margin-top:1rem;font-size:1rem;color:#7e22ce}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
4488
+ args: [{ selector: 'lib-workflow-designer', template: "<div class=\"workflow-designer\">\n <div class=\"workflow-header\">\n <h1>Workflow Designer</h1>\n </div>\n\n <!-- Toolbar Component -->\n <lib-designer-toolbar\n [selectedTool]=\"selectedTool\"\n [isSaving]=\"isSaving\"\n (toolSelected)=\"onToolSelected($event)\"\n (saveWorkflow)=\"saveWorkflow()\"\n >\n </lib-designer-toolbar>\n\n <!-- Show loading indicator if needed -->\n <div *ngIf=\"isLoading\" class=\"loading-overlay\">\n <div class=\"loading-spinner\"></div>\n <div class=\"loading-text\">Loading workflow...</div>\n </div>\n\n <!-- Canvas Component -->\n <lib-designer-canvas\n [selectedTool]=\"selectedTool\"\n (clickedPosition)=\"handleCanvasPositionClick($event)\"\n (subflowSelected)=\"onSubflowSelected()\"\n (showStageDialog)=\"onShowStageDialog($event)\"\n #canvasRef\n >\n </lib-designer-canvas>\n\n <lib-swimlane-dialog\n [visible]=\"showSwimlaneDialog\"\n [swimlaneData]=\"\n editingSwimlaneIndex !== null\n ? {\n name: state.swimlanes[editingSwimlaneIndex].label,\n tags: state.swimlanes[editingSwimlaneIndex].tags\n }\n : null\n \"\n (created)=\"onSwimlaneDialogFilled($event)\"\n (closed)=\"showSwimlaneDialog = false\"\n ></lib-swimlane-dialog>\n\n <lib-stage-dialog\n [visible]=\"showStageDialog\"\n [stageData]=\"pendingStageData\"\n (closed)=\"showStageDialog = false\"\n (saved)=\"onStageDialogSaved($event)\"\n ></lib-stage-dialog>\n</div>\n", styles: [".workflow-designer{display:flex;flex-direction:column;height:100vh;background-color:#f8fafc}.workflow-header{padding:1rem;background-color:#fff;border-bottom:1px solid #e2e8f0}.workflow-header h1{font-size:1.25rem;font-weight:600;color:#334155;margin:0}.loading-overlay{position:absolute;inset:0;background-color:#ffffffb3;display:flex;flex-direction:column;justify-content:center;align-items:center;z-index:1000}.loading-spinner{border:4px solid #f3f3f3;border-top:4px solid #d8b4fe;border-radius:50%;width:40px;height:40px;animation:spin 1s linear infinite}.loading-text{margin-top:1rem;font-size:1rem;color:#7e22ce}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}\n"] }]
4534
4489
  }], ctorParameters: () => [{ type: WorkflowDesignerState }, { type: WorkflowDataService }], propDecorators: { canvasRef: [{
4535
4490
  type: ViewChild,
4536
4491
  args: ['canvasRef']