verben-workflow-ui 0.5.72 → 0.5.74

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.
@@ -365,6 +365,43 @@ class WorkflowDesignerState {
365
365
  const sourceNodeType = sourceNodeInfo.node.type;
366
366
  return this.connectionRules[sourceNodeType] || [];
367
367
  }
368
+ /**
369
+ * Deletes a swimlane. Only succeeds when the lane has no nodes — stages must
370
+ * be moved to another lane first. Returns false when the lane still has nodes
371
+ * or the index is out of range.
372
+ *
373
+ * Keeps everything aligned after removal:
374
+ * - Re-sequences `order` on the remaining lanes (immutable reassignment so
375
+ * Angular's @for blocks re-render).
376
+ * - Decrements the swimlane indices stored on connections whose endpoints
377
+ * live below the removed lane, since those lanes shift up by one.
378
+ *
379
+ * The lane's original record is left in `swimlaneRecord`, so a previously
380
+ * loaded lane is emitted with `ObjectState.Removed` by
381
+ * `transformToWorkflowModel`; a never-saved lane simply disappears.
382
+ */
383
+ deleteSwimlane(index) {
384
+ if (index < 0 || index >= this.swimlanes.length) {
385
+ return false;
386
+ }
387
+ if (this.swimlanes[index].nodes?.length) {
388
+ console.warn('Cannot delete a swimlane that still has stages; move them out first.');
389
+ return false;
390
+ }
391
+ this.swimlanes = this.swimlanes
392
+ .filter((_, i) => i !== index)
393
+ .map((lane, i) => ({ ...lane, order: i }));
394
+ this.connections = this.connections.map((conn) => ({
395
+ ...conn,
396
+ sourceSwimlaneIndex: conn.sourceSwimlaneIndex > index
397
+ ? conn.sourceSwimlaneIndex - 1
398
+ : conn.sourceSwimlaneIndex,
399
+ targetSwimlaneIndex: conn.targetSwimlaneIndex > index
400
+ ? conn.targetSwimlaneIndex - 1
401
+ : conn.targetSwimlaneIndex,
402
+ }));
403
+ return true;
404
+ }
368
405
  updateSwimlane(index, name, tags) {
369
406
  console.log('State: Updating swimlane at index', index, 'with name', name, 'and tags', tags);
370
407
  if (index >= 0 && index < this.swimlanes.length) {
@@ -736,49 +773,71 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
736
773
  class WorkflowDataService {
737
774
  httpService;
738
775
  envSvc;
739
- constructor(httpService, envSvc) {
776
+ utilService;
777
+ pendingRequests = 0;
778
+ constructor(httpService, envSvc, utilService) {
740
779
  this.httpService = httpService;
741
780
  this.envSvc = envSvc;
781
+ this.utilService = utilService;
782
+ }
783
+ /**
784
+ * Wraps an API call so a busy indicator is shown while it is in flight.
785
+ * A ref-count keeps the loader visible until the last concurrent request
786
+ * settles, mirroring how the workflows facade toggles `sendBI`.
787
+ */
788
+ withLoader(request) {
789
+ if (this.pendingRequests++ === 0) {
790
+ this.utilService.sendBI(true);
791
+ }
792
+ return request().finally(() => {
793
+ if (--this.pendingRequests === 0) {
794
+ this.utilService.sendBI(false);
795
+ }
796
+ });
742
797
  }
743
798
  getWorkflowWithParam(param, skip = 0, limit = 1, sortParam = 'CreatedAt', sortOrder = 'Asc') {
744
799
  const url = `GetWorkflowsWithParam/${param}/${skip}/${limit}/${sortParam}/${sortOrder}`;
745
- return this.httpService.get(url);
800
+ return this.withLoader(() => this.httpService.get(url));
746
801
  }
747
802
  getWorkflows(skip = 0, limit = 0) {
748
803
  const url = `GetWorkflows/${skip}/${limit}`;
749
- return this.httpService.get(url, this.envSvc.environment.WorkFlowAPI);
804
+ return this.withLoader(() => this.httpService.get(url, this.envSvc.environment.WorkFlowAPI));
750
805
  }
751
806
  getForms(skip = 0, limit = 0) {
752
807
  const url = `GetForms/${skip}/${limit}`;
753
- return this.httpService.get(url, this.envSvc.environment.WorkFlowAPI);
808
+ return this.withLoader(() => this.httpService.get(url, this.envSvc.environment.WorkFlowAPI));
754
809
  }
755
810
  getTags(skip = 0, limit = 0) {
756
811
  const url = `GetTags/${skip}/${limit}`;
757
- return this.httpService.get(url, this.envSvc.environment.AuthAPI);
812
+ return this.withLoader(() => this.httpService.get(url, this.envSvc.environment.AuthAPI));
758
813
  }
759
814
  saveWorkflows(requests) {
760
815
  const url = `SaveWorkflows`;
761
816
  // console.log('Saving workflows', requests);
762
- return this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI);
817
+ return this.withLoader(() => this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI));
763
818
  }
764
819
  deleteWorkflowStages(requests) {
765
820
  const url = `DeleteWorkflowStage`;
766
821
  // console.log('Saving workflows', requests);
767
- return this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI);
822
+ return this.withLoader(() => this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI));
768
823
  }
769
824
  deleteWorkflowActions(requests) {
770
825
  const url = `DeleteWorkflowAction`;
771
- return this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI);
826
+ return this.withLoader(() => this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI));
827
+ }
828
+ deleteSwimLanes(requests) {
829
+ const url = `DeleteSwimLane`;
830
+ return this.withLoader(() => this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI));
772
831
  }
773
832
  saveEscalations(requests) {
774
833
  const url = `SaveEscalations`;
775
834
  // console.log('Saving workflows', requests);
776
- return this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI);
835
+ return this.withLoader(() => this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI));
777
836
  }
778
837
  deleteEscalations(requests) {
779
838
  const url = `DeleteEscalation`;
780
839
  // console.log('Saving workflows', requests);
781
- return this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI);
840
+ return this.withLoader(() => this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI));
782
841
  }
783
842
  searchEscalationWithParam(params, skip = 0, limit = 1, sortOrder = 'Asc') {
784
843
  const url = `SearchEscalations/${skip}/${limit}`;
@@ -793,27 +852,27 @@ class WorkflowDataService {
793
852
  Sort: sortOrder,
794
853
  // Fields: ['string'],
795
854
  }));
796
- return this.httpService.post(url, payload);
855
+ return this.withLoader(() => this.httpService.post(url, payload));
797
856
  }
798
857
  getEscalationWithParam(param, skip = 0, limit = 1, sortParam = 'CreatedAt', sortOrder = 'Asc') {
799
858
  const url = `GetEscalationsWithParam/${param}/${skip}/${limit}/${sortParam}/${sortOrder}`;
800
- return this.httpService.get(url);
859
+ return this.withLoader(() => this.httpService.get(url));
801
860
  }
802
861
  getReportSchedules(skip = 0, limit = 0) {
803
862
  const url = `GetReportSchedules/${skip}/${limit}`;
804
- return this.httpService.get(url, this.envSvc.environment.ReportingAPI);
863
+ return this.withLoader(() => this.httpService.get(url, this.envSvc.environment.ReportingAPI));
805
864
  }
806
865
  getOperationActions(skip = 0, limit = 0) {
807
866
  const url = `GetOperationAction`;
808
- return this.httpService.get(url);
867
+ return this.withLoader(() => this.httpService.get(url));
809
868
  }
810
869
  saveActorTags(requests) {
811
870
  const url = `SaveActorTags`;
812
- return this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI);
871
+ return this.withLoader(() => this.httpService.post(url, requests, this.envSvc.environment.WorkFlowAPI));
813
872
  }
814
873
  getFlowableStatuses(skip = 0, limit = 0) {
815
874
  const url = `GetFlowableStatus/${skip}/${limit}/CreatedAt/Desc`;
816
- return this.httpService.get(url);
875
+ return this.withLoader(() => this.httpService.get(url));
817
876
  }
818
877
  // Helper to create a mock workflow (just for demo)
819
878
  createMockWorkflow(code) {
@@ -883,7 +942,7 @@ class WorkflowDataService {
883
942
  };
884
943
  return workflow;
885
944
  }
886
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDataService, deps: [{ token: i1.HttpWebRequestService }, { token: i1.EnvironmentService }], target: i0.ɵɵFactoryTarget.Injectable });
945
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDataService, deps: [{ token: i1.HttpWebRequestService }, { token: i1.EnvironmentService }, { token: i1.UtilService }], target: i0.ɵɵFactoryTarget.Injectable });
887
946
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDataService, providedIn: 'root' });
888
947
  }
889
948
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDataService, decorators: [{
@@ -891,7 +950,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImpo
891
950
  args: [{
892
951
  providedIn: 'root',
893
952
  }]
894
- }], ctorParameters: () => [{ type: i1.HttpWebRequestService }, { type: i1.EnvironmentService }] });
953
+ }], ctorParameters: () => [{ type: i1.HttpWebRequestService }, { type: i1.EnvironmentService }, { type: i1.UtilService }] });
895
954
 
896
955
  class NodeManagementService {
897
956
  state;
@@ -2531,7 +2590,14 @@ class DesignerCanvasComponent {
2531
2590
  const nodeInfo = this.state.findNodeById(stageId);
2532
2591
  if (nodeInfo) {
2533
2592
  this.activeStageId = stageId;
2534
- this.activeStageData = { ...nodeInfo.node.stageData, id: stageId };
2593
+ // Seed the entry-point toggle from the node's actual start state, which is
2594
+ // the source of truth for the start circle and API serialization. The
2595
+ // auto-assigned entry node has isStartNode=true without stageData.IsEntryPoint.
2596
+ this.activeStageData = {
2597
+ ...nodeInfo.node.stageData,
2598
+ IsEntryPoint: nodeInfo.node.isStartNode,
2599
+ id: stageId,
2600
+ };
2535
2601
  console.log(this.activeStageData, "ACTIVE STAGE DATA");
2536
2602
  this.showStageDialogLocal = true;
2537
2603
  }
@@ -2599,6 +2665,23 @@ class DesignerCanvasComponent {
2599
2665
  ...stageData,
2600
2666
  };
2601
2667
  console.log('Updated stage data:', nodeInfo.node.stageData);
2668
+ // Sync the entry-point flag onto node.isStartNode, which drives both the
2669
+ // start circle and the IsEntryPoint value serialized to the API.
2670
+ if (stageData.IsEntryPoint) {
2671
+ // Enforce a single entry point: this node becomes the sole start node.
2672
+ this.state.swimlanes.forEach((swimlane) => {
2673
+ swimlane.nodes?.forEach((node) => {
2674
+ node.isStartNode = node.id === this.activeStageId;
2675
+ });
2676
+ });
2677
+ this.state.startNodeId = this.activeStageId;
2678
+ }
2679
+ else {
2680
+ nodeInfo.node.isStartNode = false;
2681
+ if (this.state.startNodeId === this.activeStageId) {
2682
+ this.state.startNodeId = null;
2683
+ }
2684
+ }
2602
2685
  // Emit event for parent component to know about the update
2603
2686
  this.stagePropertiesUpdated.emit({
2604
2687
  nodeId: this.activeStageId,
@@ -3622,9 +3705,11 @@ class SwimlaneDialogComponent {
3622
3705
  dataService;
3623
3706
  swimlaneService;
3624
3707
  visible = false;
3708
+ isEditing = false;
3625
3709
  swimlaneData = input(null);
3626
3710
  closed = new EventEmitter();
3627
3711
  created = new EventEmitter();
3712
+ deleted = new EventEmitter();
3628
3713
  searchQuery = '';
3629
3714
  workflowName = '';
3630
3715
  // Track selected tags in a separate array
@@ -3692,6 +3777,9 @@ class SwimlaneDialogComponent {
3692
3777
  this.selectedTagNames.push(tagName);
3693
3778
  }
3694
3779
  }
3780
+ onDeleteSwimlane() {
3781
+ this.deleted.emit();
3782
+ }
3695
3783
  onDialogClose(eventData) {
3696
3784
  console.log('Dialog closed, received data:', eventData);
3697
3785
  this.searchQuery = '';
@@ -3719,17 +3807,21 @@ class SwimlaneDialogComponent {
3719
3807
  this.searchQuery = '';
3720
3808
  }
3721
3809
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SwimlaneDialogComponent, deps: [{ token: i1$1.FormBuilder }, { token: WorkflowDataService }, { token: SwimlaneService }], target: i0.ɵɵFactoryTarget.Component });
3722
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: SwimlaneDialogComponent, selector: "lib-swimlane-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: false, isRequired: false, transformFunction: null }, swimlaneData: { classPropertyName: "swimlaneData", publicName: "swimlaneData", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", created: "created" }, ngImport: i0, template: "<verben-dialogue\n [showCloseIcon]=\"true\"\n [dismissOutsideClick]=\"true\"\n [closeOnEscape]=\"true\"\n [size]=\"'medium'\"\n [mode]=\"'drawer'\"\n [disableFooter]=\"false\"\n [isVisible]=\"visible\"\n [headerTemplate]=\"headerTemplate\"\n [bodyTemplate]=\"bodyTemplate\"\n [footerTemplate]=\"footerTemplate\"\n (openModal)=\"onDialogOpen($event)\"\n (closeModal)=\"onDialogClose($event)\"\n>\n</verben-dialogue>\n\n<ng-template #headerTemplate>\n <div\n class=\"flex items-center justify-between p-3 text-xs border-b-4 border-[#FFE681]\"\n >\n <button class=\"mr-4\" type=\"button\" (click)=\"onDialogClose($event)\">\n <span\n class=\"block w-2.5 h-2.5 border-t border-l border-[#3E3E3E]/70 transform -rotate-45\"\n ></span>\n </button>\n <div class=\"text-center\">\n <h2 class=\"text-sm font-medium\">Select Tags</h2>\n <span class=\"text-xs text-[#3E3E3E]/70\"\n >{{ this.selectedTags.length }}/{{ tags.length }}</span\n >\n </div>\n <span\n class=\"font-medium cursor-pointer text-[#3E3E3E]/70\"\n (click)=\"applySelection()\"\n >Create</span\n >\n </div>\n</ng-template>\n\n<ng-template #bodyTemplate>\n <div class=\"p-3\">\n <div class=\"mb-4 grid gap-4\">\n <!-- <input\n type=\"text\"\n placeholder=\"Enter Name\"\n class=\"w-full px-4 py-2 rounded-lg border border-[#E7C10B] outline-none\"\n [(ngModel)]=\"workflowName\"\n />\n\n <input\n type=\"search\"\n placeholder=\"Filter Tags\"\n class=\"w-full px-4 py-2 rounded-lg border border-[#E7C10B] outline-none\"\n [(ngModel)]=\"searchQuery\"\n />\n </div>\n\n <div class=\"\">\n <div class=\"py-3\">\n <label class=\"flex items-center\">\n <input type=\"checkbox\" class=\"mr-3\" [(ngModel)]=\"allSelected\" />\n <span class=\"text-sm\">Select All</span>\n </label>\n </div>\n\n <div *ngFor=\"let tag of filteredTags\" class=\"py-3\">\n <label class=\"flex items-center\">\n <input\n type=\"checkbox\"\n class=\"mr-3\"\n [checked]=\"isTagSelected(tag.Name)\"\n (change)=\"toggleTagSelection(tag.Name)\"\n />\n <span class=\"text-sm\">{{ tag.Name }}</span>\n <span *ngIf=\"!tag.IsOptional\" class=\"ml-2 text-xs text-red-500\"\n >(Required)</span\n >\n </label>\n </div> -->\n\n <verbena-input\n [(ngModel)]=\"workflowName\"\n [border]=\"'1px solid #9A9FBF66'\"\n placeHolder=\"Enter Swimlane Label\"\n [bgColor]=\"'white'\"\n ></verbena-input>\n\n <verben-drop-down\n selectKey=\"Code\"\n label=\"Tags\"\n styleClass=\"w-full\"\n width=\"100%\"\n [multiselect]=\"true\"\n placeholder=\"Select\"\n [options]=\"tags\"\n optionLabel=\"Name\"\n id=\"tags\"\n [(ngModel)]=\"selectedTags\"\n class=\"form-control\"\n [filter]=\"true\"\n [filterBy]=\"'Name'\"\n [showClear]=\"true\"\n >\n </verben-drop-down>\n </div>\n </div>\n</ng-template>\n\n<ng-template #footerTemplate>\n <div\n class=\"flex justify-between w-full items-center p-4 border-t border-gray-200\"\n >\n <!-- <button\n class=\"px-4 py-2 mr-2 bg-white border border-gray-200 rounded text-sm font-medium\"\n (click)=\"onDialogClose($event)\"\n >\n Cancel\n </button> -->\n\n <verbena-button\n (click)=\"onDialogClose($event)\"\n text=\"Cancel\"\n styleType=\"ylw-outline\"\n ></verbena-button>\n\n <verbena-button\n type=\"submit\"\n text=\"Save\"\n bgColor=\"#FFE681\"\n textColor=\"#404040\"\n borderRadius=\"10px\"\n pd=\"10px 20px\"\n width=\"114px\"\n height=\"39px\"\n [disable]=\"selectedTags.length < 1\"\n (click)=\"applySelection()\"\n ></verbena-button>\n\n <!-- <verbena-button\n width=\"114px\"\n height=\"39px\"\n text=\"Delete\"\n bgColor=\"#999999\"\n textColor=\"#404040\"\n borderRadius=\"10px\"\n ></verbena-button> -->\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "component", type: i5.VerbenDialogueComponent, selector: "verben-dialogue", inputs: ["dialogueWidth", "headerTemplate", "bodyTemplate", "footerTemplate", "showCloseIcon", "dismissOutsideClick", "closeOnEscape", "isVisible", "size", "backdropColor", "customClass", "disableFooter", "margin", "padding", "borderRadius", "dialogueBgColor", "width", "closeIconClass", "boxShadow", "enableTransition", "modalData", "mode", "position", "drawerWidth"], outputs: ["openModal", "closeModal"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i5.VerbenaButtonComponent, selector: "verbena-button", inputs: ["text", "icon", "useIcon", "svgPosition", "iconPosition", "bgColor", "textColor", "border", "borderRadius", "pd", "width", "height", "fontSize", "fontWeight", "disable", "svgSize", "weight", "variant", "styleType", "svg", "svgWidth", "svgHeight", "iconColor", "svgColor", "buttonClass", "buttonTextClass", "isLoading", "spinnerSize", "spinnerColor"] }, { kind: "component", type: i5.DropDownComponent, selector: "verben-drop-down", inputs: ["options", "width", "showHorizontalLine", "horizontalLineColor", "optionLabel", "optionSubLabel", "optionValue", "placeholder", "invalidMessage", "errorPosition", "loadMoreCaption", "display", "showClear", "lazyLoad", "selectKey", "styleClass", "group", "multiselect", "filter", "avoidDuplication", "filterBy", "debounceTime", "minChar", "disabled", "required", "load", "asyncLabel", "search"], outputs: ["optionsChange", "onChange", "onClick", "onClear"] }, { kind: "component", type: i5.VerbenaInputComponent, selector: "verbena-input", inputs: ["label", "placeHolder", "required", "svgPosition", "minLength", "maxLength", "type", "bgColor", "border", "borderRadius", "textColor", "value", "labelPosition", "labelColor", "disable", "readOnly", "min", "max", "showBorder", "showErrorMessage", "errorMessageColor", "errorBorderColor", "errorPosition", "svg", "fontSize", "svgWidth", "svgHeight", "svgColor", "capitalization", "inputContainerClass", "inputFieldClass", "passLength", "inputWrapperClass", "passwordToggle", "customErrorMessages", "icon", "textPass"], outputs: ["valueChange"] }] });
3810
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "18.2.13", type: SwimlaneDialogComponent, selector: "lib-swimlane-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: false, isRequired: false, transformFunction: null }, isEditing: { classPropertyName: "isEditing", publicName: "isEditing", isSignal: false, isRequired: false, transformFunction: null }, swimlaneData: { classPropertyName: "swimlaneData", publicName: "swimlaneData", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { closed: "closed", created: "created", deleted: "deleted" }, ngImport: i0, template: "<verben-dialogue\n [showCloseIcon]=\"true\"\n [dismissOutsideClick]=\"true\"\n [closeOnEscape]=\"true\"\n [size]=\"'medium'\"\n [mode]=\"'drawer'\"\n [disableFooter]=\"false\"\n [isVisible]=\"visible\"\n [headerTemplate]=\"headerTemplate\"\n [bodyTemplate]=\"bodyTemplate\"\n [footerTemplate]=\"footerTemplate\"\n (openModal)=\"onDialogOpen($event)\"\n (closeModal)=\"onDialogClose($event)\"\n>\n</verben-dialogue>\n\n<ng-template #headerTemplate>\n <div\n class=\"flex items-center justify-between p-3 text-xs border-b-4 border-[#FFE681]\"\n >\n <button class=\"mr-4\" type=\"button\" (click)=\"onDialogClose($event)\">\n <span\n class=\"block w-2.5 h-2.5 border-t border-l border-[#3E3E3E]/70 transform -rotate-45\"\n ></span>\n </button>\n <div class=\"text-center\">\n <h2 class=\"text-sm font-medium\">Select Tags</h2>\n <span class=\"text-xs text-[#3E3E3E]/70\"\n >{{ this.selectedTags.length }}/{{ tags.length }}</span\n >\n </div>\n <span\n class=\"font-medium cursor-pointer text-[#3E3E3E]/70\"\n (click)=\"applySelection()\"\n >Create</span\n >\n </div>\n</ng-template>\n\n<ng-template #bodyTemplate>\n <div class=\"p-3\">\n <div class=\"mb-4 grid gap-4\">\n <!-- <input\n type=\"text\"\n placeholder=\"Enter Name\"\n class=\"w-full px-4 py-2 rounded-lg border border-[#E7C10B] outline-none\"\n [(ngModel)]=\"workflowName\"\n />\n\n <input\n type=\"search\"\n placeholder=\"Filter Tags\"\n class=\"w-full px-4 py-2 rounded-lg border border-[#E7C10B] outline-none\"\n [(ngModel)]=\"searchQuery\"\n />\n </div>\n\n <div class=\"\">\n <div class=\"py-3\">\n <label class=\"flex items-center\">\n <input type=\"checkbox\" class=\"mr-3\" [(ngModel)]=\"allSelected\" />\n <span class=\"text-sm\">Select All</span>\n </label>\n </div>\n\n <div *ngFor=\"let tag of filteredTags\" class=\"py-3\">\n <label class=\"flex items-center\">\n <input\n type=\"checkbox\"\n class=\"mr-3\"\n [checked]=\"isTagSelected(tag.Name)\"\n (change)=\"toggleTagSelection(tag.Name)\"\n />\n <span class=\"text-sm\">{{ tag.Name }}</span>\n <span *ngIf=\"!tag.IsOptional\" class=\"ml-2 text-xs text-red-500\"\n >(Required)</span\n >\n </label>\n </div> -->\n\n <verbena-input\n [(ngModel)]=\"workflowName\"\n [border]=\"'1px solid #9A9FBF66'\"\n placeHolder=\"Enter Swimlane Label\"\n [bgColor]=\"'white'\"\n ></verbena-input>\n\n <verben-drop-down\n selectKey=\"Code\"\n label=\"Tags\"\n styleClass=\"w-full\"\n width=\"100%\"\n [multiselect]=\"true\"\n placeholder=\"Select\"\n [options]=\"tags\"\n optionLabel=\"Name\"\n id=\"tags\"\n [(ngModel)]=\"selectedTags\"\n class=\"form-control\"\n [filter]=\"true\"\n [filterBy]=\"'Name'\"\n [showClear]=\"true\"\n >\n </verben-drop-down>\n </div>\n </div>\n</ng-template>\n\n<ng-template #footerTemplate>\n <div\n class=\"flex justify-between w-full items-center p-4 border-t border-gray-200\"\n >\n <!-- <button\n class=\"px-4 py-2 mr-2 bg-white border border-gray-200 rounded text-sm font-medium\"\n (click)=\"onDialogClose($event)\"\n >\n Cancel\n </button> -->\n\n <verbena-button\n (click)=\"onDialogClose($event)\"\n text=\"Cancel\"\n styleType=\"ylw-outline\"\n ></verbena-button>\n\n <verbena-button\n type=\"submit\"\n text=\"Save\"\n bgColor=\"#FFE681\"\n textColor=\"#404040\"\n borderRadius=\"10px\"\n pd=\"10px 20px\"\n width=\"114px\"\n height=\"39px\"\n [disable]=\"selectedTags.length < 1\"\n (click)=\"applySelection()\"\n ></verbena-button>\n\n <verbena-button\n *ngIf=\"isEditing\"\n width=\"114px\"\n height=\"39px\"\n text=\"Delete\"\n bgColor=\"#999999\"\n textColor=\"#404040\"\n borderRadius=\"10px\"\n pd=\"10px 20px\"\n (click)=\"onDeleteSwimlane()\"\n ></verbena-button>\n </div>\n</ng-template>\n", styles: [""], dependencies: [{ kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5.VerbenDialogueComponent, selector: "verben-dialogue", inputs: ["dialogueWidth", "headerTemplate", "bodyTemplate", "footerTemplate", "showCloseIcon", "dismissOutsideClick", "closeOnEscape", "isVisible", "size", "backdropColor", "customClass", "disableFooter", "margin", "padding", "borderRadius", "dialogueBgColor", "width", "closeIconClass", "boxShadow", "enableTransition", "modalData", "mode", "position", "drawerWidth"], outputs: ["openModal", "closeModal"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i5.VerbenaButtonComponent, selector: "verbena-button", inputs: ["text", "icon", "useIcon", "svgPosition", "iconPosition", "bgColor", "textColor", "border", "borderRadius", "pd", "width", "height", "fontSize", "fontWeight", "disable", "svgSize", "weight", "variant", "styleType", "svg", "svgWidth", "svgHeight", "iconColor", "svgColor", "buttonClass", "buttonTextClass", "isLoading", "spinnerSize", "spinnerColor"] }, { kind: "component", type: i5.DropDownComponent, selector: "verben-drop-down", inputs: ["options", "width", "showHorizontalLine", "horizontalLineColor", "optionLabel", "optionSubLabel", "optionValue", "placeholder", "invalidMessage", "errorPosition", "loadMoreCaption", "display", "showClear", "lazyLoad", "selectKey", "styleClass", "group", "multiselect", "filter", "avoidDuplication", "filterBy", "debounceTime", "minChar", "disabled", "required", "load", "asyncLabel", "search"], outputs: ["optionsChange", "onChange", "onClick", "onClear"] }, { kind: "component", type: i5.VerbenaInputComponent, selector: "verbena-input", inputs: ["label", "placeHolder", "required", "svgPosition", "minLength", "maxLength", "type", "bgColor", "border", "borderRadius", "textColor", "value", "labelPosition", "labelColor", "disable", "readOnly", "min", "max", "showBorder", "showErrorMessage", "errorMessageColor", "errorBorderColor", "errorPosition", "svg", "fontSize", "svgWidth", "svgHeight", "svgColor", "capitalization", "inputContainerClass", "inputFieldClass", "passLength", "inputWrapperClass", "passwordToggle", "customErrorMessages", "icon", "textPass"], outputs: ["valueChange"] }] });
3723
3811
  }
3724
3812
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: SwimlaneDialogComponent, decorators: [{
3725
3813
  type: Component,
3726
- args: [{ selector: 'lib-swimlane-dialog', template: "<verben-dialogue\n [showCloseIcon]=\"true\"\n [dismissOutsideClick]=\"true\"\n [closeOnEscape]=\"true\"\n [size]=\"'medium'\"\n [mode]=\"'drawer'\"\n [disableFooter]=\"false\"\n [isVisible]=\"visible\"\n [headerTemplate]=\"headerTemplate\"\n [bodyTemplate]=\"bodyTemplate\"\n [footerTemplate]=\"footerTemplate\"\n (openModal)=\"onDialogOpen($event)\"\n (closeModal)=\"onDialogClose($event)\"\n>\n</verben-dialogue>\n\n<ng-template #headerTemplate>\n <div\n class=\"flex items-center justify-between p-3 text-xs border-b-4 border-[#FFE681]\"\n >\n <button class=\"mr-4\" type=\"button\" (click)=\"onDialogClose($event)\">\n <span\n class=\"block w-2.5 h-2.5 border-t border-l border-[#3E3E3E]/70 transform -rotate-45\"\n ></span>\n </button>\n <div class=\"text-center\">\n <h2 class=\"text-sm font-medium\">Select Tags</h2>\n <span class=\"text-xs text-[#3E3E3E]/70\"\n >{{ this.selectedTags.length }}/{{ tags.length }}</span\n >\n </div>\n <span\n class=\"font-medium cursor-pointer text-[#3E3E3E]/70\"\n (click)=\"applySelection()\"\n >Create</span\n >\n </div>\n</ng-template>\n\n<ng-template #bodyTemplate>\n <div class=\"p-3\">\n <div class=\"mb-4 grid gap-4\">\n <!-- <input\n type=\"text\"\n placeholder=\"Enter Name\"\n class=\"w-full px-4 py-2 rounded-lg border border-[#E7C10B] outline-none\"\n [(ngModel)]=\"workflowName\"\n />\n\n <input\n type=\"search\"\n placeholder=\"Filter Tags\"\n class=\"w-full px-4 py-2 rounded-lg border border-[#E7C10B] outline-none\"\n [(ngModel)]=\"searchQuery\"\n />\n </div>\n\n <div class=\"\">\n <div class=\"py-3\">\n <label class=\"flex items-center\">\n <input type=\"checkbox\" class=\"mr-3\" [(ngModel)]=\"allSelected\" />\n <span class=\"text-sm\">Select All</span>\n </label>\n </div>\n\n <div *ngFor=\"let tag of filteredTags\" class=\"py-3\">\n <label class=\"flex items-center\">\n <input\n type=\"checkbox\"\n class=\"mr-3\"\n [checked]=\"isTagSelected(tag.Name)\"\n (change)=\"toggleTagSelection(tag.Name)\"\n />\n <span class=\"text-sm\">{{ tag.Name }}</span>\n <span *ngIf=\"!tag.IsOptional\" class=\"ml-2 text-xs text-red-500\"\n >(Required)</span\n >\n </label>\n </div> -->\n\n <verbena-input\n [(ngModel)]=\"workflowName\"\n [border]=\"'1px solid #9A9FBF66'\"\n placeHolder=\"Enter Swimlane Label\"\n [bgColor]=\"'white'\"\n ></verbena-input>\n\n <verben-drop-down\n selectKey=\"Code\"\n label=\"Tags\"\n styleClass=\"w-full\"\n width=\"100%\"\n [multiselect]=\"true\"\n placeholder=\"Select\"\n [options]=\"tags\"\n optionLabel=\"Name\"\n id=\"tags\"\n [(ngModel)]=\"selectedTags\"\n class=\"form-control\"\n [filter]=\"true\"\n [filterBy]=\"'Name'\"\n [showClear]=\"true\"\n >\n </verben-drop-down>\n </div>\n </div>\n</ng-template>\n\n<ng-template #footerTemplate>\n <div\n class=\"flex justify-between w-full items-center p-4 border-t border-gray-200\"\n >\n <!-- <button\n class=\"px-4 py-2 mr-2 bg-white border border-gray-200 rounded text-sm font-medium\"\n (click)=\"onDialogClose($event)\"\n >\n Cancel\n </button> -->\n\n <verbena-button\n (click)=\"onDialogClose($event)\"\n text=\"Cancel\"\n styleType=\"ylw-outline\"\n ></verbena-button>\n\n <verbena-button\n type=\"submit\"\n text=\"Save\"\n bgColor=\"#FFE681\"\n textColor=\"#404040\"\n borderRadius=\"10px\"\n pd=\"10px 20px\"\n width=\"114px\"\n height=\"39px\"\n [disable]=\"selectedTags.length < 1\"\n (click)=\"applySelection()\"\n ></verbena-button>\n\n <!-- <verbena-button\n width=\"114px\"\n height=\"39px\"\n text=\"Delete\"\n bgColor=\"#999999\"\n textColor=\"#404040\"\n borderRadius=\"10px\"\n ></verbena-button> -->\n </div>\n</ng-template>\n" }]
3814
+ args: [{ selector: 'lib-swimlane-dialog', template: "<verben-dialogue\n [showCloseIcon]=\"true\"\n [dismissOutsideClick]=\"true\"\n [closeOnEscape]=\"true\"\n [size]=\"'medium'\"\n [mode]=\"'drawer'\"\n [disableFooter]=\"false\"\n [isVisible]=\"visible\"\n [headerTemplate]=\"headerTemplate\"\n [bodyTemplate]=\"bodyTemplate\"\n [footerTemplate]=\"footerTemplate\"\n (openModal)=\"onDialogOpen($event)\"\n (closeModal)=\"onDialogClose($event)\"\n>\n</verben-dialogue>\n\n<ng-template #headerTemplate>\n <div\n class=\"flex items-center justify-between p-3 text-xs border-b-4 border-[#FFE681]\"\n >\n <button class=\"mr-4\" type=\"button\" (click)=\"onDialogClose($event)\">\n <span\n class=\"block w-2.5 h-2.5 border-t border-l border-[#3E3E3E]/70 transform -rotate-45\"\n ></span>\n </button>\n <div class=\"text-center\">\n <h2 class=\"text-sm font-medium\">Select Tags</h2>\n <span class=\"text-xs text-[#3E3E3E]/70\"\n >{{ this.selectedTags.length }}/{{ tags.length }}</span\n >\n </div>\n <span\n class=\"font-medium cursor-pointer text-[#3E3E3E]/70\"\n (click)=\"applySelection()\"\n >Create</span\n >\n </div>\n</ng-template>\n\n<ng-template #bodyTemplate>\n <div class=\"p-3\">\n <div class=\"mb-4 grid gap-4\">\n <!-- <input\n type=\"text\"\n placeholder=\"Enter Name\"\n class=\"w-full px-4 py-2 rounded-lg border border-[#E7C10B] outline-none\"\n [(ngModel)]=\"workflowName\"\n />\n\n <input\n type=\"search\"\n placeholder=\"Filter Tags\"\n class=\"w-full px-4 py-2 rounded-lg border border-[#E7C10B] outline-none\"\n [(ngModel)]=\"searchQuery\"\n />\n </div>\n\n <div class=\"\">\n <div class=\"py-3\">\n <label class=\"flex items-center\">\n <input type=\"checkbox\" class=\"mr-3\" [(ngModel)]=\"allSelected\" />\n <span class=\"text-sm\">Select All</span>\n </label>\n </div>\n\n <div *ngFor=\"let tag of filteredTags\" class=\"py-3\">\n <label class=\"flex items-center\">\n <input\n type=\"checkbox\"\n class=\"mr-3\"\n [checked]=\"isTagSelected(tag.Name)\"\n (change)=\"toggleTagSelection(tag.Name)\"\n />\n <span class=\"text-sm\">{{ tag.Name }}</span>\n <span *ngIf=\"!tag.IsOptional\" class=\"ml-2 text-xs text-red-500\"\n >(Required)</span\n >\n </label>\n </div> -->\n\n <verbena-input\n [(ngModel)]=\"workflowName\"\n [border]=\"'1px solid #9A9FBF66'\"\n placeHolder=\"Enter Swimlane Label\"\n [bgColor]=\"'white'\"\n ></verbena-input>\n\n <verben-drop-down\n selectKey=\"Code\"\n label=\"Tags\"\n styleClass=\"w-full\"\n width=\"100%\"\n [multiselect]=\"true\"\n placeholder=\"Select\"\n [options]=\"tags\"\n optionLabel=\"Name\"\n id=\"tags\"\n [(ngModel)]=\"selectedTags\"\n class=\"form-control\"\n [filter]=\"true\"\n [filterBy]=\"'Name'\"\n [showClear]=\"true\"\n >\n </verben-drop-down>\n </div>\n </div>\n</ng-template>\n\n<ng-template #footerTemplate>\n <div\n class=\"flex justify-between w-full items-center p-4 border-t border-gray-200\"\n >\n <!-- <button\n class=\"px-4 py-2 mr-2 bg-white border border-gray-200 rounded text-sm font-medium\"\n (click)=\"onDialogClose($event)\"\n >\n Cancel\n </button> -->\n\n <verbena-button\n (click)=\"onDialogClose($event)\"\n text=\"Cancel\"\n styleType=\"ylw-outline\"\n ></verbena-button>\n\n <verbena-button\n type=\"submit\"\n text=\"Save\"\n bgColor=\"#FFE681\"\n textColor=\"#404040\"\n borderRadius=\"10px\"\n pd=\"10px 20px\"\n width=\"114px\"\n height=\"39px\"\n [disable]=\"selectedTags.length < 1\"\n (click)=\"applySelection()\"\n ></verbena-button>\n\n <verbena-button\n *ngIf=\"isEditing\"\n width=\"114px\"\n height=\"39px\"\n text=\"Delete\"\n bgColor=\"#999999\"\n textColor=\"#404040\"\n borderRadius=\"10px\"\n pd=\"10px 20px\"\n (click)=\"onDeleteSwimlane()\"\n ></verbena-button>\n </div>\n</ng-template>\n" }]
3727
3815
  }], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: WorkflowDataService }, { type: SwimlaneService }], propDecorators: { visible: [{
3728
3816
  type: Input
3817
+ }], isEditing: [{
3818
+ type: Input
3729
3819
  }], closed: [{
3730
3820
  type: Output
3731
3821
  }], created: [{
3732
3822
  type: Output
3823
+ }], deleted: [{
3824
+ type: Output
3733
3825
  }] } });
3734
3826
 
3735
3827
  class ActorTagsDialogComponent {
@@ -4131,6 +4223,48 @@ class WorkflowDesignerComponent {
4131
4223
  this.showSwimlaneDialog = false;
4132
4224
  this.editingSwimlaneIndex = null;
4133
4225
  }
4226
+ onSwimlaneDialogDeleted() {
4227
+ if (this.editingSwimlaneIndex === null) {
4228
+ return;
4229
+ }
4230
+ const index = this.editingSwimlaneIndex;
4231
+ const swimlane = this.state.swimlanes[index];
4232
+ // Guard: stages must be moved out first (also enforced in state.deleteSwimlane).
4233
+ if (swimlane?.nodes?.length) {
4234
+ this.utilService.showError('Move all stages out of this swimlane before deleting it.');
4235
+ return;
4236
+ }
4237
+ // A loaded lane keeps its backend Code in swimlaneRecord (keyed by id/Code).
4238
+ const backendRecord = swimlane?.id
4239
+ ? this.state.swimlaneRecord[swimlane.id]
4240
+ : undefined;
4241
+ if (backendRecord?.Code) {
4242
+ // Path A: backend-persisted lane — delete on the server first.
4243
+ this.dataService
4244
+ .deleteSwimLanes([backendRecord.Code])
4245
+ .then(() => {
4246
+ this.finishSwimlaneDelete(index);
4247
+ })
4248
+ .catch((error) => {
4249
+ console.error('Error deleting swimlane:', error);
4250
+ this.utilService.showError('Failed to delete swimlane');
4251
+ });
4252
+ }
4253
+ else {
4254
+ // Path B: never-saved lane — remove from state directly, no API call.
4255
+ this.finishSwimlaneDelete(index);
4256
+ }
4257
+ }
4258
+ finishSwimlaneDelete(index) {
4259
+ const deleted = this.state.deleteSwimlane(index);
4260
+ if (!deleted) {
4261
+ this.utilService.showError('Move all stages out of this swimlane before deleting it.');
4262
+ return;
4263
+ }
4264
+ this.showSwimlaneDialog = false;
4265
+ this.editingSwimlaneIndex = null;
4266
+ this.canvasRef?.updateCanvasSize();
4267
+ }
4134
4268
  onSubflowSelected() {
4135
4269
  // Reset the selected tool after placing a subflow node
4136
4270
  this.selectedTool = null;
@@ -4139,11 +4273,11 @@ class WorkflowDesignerComponent {
4139
4273
  this.showActorTagsDialog = true;
4140
4274
  }
4141
4275
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDesignerComponent, deps: [{ token: WorkflowDesignerState }, { token: WorkflowDataService }, { token: i1.UtilService }], target: i0.ɵɵFactoryTarget.Component });
4142
- 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>{{ state.workflow?.Name }}</h1>\n </div>\n\n <!-- Toolbar Component -->\n <lib-designer-toolbar [selectedTool]=\"selectedTool\" [isSaving]=\"isSaving\"\n [workflowCode]=\"state.workflow?.Code || null\" (toolSelected)=\"onToolSelected($event)\"\n (saveWorkflow)=\"saveWorkflow()\" (openActorTags)=\"openActorTagsDialog()\">\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 [selectedTool]=\"selectedTool\" (clickedPosition)=\"handleCanvasPositionClick($event)\"\n (subflowSelected)=\"onSubflowSelected()\" #canvasRef>\n </lib-designer-canvas>\n\n <lib-swimlane-dialog [visible]=\"showSwimlaneDialog\" [swimlaneData]=\"editingSwimlane()\"\n (created)=\"onSwimlaneDialogFilled($event)\" (closed)=\"showSwimlaneDialog = false\"></lib-swimlane-dialog>\n\n <lib-actor-tags-dialog [visible]=\"showActorTagsDialog\" [workflowCode]=\"state.workflow?.Code || null\"\n (closed)=\"showActorTagsDialog = false\"></lib-actor-tags-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;text-align:center}.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: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DesignerToolbarComponent, selector: "lib-designer-toolbar", inputs: ["selectedTool", "isSaving", "workflowCode"], outputs: ["openActorTags", "toolSelected", "saveWorkflow"] }, { kind: "component", type: DesignerCanvasComponent, selector: "lib-designer-canvas", inputs: ["selectedTool"], outputs: ["clickedPosition", "subflowSelected", "stagePropertiesUpdated", "actionPropertiesUpdated"] }, { kind: "component", type: SwimlaneDialogComponent, selector: "lib-swimlane-dialog", inputs: ["visible", "swimlaneData"], outputs: ["closed", "created"] }, { kind: "component", type: ActorTagsDialogComponent, selector: "lib-actor-tags-dialog", inputs: ["visible", "workflowCode"], outputs: ["closed"] }] });
4276
+ 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>{{ state.workflow?.Name }}</h1>\n </div>\n\n <!-- Toolbar Component -->\n <lib-designer-toolbar [selectedTool]=\"selectedTool\" [isSaving]=\"isSaving\"\n [workflowCode]=\"state.workflow?.Code || null\" (toolSelected)=\"onToolSelected($event)\"\n (saveWorkflow)=\"saveWorkflow()\" (openActorTags)=\"openActorTagsDialog()\">\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 [selectedTool]=\"selectedTool\" (clickedPosition)=\"handleCanvasPositionClick($event)\"\n (subflowSelected)=\"onSubflowSelected()\" #canvasRef>\n </lib-designer-canvas>\n\n <lib-swimlane-dialog [visible]=\"showSwimlaneDialog\" [swimlaneData]=\"editingSwimlane()\"\n [isEditing]=\"editingSwimlaneIndex !== null\" (created)=\"onSwimlaneDialogFilled($event)\"\n (deleted)=\"onSwimlaneDialogDeleted()\" (closed)=\"showSwimlaneDialog = false\"></lib-swimlane-dialog>\n\n <lib-actor-tags-dialog [visible]=\"showActorTagsDialog\" [workflowCode]=\"state.workflow?.Code || null\"\n (closed)=\"showActorTagsDialog = false\"></lib-actor-tags-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;text-align:center}.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: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DesignerToolbarComponent, selector: "lib-designer-toolbar", inputs: ["selectedTool", "isSaving", "workflowCode"], outputs: ["openActorTags", "toolSelected", "saveWorkflow"] }, { kind: "component", type: DesignerCanvasComponent, selector: "lib-designer-canvas", inputs: ["selectedTool"], outputs: ["clickedPosition", "subflowSelected", "stagePropertiesUpdated", "actionPropertiesUpdated"] }, { kind: "component", type: SwimlaneDialogComponent, selector: "lib-swimlane-dialog", inputs: ["visible", "isEditing", "swimlaneData"], outputs: ["closed", "created", "deleted"] }, { kind: "component", type: ActorTagsDialogComponent, selector: "lib-actor-tags-dialog", inputs: ["visible", "workflowCode"], outputs: ["closed"] }] });
4143
4277
  }
4144
4278
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: WorkflowDesignerComponent, decorators: [{
4145
4279
  type: Component,
4146
- args: [{ selector: 'lib-workflow-designer', template: "<div class=\"workflow-designer\">\n <div class=\"workflow-header\">\n <h1>{{ state.workflow?.Name }}</h1>\n </div>\n\n <!-- Toolbar Component -->\n <lib-designer-toolbar [selectedTool]=\"selectedTool\" [isSaving]=\"isSaving\"\n [workflowCode]=\"state.workflow?.Code || null\" (toolSelected)=\"onToolSelected($event)\"\n (saveWorkflow)=\"saveWorkflow()\" (openActorTags)=\"openActorTagsDialog()\">\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 [selectedTool]=\"selectedTool\" (clickedPosition)=\"handleCanvasPositionClick($event)\"\n (subflowSelected)=\"onSubflowSelected()\" #canvasRef>\n </lib-designer-canvas>\n\n <lib-swimlane-dialog [visible]=\"showSwimlaneDialog\" [swimlaneData]=\"editingSwimlane()\"\n (created)=\"onSwimlaneDialogFilled($event)\" (closed)=\"showSwimlaneDialog = false\"></lib-swimlane-dialog>\n\n <lib-actor-tags-dialog [visible]=\"showActorTagsDialog\" [workflowCode]=\"state.workflow?.Code || null\"\n (closed)=\"showActorTagsDialog = false\"></lib-actor-tags-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;text-align:center}.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"] }]
4280
+ args: [{ selector: 'lib-workflow-designer', template: "<div class=\"workflow-designer\">\n <div class=\"workflow-header\">\n <h1>{{ state.workflow?.Name }}</h1>\n </div>\n\n <!-- Toolbar Component -->\n <lib-designer-toolbar [selectedTool]=\"selectedTool\" [isSaving]=\"isSaving\"\n [workflowCode]=\"state.workflow?.Code || null\" (toolSelected)=\"onToolSelected($event)\"\n (saveWorkflow)=\"saveWorkflow()\" (openActorTags)=\"openActorTagsDialog()\">\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 [selectedTool]=\"selectedTool\" (clickedPosition)=\"handleCanvasPositionClick($event)\"\n (subflowSelected)=\"onSubflowSelected()\" #canvasRef>\n </lib-designer-canvas>\n\n <lib-swimlane-dialog [visible]=\"showSwimlaneDialog\" [swimlaneData]=\"editingSwimlane()\"\n [isEditing]=\"editingSwimlaneIndex !== null\" (created)=\"onSwimlaneDialogFilled($event)\"\n (deleted)=\"onSwimlaneDialogDeleted()\" (closed)=\"showSwimlaneDialog = false\"></lib-swimlane-dialog>\n\n <lib-actor-tags-dialog [visible]=\"showActorTagsDialog\" [workflowCode]=\"state.workflow?.Code || null\"\n (closed)=\"showActorTagsDialog = false\"></lib-actor-tags-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;text-align:center}.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"] }]
4147
4281
  }], ctorParameters: () => [{ type: WorkflowDesignerState }, { type: WorkflowDataService }, { type: i1.UtilService }], propDecorators: { canvasRef: [{
4148
4282
  type: ViewChild,
4149
4283
  args: ['canvasRef']